-
Notifications
You must be signed in to change notification settings - Fork 1.9k
C++: Initial telemetry queries #17892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
f6776a4
C++: Initial telemetry queries and tests
calumgrant a53e1de
C++: Minor edits
calumgrant 6c402ad
C++: Minor edits
calumgrant 8d2cef6
C++: Minor edits
calumgrant 9a81ce8
C++: Separate int and float metrics
calumgrant 067ecde
C++: Match more tags
calumgrant faeff39
C++: Limit metrics to top 500
calumgrant e6f3512
C++: Remove unused class UndefinedIdentifier
calumgrant 34ee947
C++: Limit number of errors/includes to 50
calumgrant fb82d43
C++: Various renamings
calumgrant 593dcb6
C++: Remove missing includes test
calumgrant 2351328
C++: Rename SourceFiles metric
calumgrant 317f43d
C++: Don't use PrintAST in a test
calumgrant 91b1cb8
C++: Some renaming
calumgrant File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /** | ||
| * @name Compiler errors | ||
| * @description A count of all compiler errors, grouped by error text. | ||
| * @kind metric | ||
| * @tags summary telemetry | ||
| * @id cpp/telemetry/compiler-errors | ||
| */ | ||
|
|
||
| import Metrics | ||
|
|
||
| from CppMetrics::ErrorCount m | ||
| where RankMetric<CppMetrics::ErrorCount>::getRank(m) <= 50 | ||
| select m.toString(), m.getValue() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /** | ||
| * @name Database quality | ||
| * @description Metrics that indicate the quality of the database. | ||
| * @kind metric | ||
| * @tags summary telemetry | ||
| * @id cpp/telemetry/database-quality | ||
| */ | ||
|
|
||
| import Metrics | ||
|
|
||
| from QualityMetric m | ||
| select m.toString(), m.getValue() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import cpp | ||
|
|
||
| /** | ||
| * A syntax error. | ||
| */ | ||
| class SyntaxError extends CompilerError { | ||
| SyntaxError() { | ||
| this.getTag().matches("exp_%") or | ||
| this.getTag() = | ||
| [ | ||
| "bad_data_member_initialization", "bad_pure_specifier", "bad_return", "bad_uuid_string", | ||
| "literal_without_initializer", "missing_class_definition", "missing_exception_declaration", | ||
| "nonstd_const_member_decl_not_allowed", "operator_name_not_allowed", | ||
| "wide_string_invalid_in_asm" | ||
| ] | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A cannot open file error. | ||
| * Typically this is due to a missing include. | ||
| */ | ||
| class CannotOpenFileError extends CompilerError { | ||
| CannotOpenFileError() { this.hasTag(["cannot_open_file", "cannot_open_file_reason"]) } | ||
|
|
||
| string getIncludedFile() { | ||
| result = this.getMessage().regexpCapture("cannot open source file '([^']+)'", 1) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /** | ||
| * @name Extraction metrics | ||
| * @description Raw metrics relating to extraction. | ||
| * @kind metric | ||
| * @tags summary telemetry | ||
| * @id cpp/telemetry/extraction-metrics | ||
| */ | ||
|
|
||
| import Metrics | ||
|
|
||
| from ExtractionMetric m | ||
| select m.toString(), m.getValue() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| import cpp | ||
| import Diagnostics | ||
|
|
||
| /** | ||
| * A metric is a string with a value. | ||
| */ | ||
| abstract class Metric extends string { | ||
| bindingset[this] | ||
| Metric() { any() } | ||
| } | ||
|
|
||
| /** | ||
| * A metric that we want to report in cpp/telemetry/extraction-metrics | ||
| */ | ||
| abstract class ExtractionMetric extends Metric { | ||
| bindingset[this] | ||
| ExtractionMetric() { any() } | ||
|
|
||
| /** Gets the value of this metric. */ | ||
| abstract int getValue(); | ||
| } | ||
|
|
||
| /** | ||
| * A metric that provides a baseline for a SuccessMetric. | ||
| */ | ||
| abstract class BaseMetric extends ExtractionMetric { | ||
| bindingset[this] | ||
| BaseMetric() { any() } | ||
| } | ||
|
|
||
| /** | ||
| * A metric that is relative to another metric, | ||
| * so can be used to calculate percentages. | ||
| * | ||
| * For clarity, metrics should express success, | ||
| * so higher values means better. | ||
| */ | ||
| abstract class SuccessMetric extends ExtractionMetric { | ||
| bindingset[this] | ||
| SuccessMetric() { any() } | ||
|
|
||
| /** Gets the metric this is relative to. */ | ||
| abstract BaseMetric getBaseline(); | ||
| } | ||
|
|
||
| /** | ||
| * A metric used to report database quality. | ||
| */ | ||
| class QualityMetric extends Metric { | ||
| BaseMetric baseMetric; | ||
| SuccessMetric relativeMetric; | ||
|
|
||
| QualityMetric() { | ||
| baseMetric = relativeMetric.getBaseline() and this = "Percentage of " + relativeMetric | ||
| } | ||
|
|
||
| float getValue() { | ||
| baseMetric.getValue() > 0 and | ||
| result = 100.0 * relativeMetric.getValue() / baseMetric.getValue() | ||
| } | ||
| } | ||
|
|
||
| signature class RankedMetric extends Metric { | ||
| int getValue(); | ||
| } | ||
|
|
||
| module RankMetric<RankedMetric M> { | ||
| int getRank(M s) { s = rank[result](M m | | m order by m.getValue() desc) } | ||
| } | ||
|
|
||
| /** Various metrics we want to report. */ | ||
| module CppMetrics { | ||
| class Compilations extends BaseMetric { | ||
| Compilations() { this = "compilations" } | ||
|
|
||
| override int getValue() { result = count(Compilation c) } | ||
jketema marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| class SourceAndHeaderFiles extends BaseMetric { | ||
| SourceAndHeaderFiles() { this = "source/header files" } | ||
|
|
||
| override int getValue() { result = count(File f | f.fromSource()) } | ||
jketema marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| class SourceAndHeaderFilesWithoutErrors extends SuccessMetric { | ||
| SourceAndHeaderFilesWithoutErrors() { this = "source/header files without errors" } | ||
|
|
||
| override int getValue() { | ||
| result = count(File f | f.fromSource() and not exists(CompilerError e | f = e.getFile())) | ||
| } | ||
|
|
||
| override SourceAndHeaderFiles getBaseline() { any() } | ||
| } | ||
|
|
||
| class CompilationsWithoutErrors extends SuccessMetric { | ||
| CompilationsWithoutErrors() { this = "compilations without errors" } | ||
|
|
||
| override int getValue() { | ||
| result = count(Compilation c | not exists(Diagnostic d | d.getFile() = c.getAFileCompiled())) | ||
| } | ||
|
|
||
| override Compilations getBaseline() { any() } | ||
| } | ||
|
|
||
| class Expressions extends BaseMetric { | ||
| Expressions() { this = "expressions" } | ||
|
|
||
| override int getValue() { result = count(Expr e) } | ||
| } | ||
|
|
||
| class SucceededExpressions extends SuccessMetric { | ||
| SucceededExpressions() { this = "non-error expressions" } | ||
|
|
||
| override int getValue() { result = count(Expr e) - count(ErrorExpr e) } | ||
|
|
||
| override Expressions getBaseline() { any() } | ||
| } | ||
|
|
||
| class TypedExpressions extends SuccessMetric { | ||
| TypedExpressions() { this = "expressions with a known type" } | ||
|
|
||
| override int getValue() { result = count(Expr e | not e.getType() instanceof ErroneousType) } | ||
|
|
||
| override Expressions getBaseline() { any() } | ||
| } | ||
|
|
||
| class Calls extends BaseMetric { | ||
| Calls() { this = "calls" } | ||
|
|
||
| override int getValue() { result = count(Call c) } | ||
| } | ||
|
|
||
| class CallsWithExplicitTarget extends SuccessMetric { | ||
| CallsWithExplicitTarget() { this = "calls with an explicit target" } | ||
|
|
||
| override int getValue() { | ||
| result = count(Call c | not c.getTarget().getADeclarationEntry().isImplicit()) | ||
jketema marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| override Calls getBaseline() { any() } | ||
| } | ||
|
|
||
| class Variables extends BaseMetric { | ||
| Variables() { this = "variables" } | ||
|
|
||
| override int getValue() { result = count(Variable v) } | ||
| } | ||
|
|
||
| class VariablesKnownType extends SuccessMetric { | ||
| VariablesKnownType() { this = "variables with a known type" } | ||
|
|
||
| override int getValue() { | ||
| result = count(Variable v | not v.getType() instanceof ErroneousType) | ||
| } | ||
|
|
||
| override Variables getBaseline() { any() } | ||
| } | ||
|
|
||
| class LinesOfText extends BaseMetric { | ||
| LinesOfText() { this = "lines of text" } | ||
|
|
||
| override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLines()) } | ||
| } | ||
|
|
||
| class LinesOfCode extends BaseMetric { | ||
| LinesOfCode() { this = "lines of code" } | ||
|
|
||
| override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) } | ||
| } | ||
|
|
||
| private predicate errorLine(File file, int line) { | ||
| exists(Locatable l, Location loc | | ||
| loc = l.getLocation() and | ||
| loc.getFile() = file and | ||
| line in [loc.getStartLine() .. loc.getEndLine()] | ||
| | | ||
| l instanceof Diagnostic | ||
| or | ||
| l instanceof ErrorExpr | ||
| ) | ||
| } | ||
|
|
||
| class SucceededLines extends SuccessMetric { | ||
| SucceededLines() { this = "lines of code without errors" } | ||
|
|
||
| override int getValue() { | ||
| result = | ||
| sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) - | ||
| count(File f, int line | errorLine(f, line)) | ||
| } | ||
|
|
||
| override LinesOfCode getBaseline() { any() } | ||
| } | ||
|
|
||
| class Functions extends BaseMetric { | ||
| Functions() { this = "functions" } | ||
|
|
||
| override int getValue() { result = count(Function f) } | ||
| } | ||
|
|
||
| class SucceededFunctions extends SuccessMetric { | ||
| SucceededFunctions() { this = "functions without errors" } | ||
|
|
||
| override int getValue() { result = count(Function f | not f.hasErrors()) } | ||
|
|
||
| override Functions getBaseline() { any() } | ||
| } | ||
|
|
||
| class Includes extends BaseMetric { | ||
| Includes() { this = "#include directives" } | ||
|
|
||
| override int getValue() { result = count(Include i) + count(CannotOpenFileError e) } | ||
| } | ||
|
|
||
| class SucceededIncludes extends SuccessMetric { | ||
| SucceededIncludes() { this = "successfully resolved #include directives" } | ||
|
|
||
| override int getValue() { result = count(Include i) } | ||
|
|
||
| override Includes getBaseline() { any() } | ||
| } | ||
|
|
||
| class SucceededIncludeCount extends Metric { | ||
| string includeText; | ||
|
|
||
| SucceededIncludeCount() { | ||
| exists(Include i | | ||
| i.getIncludeText() = includeText and | ||
| exists(i.getFile().getRelativePath()) // Only report includes from the repo | ||
| ) and | ||
| this = "Successfully included " + includeText | ||
| } | ||
|
|
||
| int getValue() { result = count(Include i | i.getIncludeText() = includeText) } | ||
|
|
||
| string getIncludeText() { result = includeText } | ||
| } | ||
|
|
||
| class MissingIncludeCount extends Metric { | ||
| string includeText; | ||
|
|
||
| MissingIncludeCount() { | ||
| exists(CannotOpenFileError e | e.getIncludedFile() = includeText) and | ||
| this = "Failed to include '" + includeText + "'" | ||
| } | ||
|
|
||
| int getValue() { result = count(CannotOpenFileError e | e.getIncludedFile() = includeText) } | ||
|
|
||
| string getIncludeText() { result = includeText } | ||
| } | ||
|
|
||
| class CompilerErrors extends ExtractionMetric { | ||
| CompilerErrors() { this = "compiler errors" } | ||
|
|
||
| override int getValue() { result = count(CompilerError e) } | ||
| } | ||
|
|
||
| class ErrorCount extends Metric { | ||
| ErrorCount() { exists(CompilerError e | e.getMessage() = this) } | ||
|
|
||
| int getValue() { result = count(CompilerError e | e.getMessage() = this) } | ||
| } | ||
|
|
||
| class SyntaxErrorCount extends ExtractionMetric { | ||
| SyntaxErrorCount() { this = "syntax errors" } | ||
|
|
||
| override int getValue() { result = count(SyntaxError e) } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /** | ||
| * @name Failed to include header file | ||
| * @description A count of all failed includes, grouped by filename. | ||
| * @kind metric | ||
| * @tags summary telemetry | ||
| * @id cpp/telemetry/failed-includes | ||
| */ | ||
|
|
||
| import Metrics | ||
|
|
||
| from CppMetrics::MissingIncludeCount e | ||
| where RankMetric<CppMetrics::MissingIncludeCount>::getRank(e) <= 50 | ||
| select e.getIncludeText(), e.getValue() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /** | ||
| * @name Successfully included header files | ||
| * @description A count of all succeeded includes, grouped by filename. | ||
| * @kind metric | ||
| * @tags summary telemetry | ||
| * @id cpp/telemetry/succeeded-includes | ||
| */ | ||
|
|
||
| import Metrics | ||
|
|
||
| from CppMetrics::SucceededIncludeCount m | ||
| where RankMetric<CppMetrics::SucceededIncludeCount>::getRank(m) <= 50 | ||
| select m.getIncludeText(), m.getValue() |
10 changes: 10 additions & 0 deletions
10
cpp/ql/test/library-tests/extraction_errors/CompilerErrors.expected
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| | 'this' may only be used inside a nonstatic member function | 1 | | ||
| | There was an error during this compilation | 1 | | ||
| | expected a ')' | 1 | | ||
| | expected a ';' | 1 | | ||
| | expected an expression | 1 | | ||
| | identifier 'no_such_function' is undefined | 1 | | ||
| | identifier 'nsf2' is undefined | 1 | | ||
| | identifier 'so_is_this' is undefined | 1 | | ||
| | identifier 'uint32_t' is undefined | 1 | | ||
| | too few arguments in function call | 1 | |
1 change: 1 addition & 0 deletions
1
cpp/ql/test/library-tests/extraction_errors/CompilerErrors.qlref
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Telemetry/CompilerErrors.ql |
9 changes: 9 additions & 0 deletions
9
cpp/ql/test/library-tests/extraction_errors/DatabaseQuality.expected
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| | Percentage of calls with an explicit target | 50.0 | | ||
| | Percentage of compilations without errors | 50.0 | | ||
| | Percentage of expressions with a known type | 30.0 | | ||
| | Percentage of functions without errors | 75.0 | | ||
| | Percentage of lines of code without errors | 63.1578947368421 | | ||
| | Percentage of non-error expressions | 30.0 | | ||
| | Percentage of source/header files without errors | 66.66666666666667 | | ||
| | Percentage of successfully resolved #include directives | 100.0 | | ||
| | Percentage of variables with a known type | 90.0 | |
1 change: 1 addition & 0 deletions
1
cpp/ql/test/library-tests/extraction_errors/DatabaseQuality.qlref
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Telemetry/DatabaseQuality.ql |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this regex does not what you want in the case of
cannot_open_file_reason, i.e., you get the reason as part of the result string.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I've no real way of testing this as I've never seen the extractor produce this error message. I suppose a
.*at the end of the regex might be reasonable?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the file names always quoted? This is non-obvious from the frontend source code.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I tested this on some real databases. But an integration test is clearly needed then. Qltest cannot test this situation due to the absence of
SEMMLE_CPP_MISSING_INCLUDES_NOT_FATAL.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that having some integration test for this is the only thing that still needs addressing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is: https://github.com/github/semmle-code/pull/51788