C++: Initial telemetry queries and tests

This commit is contained in:
Calum Grant
2024-10-25 18:06:38 +01:00
parent fed240a2b2
commit f6776a4249
32 changed files with 547 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
/**
* @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
select m, m.getValue() as c order by c desc

View File

@@ -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, m.getValue() order by m

View File

@@ -0,0 +1,32 @@
import cpp
/**
* A syntax error.
*/
class SyntaxError extends CompilerError {
SyntaxError() { this.getTag().matches("exp_%") }
}
/**
* A cannot open file error.
* Typically this is due to a missing include.
*/
class CannotOpenFile extends CompilerError {
CannotOpenFile() { this.hasTag("cannot_open_file") }
string getIncludedFile() {
result = this.getMessage().regexpCapture("cannot open source file '([^']+)'", 1)
}
}
/**
* An undefined identifier error.
* Currently unused.
*/
class UndefinedIdentifier extends CompilerError {
UndefinedIdentifier() { this.hasTag("undefined_identifier") }
string getIdentifier() {
result = this.getMessage().regexpCapture("identifier '([^']+)' is undefined", 1)
}
}

View File

@@ -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, m.getValue() order by m

View File

@@ -0,0 +1,260 @@
import cpp
import Diagnostics
/**
* A metric is a string with a value.
*/
abstract class Metric extends string {
bindingset[this]
Metric() { any() }
/** Gets the value of this metric. */
abstract float getValue();
}
/**
* A metric that we want to report in cpp/telemetry/extraction-metrics
*/
abstract class ExtractionMetric extends Metric {
bindingset[this]
ExtractionMetric() { any() }
}
/**
* 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 base_metric;
SuccessMetric relative_metric;
QualityMetric() {
base_metric = relative_metric.getBaseline() and this = "Percentage of " + relative_metric
}
override float getValue() { result = 100 * relative_metric.getValue() / base_metric.getValue() }
}
/** Various metrics we want to report. */
module CppMetrics {
class CompilationUnits extends BaseMetric {
CompilationUnits() { this = "compilation units" }
override float getValue() { result = count(Compilation c) }
}
class SourceFiles extends BaseMetric {
SourceFiles() { this = "source files" }
override float getValue() { result = count(File f | f.fromSource()) }
}
class SourceFilesWithoutErrors extends SuccessMetric {
SourceFilesWithoutErrors() { this = "source files without errors" }
override float getValue() {
result = count(File f | f.fromSource() and not exists(CompilerError e | f = e.getFile()))
}
override SourceFiles getBaseline() { any() }
}
class CompilationUnitsWithoutErrors extends SuccessMetric {
CompilationUnitsWithoutErrors() { this = "compilation units without errors" }
override float getValue() {
result = count(Compilation c | not exists(Diagnostic d | d.getFile() = c.getAFileCompiled()))
}
override CompilationUnits getBaseline() { any() }
}
class Expressions extends BaseMetric {
Expressions() { this = "expressions" }
override float getValue() { result = count(Expr e) }
}
class SucceededExpressions extends SuccessMetric {
SucceededExpressions() { this = "non-error expressions" }
override float getValue() { result = count(Expr e) - count(ErrorExpr e) }
override Expressions getBaseline() { any() }
}
class TypedExpressions extends SuccessMetric {
TypedExpressions() { this = "expressions with a known type" }
override float getValue() { result = count(Expr e | not e.getType() instanceof ErroneousType) }
override Expressions getBaseline() { any() }
}
class Calls extends BaseMetric {
Calls() { this = "calls" }
override float getValue() { result = count(Call c) }
}
class SucceededCalls extends SuccessMetric {
SucceededCalls() { this = "calls with a target" }
override float getValue() {
result = count(Call c | not c.getTarget().getADeclarationEntry().isImplicit())
}
override Calls getBaseline() { any() }
}
class Variables extends BaseMetric {
Variables() { this = "variables" }
override float getValue() { result = count(Variable v) }
}
class VariablesKnownType extends SuccessMetric {
VariablesKnownType() { this = "variables with a known type" }
override float getValue() {
result = count(Variable v | not v.getType() instanceof ErroneousType)
}
override Variables getBaseline() { any() }
}
class LinesOfText extends BaseMetric {
LinesOfText() { this = "lines of text" }
override float getValue() { result = sum(File f | | f.getMetrics().getNumberOfLines()) }
}
class LinesOfCode extends BaseMetric {
LinesOfCode() { this = "lines of code" }
override float 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 float getValue() {
result =
sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) -
count(File file, int line | errorLine(file, line))
}
override LinesOfCode getBaseline() { any() }
}
class Functions extends BaseMetric {
Functions() { this = "functions" }
override float getValue() { result = count(Function f) }
}
class SucceededFunctions extends SuccessMetric {
SucceededFunctions() { this = "functions without errors" }
override float getValue() { result = count(Function f | not f.hasErrors()) }
override Functions getBaseline() { any() }
}
class Includes extends BaseMetric {
Includes() { this = "#include directives" }
override float getValue() { result = count(Include i) + count(CannotOpenFile e) }
}
class SucceededIncludes extends SuccessMetric {
SucceededIncludes() { this = "successfully resolved #include directives" }
override float getValue() { result = count(Include i) }
override Includes getBaseline() { any() }
}
class SucceededIncludeCount extends Metric {
string include_text;
SucceededIncludeCount() {
exists(Include i |
i.getIncludeText() = include_text and
exists(i.getFile().getRelativePath()) // Only report includes from the repo
) and
this = "Successfully included " + include_text
}
override float getValue() { result = count(Include i | i.getIncludeText() = include_text) }
string getIncludeText() { result = include_text }
}
class MissingIncludeCount extends Metric {
string include_text;
MissingIncludeCount() {
exists(CannotOpenFile e | e.getIncludedFile() = include_text) and
this = "Failed to include '" + include_text + "'"
}
override float getValue() {
result = count(CannotOpenFile e | e.getIncludedFile() = include_text)
}
string getIncludeText() { result = include_text }
}
class CompilerErrors extends ExtractionMetric {
CompilerErrors() { this = "compiler errors" }
override float getValue() { result = count(CompilerError e) }
}
class ErrorCount extends Metric {
ErrorCount() { exists(CompilerError e | e.getMessage() = this) }
override float getValue() { result = count(CompilerError e | e.getMessage() = this) }
}
class SyntaxErrorCount extends ExtractionMetric {
SyntaxErrorCount() { this = "syntax errors" }
override float getValue() { result = count(SyntaxError e) }
}
}

View File

@@ -0,0 +1,12 @@
/**
* @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
select e.getIncludeText(), e.getValue() as c order by c desc

View File

@@ -0,0 +1,12 @@
/**
* @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
select m.getIncludeText(), m.getValue()