mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge pull request #12216 from aibaars/diagnostics-2
Ruby: improve diagnostic messages
This commit is contained in:
@@ -88,7 +88,7 @@ pub struct LogWriter {
|
||||
}
|
||||
|
||||
impl LogWriter {
|
||||
pub fn message(&self, id: &str, name: &str) -> DiagnosticMessage {
|
||||
pub fn new_entry(&self, id: &str, name: &str) -> DiagnosticMessage {
|
||||
DiagnosticMessage {
|
||||
timestamp: chrono::Utc::now(),
|
||||
source: Source {
|
||||
@@ -199,9 +199,29 @@ impl DiagnosticLoggers {
|
||||
}
|
||||
}
|
||||
|
||||
fn longest_backtick_sequence_length(text: &str) -> usize {
|
||||
let mut result = 0;
|
||||
let mut count = 0;
|
||||
for c in text.chars() {
|
||||
if c == '`' {
|
||||
count += 1;
|
||||
} else {
|
||||
if count > result {
|
||||
result = count;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
impl DiagnosticMessage {
|
||||
pub fn full_error_message(&self) -> String {
|
||||
match &self.location {
|
||||
Some(Location {
|
||||
file: Some(path),
|
||||
start_line: None,
|
||||
..
|
||||
}) => format!("{}: {}", path, self.plaintext_message),
|
||||
Some(Location {
|
||||
file: Some(path),
|
||||
start_line: Some(line),
|
||||
@@ -211,12 +231,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
|
||||
}
|
||||
|
||||
#[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 {
|
||||
self.markdown_message = text.to_owned();
|
||||
self
|
||||
@@ -249,6 +295,11 @@ impl DiagnosticMessage {
|
||||
self.visibility.telemetry = true;
|
||||
self
|
||||
}
|
||||
pub fn file(&mut self, path: &str) -> &mut Self {
|
||||
let loc = self.location.get_or_insert(Default::default());
|
||||
loc.file = Some(path.to_owned());
|
||||
self
|
||||
}
|
||||
pub fn location(
|
||||
&mut self,
|
||||
path: &str,
|
||||
@@ -266,3 +317,23 @@ impl DiagnosticMessage {
|
||||
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: {}", &["oh `hello`!"]);
|
||||
assert_eq!("hello with backticks: oh `hello`!", m.plaintext_message);
|
||||
assert_eq!(
|
||||
"hello with backticks: `` oh `hello`! ``",
|
||||
m.markdown_message
|
||||
);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,13 @@ impl<'a> Visitor<'a> {
|
||||
);
|
||||
}
|
||||
|
||||
fn record_parse_error_for_node(&mut self, error_message: String, node: Node) {
|
||||
fn record_parse_error_for_node(
|
||||
&mut self,
|
||||
message: &str,
|
||||
args: &[&str],
|
||||
node: Node,
|
||||
status_page: bool,
|
||||
) {
|
||||
let (start_line, start_column, end_line, end_column) = location_for(self, node);
|
||||
let loc = location(
|
||||
self.trap_writer,
|
||||
@@ -284,26 +290,38 @@ impl<'a> Visitor<'a> {
|
||||
end_line,
|
||||
end_column,
|
||||
);
|
||||
self.record_parse_error(
|
||||
loc,
|
||||
self.diagnostics_writer
|
||||
.message("parse-error", "Parse error")
|
||||
.severity(diagnostics::Severity::Error)
|
||||
.location(self.path, start_line, start_column, end_line, end_column)
|
||||
.text(&error_message),
|
||||
);
|
||||
let mut mesg = self
|
||||
.diagnostics_writer
|
||||
.new_entry("parse-error", "Parse error");
|
||||
&mesg
|
||||
.severity(diagnostics::Severity::Error)
|
||||
.location(self.path, start_line, start_column, end_line, end_column)
|
||||
.message(message, args);
|
||||
if status_page {
|
||||
&mesg.status_page();
|
||||
}
|
||||
self.record_parse_error(loc, &mesg);
|
||||
}
|
||||
|
||||
fn enter_node(&mut self, node: Node) -> bool {
|
||||
if node.is_error() || node.is_missing() {
|
||||
let error_message = if node.is_missing() {
|
||||
format!("parse error: expecting '{}'", node.kind())
|
||||
} else {
|
||||
"parse error".to_string()
|
||||
};
|
||||
self.record_parse_error_for_node(error_message, node);
|
||||
if node.is_missing() {
|
||||
self.record_parse_error_for_node(
|
||||
"A parse error occurred (expected {} symbol). Check the syntax of the file using the {} command. If the file is invalid, correct the error or exclude the file from analysis.",
|
||||
&[node.kind(), "ruby -c"],
|
||||
node,
|
||||
true,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if node.is_error() {
|
||||
self.record_parse_error_for_node(
|
||||
"A parse error occurred. Check the syntax of the file using the {} command. If the file is invalid, correct the error or exclude the file from analysis.",
|
||||
&["ruby -c"],
|
||||
node,
|
||||
true,
|
||||
);
|
||||
return false;
|
||||
};
|
||||
|
||||
let id = self.trap_writer.fresh_id();
|
||||
|
||||
@@ -383,15 +401,13 @@ impl<'a> Visitor<'a> {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let error_message = format!("unknown table type: '{}'", node.kind());
|
||||
self.record_parse_error(
|
||||
loc,
|
||||
self.diagnostics_writer
|
||||
.message("parse-error", "Parse error")
|
||||
.new_entry("parse-error", "Parse error")
|
||||
.severity(diagnostics::Severity::Error)
|
||||
.location(self.path, start_line, start_column, end_line, end_column)
|
||||
.text(&error_message)
|
||||
.status_page(),
|
||||
.message("Unknown table type: {}", &[node.kind()]),
|
||||
);
|
||||
|
||||
valid = false;
|
||||
@@ -439,23 +455,29 @@ impl<'a> Visitor<'a> {
|
||||
values.push(trap::Arg::Label(child_node.label));
|
||||
}
|
||||
} else if field.name.is_some() {
|
||||
let error_message = format!(
|
||||
"type mismatch for field {}::{} with type {:?} != {:?}",
|
||||
node.kind(),
|
||||
child_node.field_name.unwrap_or("child"),
|
||||
child_node.type_name,
|
||||
field.type_info
|
||||
self.record_parse_error_for_node(
|
||||
"Type mismatch for field {}::{} with type {} != {}",
|
||||
&[
|
||||
node.kind(),
|
||||
child_node.field_name.unwrap_or("child"),
|
||||
&format!("{:?}", child_node.type_name),
|
||||
&format!("{:?}", field.type_info),
|
||||
],
|
||||
*node,
|
||||
false,
|
||||
);
|
||||
self.record_parse_error_for_node(error_message, *node);
|
||||
}
|
||||
} else if child_node.field_name.is_some() || child_node.type_name.named {
|
||||
let error_message = format!(
|
||||
"value for unknown field: {}::{} and type {:?}",
|
||||
node.kind(),
|
||||
&child_node.field_name.unwrap_or("child"),
|
||||
&child_node.type_name
|
||||
self.record_parse_error_for_node(
|
||||
"Value for unknown field: {}::{} and type {}",
|
||||
&[
|
||||
node.kind(),
|
||||
&child_node.field_name.unwrap_or("child"),
|
||||
&format!("{:?}", child_node.type_name),
|
||||
],
|
||||
*node,
|
||||
false,
|
||||
);
|
||||
self.record_parse_error_for_node(error_message, *node);
|
||||
}
|
||||
}
|
||||
let mut args = Vec::new();
|
||||
@@ -471,14 +493,14 @@ impl<'a> Visitor<'a> {
|
||||
let error_message = format!(
|
||||
"{} for field: {}::{}",
|
||||
if child_values.is_empty() {
|
||||
"missing value"
|
||||
"Missing value"
|
||||
} else {
|
||||
"too many values"
|
||||
"Too many values"
|
||||
},
|
||||
node.kind(),
|
||||
column_name
|
||||
);
|
||||
self.record_parse_error_for_node(error_message, *node);
|
||||
self.record_parse_error_for_node(&error_message, &[], *node, false);
|
||||
}
|
||||
}
|
||||
Storage::Table {
|
||||
@@ -488,13 +510,12 @@ impl<'a> Visitor<'a> {
|
||||
} => {
|
||||
for (index, child_value) in child_values.iter().enumerate() {
|
||||
if !*has_index && index > 0 {
|
||||
let error_message = format!(
|
||||
"too many values for field: {}::{}",
|
||||
node.kind(),
|
||||
table_name,
|
||||
self.record_parse_error_for_node(
|
||||
"Too many values for field: {}::{}",
|
||||
&[node.kind(), table_name],
|
||||
*node,
|
||||
false,
|
||||
);
|
||||
|
||||
self.record_parse_error_for_node(error_message, *node);
|
||||
break;
|
||||
}
|
||||
let mut args = vec![trap::Arg::Label(parent_id)];
|
||||
@@ -591,9 +612,8 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
|
||||
visitor.diagnostics_writer.write(
|
||||
visitor
|
||||
.diagnostics_writer
|
||||
.message("internal-error", "Internal error")
|
||||
.text("expecting a line break symbol, but none found while correcting end column value")
|
||||
.status_page()
|
||||
.new_entry("internal-error", "Internal error")
|
||||
.message("Expecting a line break symbol, but none found while correcting end column value", &[])
|
||||
.severity(diagnostics::Severity::Error),
|
||||
);
|
||||
}
|
||||
@@ -607,13 +627,11 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
|
||||
visitor.diagnostics_writer.write(
|
||||
visitor
|
||||
.diagnostics_writer
|
||||
.message("internal-error", "Internal error")
|
||||
.text(&format!(
|
||||
"cannot correct end column value: end_byte index {} is not in range [1,{}]",
|
||||
index,
|
||||
source.len()
|
||||
))
|
||||
.status_page()
|
||||
.new_entry("internal-error", "Internal error")
|
||||
.message(
|
||||
"Cannot correct end column value: end_byte index {} is not in range [1,{}]",
|
||||
&[&index.to_string(), &source.len().to_string()],
|
||||
)
|
||||
.severity(diagnostics::Severity::Error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,9 +73,8 @@ fn main() -> std::io::Result<()> {
|
||||
Err(e) => {
|
||||
main_thread_logger.write(
|
||||
main_thread_logger
|
||||
.message("configuration-error", "Configuration error")
|
||||
.text(&format!("{}; defaulting to 1 thread.", e))
|
||||
.status_page()
|
||||
.new_entry("configuration-error", "Configuration error")
|
||||
.message("{}; defaulting to 1 thread.", &[&e])
|
||||
.severity(diagnostics::Severity::Warning),
|
||||
);
|
||||
1
|
||||
@@ -95,9 +94,8 @@ fn main() -> std::io::Result<()> {
|
||||
Err(e) => {
|
||||
main_thread_logger.write(
|
||||
main_thread_logger
|
||||
.message("configuration-error", "Configuration error")
|
||||
.text(&format!("{}; using gzip.", e))
|
||||
.status_page()
|
||||
.new_entry("configuration-error", "Configuration error")
|
||||
.message("{}; using gzip.", &[&e])
|
||||
.severity(diagnostics::Severity::Warning),
|
||||
);
|
||||
trap::Compression::Gzip
|
||||
@@ -199,17 +197,17 @@ fn main() -> std::io::Result<()> {
|
||||
needs_conversion = false;
|
||||
diagnostics_writer.write(
|
||||
diagnostics_writer
|
||||
.message(
|
||||
"character-encoding-error",
|
||||
"Character encoding error",
|
||||
.new_entry(
|
||||
"character-decoding-error",
|
||||
"Character decoding error",
|
||||
)
|
||||
.file(&path.to_string_lossy())
|
||||
.message(
|
||||
"Could not decode the file contents as {}: {}. The contents of the file must match the character encoding specified in the {} directive.",
|
||||
&[&encoding_name, &msg, "encoding:"],
|
||||
)
|
||||
.text(&format!(
|
||||
"{}: character decoding failure: {} ({})",
|
||||
&path.to_string_lossy(),
|
||||
msg,
|
||||
&encoding_name
|
||||
))
|
||||
.status_page()
|
||||
.help_link("https://docs.ruby-lang.org/en/master/syntax/comments_rdoc.html#label-encoding+Directive")
|
||||
.severity(diagnostics::Severity::Warning),
|
||||
);
|
||||
}
|
||||
@@ -218,13 +216,14 @@ fn main() -> std::io::Result<()> {
|
||||
} else {
|
||||
diagnostics_writer.write(
|
||||
diagnostics_writer
|
||||
.message("character-encoding-error", "Character encoding error")
|
||||
.text(&format!(
|
||||
"{}: unknown character encoding: '{}'",
|
||||
&path.to_string_lossy(),
|
||||
&encoding_name
|
||||
))
|
||||
.new_entry("unknown-character-encoding", "Unknown character encoding")
|
||||
.file(&path.to_string_lossy())
|
||||
.message(
|
||||
"Unknown character encoding {} in {} directive.",
|
||||
&[&encoding_name, "#encoding:"],
|
||||
)
|
||||
.status_page()
|
||||
.help_link("https://docs.ruby-lang.org/en/master/syntax/comments_rdoc.html#label-encoding+Directive")
|
||||
.severity(diagnostics::Severity::Warning),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
| src/not_ruby.rb:5:25:5:26 | parse error | Extraction failed in src/not_ruby.rb with error parse error | 2 |
|
||||
| src/not_ruby.rb:5:25:5:26 | A parse error occurred. Check the syntax of the file using the ruby -c command. If the file is invalid, correct the error or exclude the file from analysis. | Extraction failed in src/not_ruby.rb with error A parse error occurred. Check the syntax of the file using the ruby -c command. If the file is invalid, correct the error or exclude the file from analysis. | 2 |
|
||||
|
||||
Reference in New Issue
Block a user