Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 11949c6

Browse files
authored
Merge pull request #176 from github/diagnostics-entries
Start writing diagnostics to the DB, and some basic summary/diagnostics queries
2 parents 07c059c + 15712df commit 11949c6

24 files changed

Lines changed: 6978 additions & 3583 deletions

extractor/src/extractor.rs

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -305,22 +305,58 @@ struct Visitor<'a> {
305305
}
306306

307307
impl Visitor<'_> {
308+
fn record_parse_error(
309+
&mut self,
310+
error_message: String,
311+
full_error_message: String,
312+
loc: Label,
313+
) {
314+
error!("{}", full_error_message);
315+
let id = self.trap_writer.fresh_id();
316+
self.trap_writer.add_tuple(
317+
"diagnostics",
318+
vec![
319+
Arg::Label(id),
320+
Arg::Int(40), // severity 40 = error
321+
Arg::String("parse_error".to_string()),
322+
Arg::String(error_message),
323+
Arg::String(full_error_message),
324+
Arg::Label(loc),
325+
],
326+
);
327+
}
328+
329+
fn record_parse_error_for_node(
330+
&mut self,
331+
error_message: String,
332+
full_error_message: String,
333+
node: Node,
334+
) {
335+
let (start_line, start_column, end_line, end_column) = location_for(&self.source, node);
336+
let loc = self.trap_writer.location(
337+
self.file_label,
338+
start_line,
339+
start_column,
340+
end_line,
341+
end_column,
342+
);
343+
self.record_parse_error(error_message, full_error_message, loc);
344+
}
345+
308346
fn enter_node(&mut self, node: Node) -> bool {
309-
if node.is_error() {
310-
error!(
311-
"{}:{}: parse error",
312-
&self.path,
313-
node.start_position().row + 1
314-
);
315-
return false;
316-
}
317-
if node.is_missing() {
318-
error!(
319-
"{}:{}: parse error: expecting '{}'",
347+
if node.is_error() || node.is_missing() {
348+
let error_message = if node.is_missing() {
349+
format!("parse error: expecting '{}'", node.kind())
350+
} else {
351+
"parse error".to_string()
352+
};
353+
let full_error_message = format!(
354+
"{}:{}: {}",
320355
&self.path,
321356
node.start_position().row + 1,
322-
node.kind()
357+
error_message
323358
);
359+
self.record_parse_error_for_node(error_message, full_error_message, node);
324360
return false;
325361
}
326362

@@ -405,12 +441,15 @@ impl Visitor<'_> {
405441
}
406442
}
407443
_ => {
408-
error!(
409-
"{}:{}: unknown table type: '{}'",
444+
let error_message = format!("unknown table type: '{}'", node.kind());
445+
let full_error_message = format!(
446+
"{}:{}: {}",
410447
&self.path,
411448
node.start_position().row + 1,
412-
node.kind()
449+
error_message
413450
);
451+
self.record_parse_error(error_message, full_error_message, loc);
452+
414453
valid = false;
415454
}
416455
}
@@ -456,26 +495,36 @@ impl Visitor<'_> {
456495
values.push(Arg::Label(child_node.label));
457496
}
458497
} else if field.name.is_some() {
459-
error!(
460-
"{}:{}: type mismatch for field {}::{} with type {:?} != {:?}",
461-
&self.path,
462-
node.start_position().row + 1,
498+
let error_message = format!(
499+
"type mismatch for field {}::{} with type {:?} != {:?}",
463500
node.kind(),
464501
child_node.field_name.unwrap_or("child"),
465502
child_node.type_name,
466503
field.type_info
467-
)
504+
);
505+
let full_error_message = format!(
506+
"{}:{}: {}",
507+
&self.path,
508+
node.start_position().row + 1,
509+
error_message
510+
);
511+
self.record_parse_error_for_node(error_message, full_error_message, *node);
468512
}
469513
} else {
470514
if child_node.field_name.is_some() || child_node.type_name.named {
471-
error!(
472-
"{}:{}: value for unknown field: {}::{} and type {:?}",
473-
&self.path,
474-
node.start_position().row + 1,
515+
let error_message = format!(
516+
"value for unknown field: {}::{} and type {:?}",
475517
node.kind(),
476518
&child_node.field_name.unwrap_or("child"),
477519
&child_node.type_name
478520
);
521+
let full_error_message = format!(
522+
"{}:{}: {}",
523+
&self.path,
524+
node.start_position().row + 1,
525+
error_message
526+
);
527+
self.record_parse_error_for_node(error_message, full_error_message, *node);
479528
}
480529
}
481530
}
@@ -489,18 +538,23 @@ impl Visitor<'_> {
489538
args.push(child_values.first().unwrap().clone());
490539
} else {
491540
is_valid = false;
492-
error!(
493-
"{}:{}: {} for field: {}::{}",
494-
&self.path,
495-
node.start_position().row + 1,
541+
let error_message = format!(
542+
"{} for field: {}::{}",
496543
if child_values.is_empty() {
497544
"missing value"
498545
} else {
499546
"too many values"
500547
},
501548
node.kind(),
502549
column_name
503-
)
550+
);
551+
let full_error_message = format!(
552+
"{}:{}: {}",
553+
&self.path,
554+
node.start_position().row + 1,
555+
error_message
556+
);
557+
self.record_parse_error_for_node(error_message, full_error_message, *node);
504558
}
505559
}
506560
Storage::Table {

generator/src/main.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ fn convert_nodes<'a>(nodes: &'a node_types::NodeTypeMap) -> Vec<dbscheme::Entry<
248248
entries.push(dbscheme::Entry::Table(token_table));
249249
entries.push(dbscheme::Entry::Case(token_case));
250250

