mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Ruby: diagnostics: add support for markdown messages
This commit is contained in:
@@ -88,7 +88,7 @@ pub struct LogWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LogWriter {
|
impl LogWriter {
|
||||||
pub fn message(&self, id: &str, name: &str) -> DiagnosticMessage {
|
pub fn new_entry(&self, id: &str, name: &str) -> DiagnosticMessage {
|
||||||
DiagnosticMessage {
|
DiagnosticMessage {
|
||||||
timestamp: chrono::Utc::now(),
|
timestamp: chrono::Utc::now(),
|
||||||
source: Source {
|
source: Source {
|
||||||
@@ -199,6 +199,17 @@ impl DiagnosticLoggers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn longest_backtick_sequence_length(text: &str) -> usize {
|
||||||
|
let mut count = 0;
|
||||||
|
for c in text.chars() {
|
||||||
|
if c == '`' {
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
impl DiagnosticMessage {
|
impl DiagnosticMessage {
|
||||||
pub fn full_error_message(&self) -> String {
|
pub fn full_error_message(&self) -> String {
|
||||||
match &self.location {
|
match &self.location {
|
||||||
@@ -216,12 +227,38 @@ impl DiagnosticMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(&mut self, text: &str) -> &mut Self {
|
fn text(&mut self, text: &str) -> &mut Self {
|
||||||
self.plaintext_message = text.to_owned();
|
self.plaintext_message = text.to_owned();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
pub fn message(&mut self, text: &str, args: &[&str]) -> &mut Self {
|
||||||
|
let parts = text.split("{}");
|
||||||
|
let args = args.iter().chain(std::iter::repeat(&""));
|
||||||
|
let mut plain = String::with_capacity(2 * text.len());
|
||||||
|
let mut markdown = String::with_capacity(2 * text.len());
|
||||||
|
for (p, a) in parts.zip(args) {
|
||||||
|
plain.push_str(p);
|
||||||
|
plain.push_str(a);
|
||||||
|
markdown.push_str(p);
|
||||||
|
if a.len() > 0 {
|
||||||
|
let count = longest_backtick_sequence_length(a) + 1;
|
||||||
|
markdown.push_str(&"`".repeat(count));
|
||||||
|
if count > 1 {
|
||||||
|
markdown.push_str(" ");
|
||||||
|
}
|
||||||
|
markdown.push_str(a);
|
||||||
|
if count > 1 {
|
||||||
|
markdown.push_str(" ");
|
||||||
|
}
|
||||||
|
markdown.push_str(&"`".repeat(count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.text(&plain);
|
||||||
|
self.markdown(&markdown);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn markdown(&mut self, text: &str) -> &mut Self {
|
pub fn markdown(&mut self, text: &str) -> &mut Self {
|
||||||
self.markdown_message = text.to_owned();
|
self.markdown_message = text.to_owned();
|
||||||
self
|
self
|
||||||
@@ -276,3 +313,20 @@ impl DiagnosticMessage {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_message() {
|
||||||
|
let mut m = DiagnosticLoggers::new("foo")
|
||||||
|
.logger()
|
||||||
|
.new_entry("id", "name");
|
||||||
|
m.message("hello: {}", &["hello"]);
|
||||||
|
assert_eq!("hello: hello", m.plaintext_message);
|
||||||
|
assert_eq!("hello: `hello`", m.markdown_message);
|
||||||
|
|
||||||
|
let mut m = DiagnosticLoggers::new("foo")
|
||||||
|
.logger()
|
||||||
|
.new_entry("id", "name");
|
||||||
|
m.message("hello with backticks: {}", &["`hello`"]);
|
||||||
|
assert_eq!("hello with backticks: `hello`", m.plaintext_message);
|
||||||
|
assert_eq!("hello with backticks: `` `hello` ``", m.markdown_message);
|
||||||
|
}
|
||||||
|
|||||||
@@ -276,7 +276,8 @@ impl<'a> Visitor<'a> {
|
|||||||
|
|
||||||
fn record_parse_error_for_node(
|
fn record_parse_error_for_node(
|
||||||
&mut self,
|
&mut self,
|
||||||
error_message: String,
|
message: &str,
|
||||||
|
args: &[&str],
|
||||||
node: Node,
|
node: Node,
|
||||||
status_page: bool,
|
status_page: bool,
|
||||||
) {
|
) {
|
||||||
@@ -291,10 +292,11 @@ impl<'a> Visitor<'a> {
|
|||||||
);
|
);
|
||||||
let mut mesg = self
|
let mut mesg = self
|
||||||
.diagnostics_writer
|
.diagnostics_writer
|
||||||
.message("parse-error", "Parse error");
|
.new_entry("parse-error", "Parse error");
|
||||||
&mesg.severity(diagnostics::Severity::Error)
|
&mesg
|
||||||
|
.severity(diagnostics::Severity::Error)
|
||||||
.location(self.path, start_line, start_column, end_line, end_column)
|
.location(self.path, start_line, start_column, end_line, end_column)
|
||||||
.text(&error_message);
|
.message(message, args);
|
||||||
if status_page {
|
if status_page {
|
||||||
&mesg.status_page();
|
&mesg.status_page();
|
||||||
}
|
}
|
||||||
@@ -302,15 +304,19 @@ impl<'a> Visitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enter_node(&mut self, node: Node) -> bool {
|
fn enter_node(&mut self, node: Node) -> bool {
|
||||||
if node.is_error() || node.is_missing() {
|
if node.is_missing() {
|
||||||
let error_message = if node.is_missing() {
|
self.record_parse_error_for_node(
|
||||||
format!("parse error: expecting '{}'", node.kind())
|
"parse error: expecting {}",
|
||||||
} else {
|
&[node.kind()],
|
||||||
"parse error".to_string()
|
node,
|
||||||
};
|
true,
|
||||||
self.record_parse_error_for_node(error_message, node, true);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if node.is_error() {
|
||||||
|
self.record_parse_error_for_node("parse error", &[], node, true);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
let id = self.trap_writer.fresh_id();
|
let id = self.trap_writer.fresh_id();
|
||||||
|
|
||||||
@@ -390,14 +396,13 @@ impl<'a> Visitor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let error_message = format!("unknown table type: '{}'", node.kind());
|
|
||||||
self.record_parse_error(
|
self.record_parse_error(
|
||||||
loc,
|
loc,
|
||||||
self.diagnostics_writer
|
self.diagnostics_writer
|
||||||
.message("parse-error", "Parse error")
|
.new_entry("parse-error", "Parse error")
|
||||||
.severity(diagnostics::Severity::Error)
|
.severity(diagnostics::Severity::Error)
|
||||||
.location(self.path, start_line, start_column, end_line, end_column)
|
.location(self.path, start_line, start_column, end_line, end_column)
|
||||||
.text(&error_message),
|
.message("unknown table type: {}", &[node.kind()]),
|
||||||
);
|
);
|
||||||
|
|
||||||
valid = false;
|
valid = false;
|
||||||
@@ -445,23 +450,29 @@ impl<'a> Visitor<'a> {
|
|||||||
values.push(trap::Arg::Label(child_node.label));
|
values.push(trap::Arg::Label(child_node.label));
|
||||||
}
|
}
|
||||||
} else if field.name.is_some() {
|
} else if field.name.is_some() {
|
||||||
let error_message = format!(
|
self.record_parse_error_for_node(
|
||||||
"type mismatch for field {}::{} with type {:?} != {:?}",
|
"type mismatch for field {}::{} with type {} != {}",
|
||||||
node.kind(),
|
&[
|
||||||
child_node.field_name.unwrap_or("child"),
|
node.kind(),
|
||||||
child_node.type_name,
|
child_node.field_name.unwrap_or("child"),
|
||||||
field.type_info
|
&format!("{:?}", child_node.type_name),
|
||||||
|
&format!("{:?}", field.type_info),
|
||||||
|
],
|
||||||
|
*node,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
self.record_parse_error_for_node(error_message, *node,false);
|
|
||||||
}
|
}
|
||||||
} else if child_node.field_name.is_some() || child_node.type_name.named {
|
} else if child_node.field_name.is_some() || child_node.type_name.named {
|
||||||
let error_message = format!(
|
self.record_parse_error_for_node(
|
||||||
"value for unknown field: {}::{} and type {:?}",
|
"value for unknown field: {}::{} and type {}",
|
||||||
node.kind(),
|
&[
|
||||||
&child_node.field_name.unwrap_or("child"),
|
node.kind(),
|
||||||
&child_node.type_name
|
&child_node.field_name.unwrap_or("child"),
|
||||||
|
&format!("{:?}", child_node.type_name),
|
||||||
|
],
|
||||||
|
*node,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
self.record_parse_error_for_node(error_message, *node, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
@@ -484,7 +495,7 @@ impl<'a> Visitor<'a> {
|
|||||||
node.kind(),
|
node.kind(),
|
||||||
column_name
|
column_name
|
||||||
);
|
);
|
||||||
self.record_parse_error_for_node(error_message, *node, false);
|
self.record_parse_error_for_node(&error_message, &[], *node, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Storage::Table {
|
Storage::Table {
|
||||||
@@ -494,13 +505,12 @@ impl<'a> Visitor<'a> {
|
|||||||
} => {
|
} => {
|
||||||
for (index, child_value) in child_values.iter().enumerate() {
|
for (index, child_value) in child_values.iter().enumerate() {
|
||||||
if !*has_index && index > 0 {
|
if !*has_index && index > 0 {
|
||||||
let error_message = format!(
|
self.record_parse_error_for_node(
|
||||||
"too many values for field: {}::{}",
|
"too many values for field: {}::{}",
|
||||||
node.kind(),
|
&[node.kind(), table_name],
|
||||||
table_name,
|
*node,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.record_parse_error_for_node(error_message, *node, false);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let mut args = vec![trap::Arg::Label(parent_id)];
|
let mut args = vec![trap::Arg::Label(parent_id)];
|
||||||
@@ -597,8 +607,8 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
|
|||||||
visitor.diagnostics_writer.write(
|
visitor.diagnostics_writer.write(
|
||||||
visitor
|
visitor
|
||||||
.diagnostics_writer
|
.diagnostics_writer
|
||||||
.message("internal-error", "Internal error")
|
.new_entry("internal-error", "Internal error")
|
||||||
.text("expecting a line break symbol, but none found while correcting end column value")
|
.message("expecting a line break symbol, but none found while correcting end column value", &[])
|
||||||
.severity(diagnostics::Severity::Error),
|
.severity(diagnostics::Severity::Error),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -612,12 +622,11 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
|
|||||||
visitor.diagnostics_writer.write(
|
visitor.diagnostics_writer.write(
|
||||||
visitor
|
visitor
|
||||||
.diagnostics_writer
|
.diagnostics_writer
|
||||||
.message("internal-error", "Internal error")
|
.new_entry("internal-error", "Internal error")
|
||||||
.text(&format!(
|
.message(
|
||||||
"cannot correct end column value: end_byte index {} is not in range [1,{}]",
|
"cannot correct end column value: end_byte index {} is not in range [1,{}]",
|
||||||
index,
|
&[&index.to_string(), &source.len().to_string()],
|
||||||
source.len()
|
)
|
||||||
))
|
|
||||||
.severity(diagnostics::Severity::Error),
|
.severity(diagnostics::Severity::Error),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
main_thread_logger.write(
|
main_thread_logger.write(
|
||||||
main_thread_logger
|
main_thread_logger
|
||||||
.message("configuration-error", "Configuration error")
|
.new_entry("configuration-error", "Configuration error")
|
||||||
.text(&format!("{}; defaulting to 1 thread.", e))
|
.message("{}; defaulting to 1 thread.", &[&e])
|
||||||
.severity(diagnostics::Severity::Warning),
|
.severity(diagnostics::Severity::Warning),
|
||||||
);
|
);
|
||||||
1
|
1
|
||||||
@@ -94,8 +94,8 @@ fn main() -> std::io::Result<()> {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
main_thread_logger.write(
|
main_thread_logger.write(
|
||||||
main_thread_logger
|
main_thread_logger
|
||||||
.message("configuration-error", "Configuration error")
|
.new_entry("configuration-error", "Configuration error")
|
||||||
.text(&format!("{}; using gzip.", e))
|
.message("{}; using gzip.", &[&e])
|
||||||
.severity(diagnostics::Severity::Warning),
|
.severity(diagnostics::Severity::Warning),
|
||||||
);
|
);
|
||||||
trap::Compression::Gzip
|
trap::Compression::Gzip
|
||||||
@@ -197,16 +197,15 @@ fn main() -> std::io::Result<()> {
|
|||||||
needs_conversion = false;
|
needs_conversion = false;
|
||||||
diagnostics_writer.write(
|
diagnostics_writer.write(
|
||||||
diagnostics_writer
|
diagnostics_writer
|
||||||
.message(
|
.new_entry(
|
||||||
"character-decoding-error",
|
"character-decoding-error",
|
||||||
"Character decoding error",
|
"Character decoding error",
|
||||||
)
|
)
|
||||||
.file(&path.to_string_lossy())
|
.file(&path.to_string_lossy())
|
||||||
.text(&format!(
|
.message(
|
||||||
"could not decode the file contents as '{}': {}",
|
"could not decode the file contents as {}: {}",
|
||||||
&encoding_name,
|
&[&encoding_name, &msg],
|
||||||
msg,
|
)
|
||||||
))
|
|
||||||
.status_page()
|
.status_page()
|
||||||
.severity(diagnostics::Severity::Warning),
|
.severity(diagnostics::Severity::Warning),
|
||||||
);
|
);
|
||||||
@@ -216,12 +215,12 @@ fn main() -> std::io::Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
diagnostics_writer.write(
|
diagnostics_writer.write(
|
||||||
diagnostics_writer
|
diagnostics_writer
|
||||||
.message("unknown-character-encoding", "Unknown character encoding")
|
.new_entry("unknown-character-encoding", "Unknown character encoding")
|
||||||
.file(&path.to_string_lossy())
|
.file(&path.to_string_lossy())
|
||||||
.text(&format!(
|
.message(
|
||||||
"unknown character encoding '{}' in '#encoding:' directive.",
|
"unknown character encoding {} in {} directive.",
|
||||||
&encoding_name
|
&[&encoding_name, "#encoding:"],
|
||||||
))
|
)
|
||||||
.status_page()
|
.status_page()
|
||||||
.help_link("https://docs.ruby-lang.org/en/3.2/syntax/comments_rdoc.html#label-encoding+Directive")
|
.help_link("https://docs.ruby-lang.org/en/3.2/syntax/comments_rdoc.html#label-encoding+Directive")
|
||||||
.severity(diagnostics::Severity::Warning),
|
.severity(diagnostics::Severity::Warning),
|
||||||
|
|||||||
Reference in New Issue
Block a user