251+
// Add the diagnostics table
252+
let (diagnostics_case, diagnostics_table) = create_diagnostics();
253+
entries.push(dbscheme::Entry::Table(diagnostics_table));
254+
entries.push(dbscheme::Entry::Case(diagnostics_case));
255+
251256
// Create a union of all database types.
252257
entries.push(dbscheme::Entry::Union(dbscheme::Union {
253258
name: "ast_node",
@@ -590,6 +595,69 @@ fn create_source_location_prefix_table<'a>() -> dbscheme::Entry<'a> {
590595
})
591596
}
592597

598+
fn create_diagnostics<'a>() -> (dbscheme::Case<'a>, dbscheme::Table<'a>) {
599+
let table = dbscheme::Table {
600+
name: "diagnostics",
601+
keysets: None,
602+
columns: vec![
603+
dbscheme::Column {
604+
unique: true,
605+
db_type: dbscheme::DbColumnType::Int,
606+
name: "id",
607+
ql_type: ql::Type::AtType("diagnostic"),
608+
ql_type_is_ref: false,
609+
},
610+
dbscheme::Column {
611+
unique: false,
612+
db_type: dbscheme::DbColumnType::Int,
613+
name: "severity",
614+
ql_type: ql::Type::Int,
615+
ql_type_is_ref: true,
616+
},
617+
dbscheme::Column {
618+
unique: false,
619+
db_type: dbscheme::DbColumnType::String,
620+
name: "error_tag",
621+
ql_type: ql::Type::String,
622+
ql_type_is_ref: true,
623+
},
624+
dbscheme::Column {
625+
unique: false,
626+
db_type: dbscheme::DbColumnType::String,
627+
name: "error_message",
628+
ql_type: ql::Type::String,
629+
ql_type_is_ref: true,
630+
},
631+
dbscheme::Column {
632+
unique: false,
633+
db_type: dbscheme::DbColumnType::String,
634+
name: "full_error_message",
635+
ql_type: ql::Type::String,
636+
ql_type_is_ref: true,
637+
},
638+
dbscheme::Column {
639+
unique: false,
640+
db_type: dbscheme::DbColumnType::Int,
641+
name: "location",
642+
ql_type: ql::Type::AtType("location_default"),
643+
ql_type_is_ref: true,
644+
},
645+
],
646+
};
647+
let severities: Vec<(usize, &str)> = vec![
648+
(10, "diagnostic_debug"),
649+
(20, "diagnostic_info"),
650+
(30, "diagnostic_warning"),
651+
(40, "diagnostic_error"),
652+
];
653+
let case = dbscheme::Case {
654+
name: "diagnostic",
655+
column: "severity",
656+
branches: severities,
657+
};
658+
(case, table)
659+
}
660+
593661
fn main() {
594662
tracing_subscriber::fmt()
595663
.with_target(false)

ql/src/codeql_ruby/Diagnostics.qll

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
private import codeql.Locations
2+
3+
/** A diagnostic emitted during extraction, such as a parse error */
4+
class Diagnostic extends @diagnostic {
5+
int severity;
6+
string tag;
7+
string message;
8+
string fullMessage;
9+
Location location;
10+
11+
Diagnostic() { diagnostics(this, severity, tag, message, fullMessage, location) }
12+
13+
/**
14+
* Gets the numerical severity level associated with this diagnostic.
15+
*/
16+
int getSeverity() { result = severity }
17+
18+
/** Gets a string representation of the severity of this diagnostic. */
19+
string getSeverityText() {
20+
severity = 10 and result = "Debug"
21+
or
22+
severity = 20 and result = "Info"
23+
or
24+
severity = 30 and result = "Warning"
25+
or
26+
severity = 40 and result = "Error"
27+
}
28+
29+
/** Gets the error code associated with this diagnostic, e.g. parse_error. */
30+
string getTag() { result = tag }
31+
32+
/**
33+
* Gets the error message text associated with this diagnostic.
34+
*/
35+
string getMessage() { result = message }
36+
37+
/**
38+
* Gets the full error message text associated with this diagnostic.
39+
*/
40+
string getFullMessage() { result = fullMessage }
41+
42+
/** Gets the source location of this diagnostic. */
43+
Location getLocation() { result = location }
44+
45+
/** Gets a textual representation of this diagnostic. */
46+
string toString() { result = this.getMessage() }
47+
}
48+
49+
/** A diagnostic relating to a particular error in extracting a file. */
50+
class ExtractionError extends Diagnostic, @diagnostic_error {
51+
ExtractionError() { this.getTag() = "parse_error" }
52+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Extraction errors
3+
* @description List all extraction errors for files in the source code directory.
4+
* @kind diagnostic
5+
* @id rb/diagnostics/extraction-errors
6+
*/
7+
8+
import ruby
9+
import codeql_ruby.Diagnostics
10+
11+
/** Gets the SARIF severity to associate an error. */
12+
int getSeverity() { result = 2 }
13+
14+
from ExtractionError error, File f
15+
where
16+
f = error.getLocation().getFile() and
17+
exists(f.getRelativePath())
18+
select error, "Extraction failed in " + f + " with error " + error.getMessage(), getSeverity()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @name Successfully extracted files
3+
* @description Lists all files in the source code directory that were extracted
4+
* without encountering an error in the file.
5+
* @kind diagnostic
6+
* @id rb/diagnostics/successfully-extracted-files
7+
*/
8+
9+
import ruby
10+
import codeql_ruby.Diagnostics
11+
12+
from File f
13+
where
14+
not exists(ExtractionError e | e.getLocation().getFile() = f) and
15+
exists(f.getRelativePath())
16+
select f, ""
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @id rb/summary/number-of-files-extracted-with-errors
3+
* @name Total number of files that were extracted with errors
4+
* @description The total number of Ruby code files that we extracted, but where
5+
* at least one extraction error occurred in the process.
6+
* @kind metric
7+
* @tags summary
8+
*/
9+
10+
import ruby
11+
import codeql_ruby.Diagnostics
12+
13+
select count(File f |
14+
exists(ExtractionError e | e.getLocation().getFile() = f) and exists(f.getRelativePath())
15+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @id rb/summary/number-of-successfully-extracted-files
3+
* @name Total number of files that were extracted without error
4+
* @description The total number of Ruby code files that we extracted without
5+
* encountering any extraction errors
6+
* @kind metric
7+
* @tags summary
8+
*/
9+
10+
import ruby
11+
import codeql_ruby.Diagnostics
12+
13+
select count(File f |
14+
not exists(ExtractionError e | e.getLocation().getFile() = f) and exists(f.getRelativePath())
15+
)

ql/src/ruby.dbscheme

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,23 @@ case @token.kind of
12371237
;
12381238

12391239

1240+
diagnostics(
1241+
unique int id: @diagnostic,
1242+
int severity: int ref,
1243+
string error_tag: string ref,
1244+
string error_message: string ref,
1245+
string full_error_message: string ref,
1246+
int location: @location_default ref
1247+
);
1248+
1249+
case @diagnostic.severity of
1250+
10 = @diagnostic_debug
1251+
| 20 = @diagnostic_info
1252+
| 30 = @diagnostic_warning
1253+
| 40 = @diagnostic_error
1254+
;
1255+
1256+
12401257
@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield
12411258

12421259
@ast_node_parent = @ast_node | @file

0 commit comments

Comments
 (0)