| agent | agent |
|---|
Use the MCP server tools to identify classes, modules, and predicates defined in a
.ql or .qll file and check for possible "duplicated code," where duplicated code
is defined to be:
- Reimplementing functionality that already exists in the standard library, or shared project
.qllfiles, and - The local definition is identical, or semantically equivalent, or superior to the library definition, or
- The local definition could be simplified by reusing the existing definition (e.g. a base class already exists that captures some of the shared logic)
Here are some examples:
import cpp
// Duplicated: `StandardNamespace` already exists in the standard library and is identical
class NamespaceStd extends Namespace {
NamespaceStd() { this.getName() = "std" }
}
// Duplicated: class should extend `Operator`, not `Function`
class ThrowingOperator extends Function {
ThrowingOperator() {
// Duplicated: this check is implied by using base class `Operator`
this.getName().matches("%operator%") and
and exists(ThrowExpr te |
// Duplicated: this is equivalent to `te.getEnclosingFunction() = this`
te.getParent*() = this.getAChild()
)
}
// Duplicated: member predicate `getDeclaringType()` already does this.
Class getDefiningClass() { ... }
}
// Duplicated: `ControlFlowNode.getASuccessor()` already exists in `cpp` and is superior
predicate getASuccessor(Stmt a, Stmt b) {
exists(Block b, int i | a = b.getChild(i) and b = b.getChild(i + 1))
}
// Duplicated: prefer to import `semmle.code.cpp.controlflow.Dominance`, defined in dependency pack `cpp-all`
predicate dominates(Block a, Block b) { ... }Duplicate code removal isn't done arbitrarily, but for several key reasons:
- Maintainability: Duplicated code must be maintained separately, may diverge and have different bugs
- Simplicity: Relying on existing definitions reduces the amount of code to read and understand
- Readability: Existing definitions map wrap complex ideas into readable names
- Consistency: A single source of truth makes for a more consistent user experience across queries
- Completeness/Correctness: Recreating an already-existing definition can miss edge cases, resulting in false positives or false negatives
- A query file defines a class or predicate whose name sounds generic (e.g.
StandardNamespace,Callable,SecurityFeature) - Refactoring a query that was written before a relevant library predicate existed
- Reviewing a shared
.qllfile to check whether its helpers have been upstreamed into the standard library in a newer CodeQL version - Performing a code-quality audit across a suite of custom queries
- The file path of the
.qlor.qllfile to audit for code duplication - Understand which packs are imported by
qlpack.yml - Understand where the relevant language library packs are located (e.g.
~/.codeql) - Understand where project-specific library packs are located (e.g.
$LANGUAGE/libor$LANGUAGE/common) - Understand where pack-specific shared
.qllfiles are located (e.g.$PACKROOT/common/*.qll)
The core idea is to enumerate every top-level name defined in the file under review.
Then, find candidate .qll files, based on file name and path, that are available to
that .ql file in review. Then, enumerate the top-level names in each candidate
.qll file, to find potential duplicates, dive further if necessary, and then report
the findings as code improvement recommendations to the user.
- Read the file to see its imports and top-level structure
- Enumerate top-level definitions with
codeql_lsp_document_symbols - Find available .qll files in the
.qlfile's pack, and its dependencies, including the standard library - Identify promising .qll file candidates based on their file name and path
- Enumerate top-level definitions in candidate
.qllfiles withcodeql_lsp_document_symbols - Detect overlap, comparing definitions if unclear (e.g. by using
find_predicate_positionandfind_class_positiontools) - Report findings as a set of recommendations to the user about which definitions could be improved, by reusing which existing definitions
Tool: read_file
Parameters:
file_path: /path/to/query.ql
start_line: 1
end_line: 60 # enough to see all imports
Record every import statement. These are the namespaces the standard library
exposes; duplication is only meaningful for libraries that are already imported (or
easily importable).
Use codeql_lsp_document_symbols to retrieve every class, predicate, and module
defined at the top level of the file in a single call:
Tool: codeql_lsp_document_symbols
Parameters:
file_path: /path/to/query.ql
names_only: true # provides significantly smaller response payload
workspace_uri: /path/to/pack-root # directory containing codeql-pack.yml
The response contains a symbols array. Each entry has:
name— the identifier as written in sourcekind— numeric SymbolKind (5 = Class, 12 = Function/predicate, 2 = Module, etc.)range— the full definition range (0-based lines)selectionRange— the range of just the name tokenchildren— nested members (for classes and modules)
Top-level symbols are the root nodes of the array; children hold member
predicates and fields.
Run the tool codeql_resolve_library-path with the given ql query file to find where
the available library sources live.
For each source root, run find $ROOT -name "*.qll" to find all .qll files
available in that pack. Do not preemptively filter this list of qll files. The names
of the files may be broad or nondescriptive. Read all file names for each project to
understand its structure and responsibilities before proceeding to step 3d.
Choose promising candidate .qll files you found in the previous step. Pick
candidates that may potentially define behavior relevant to the current query and its
enumerated definitions, based on the candidate filename, path, and priority.
Prioritize as follows:
.qllfiles in the same directory as the query file have the absolute highest priority.qllfiles in the same pack have the next extremely high priority.qllfiles in project-specific library packs have the very high priority.qllfiles in downloaded direct dependencies have standard priority.qllfiles in transitive dependencies have the least priority.
Enumerate the top-level definitions for each candidate .qll file using the tool
codeql_lsp_document_symbols again. Some top levels may clearly match the name or
purpose of a definition in the query file, while others may only appear as possibly
related.
Tool: codeql_lsp_document_symbols
Parameters:
file_path: /path/to/library/file.qll
workspace_uri: /path/to/pack-root
For each promising candidate, identify the predicate or class definitions that may overlap. One definition will be in the query file (.ql) and the other will be in the library file (.qll).
Using the tools find_predicate_position and find_class_position, you can retrieve the full definition of each predicate or class, and compare them to determine whether they are identical, equivalent, overlapping, or if one is a superior implementation that could be reused by the query file.
Dutifully analyze whether the shared library file definition would reduce code duplication in the categories identified before: maintenance, simplicity, readability, consistency, and completeness/correctness. Consider contextual factors such as comments explaining why the local definition differs from the library one, or whether the local definition is a thin wrapper around the library definition that adds value (e.g. by improving naming or adding extra checks).
Do not go on a wild goose chase trying to find every possible overlap. Consider the likelihood of overlap based on the broadness of functionality, and the value that would be brought be reuse. Do not waste significant time on unimportant or unlikely overlaps.
For each duplicate found, report:
| Local name | Local file | import path | Notes |
|---|---|---|---|
StandardNamespace |
query.ql:42 |
already imported in import cpp |
Identical |
myHelper |
query.ql:80 |
import myproject.Helpers |
Equivalent |
myHelper |
query.ql:80 |
import myproject.Helpers |
Equivalent |
Recommend one of:
- Replace: remove the local definition and use the standard definition directly instead
- Integrate: refactor and simplify the local definition by making use of the standard definition
- Annotate: add comments to the local definition to explain how it differs from the standard definition and why the duplication is necessary
Additionally, report if any issues came up in using the tools, or finding the qll files.
For each concept for which no duplicate was found, provide at most a brief description of what the concept is. Do not provide a long detailed explanation of a non-finding.
Do not perform any updates to any code during this analysis.
As you work through completing this task, ask yourself:
- Have I changed any code, even though that was not my task, or am I about to? Stop, do not change any code!
- Did I sufficiently analyze the definitions such that I likely found most overlapping definitions?
- Will my suggestions improve the maintainability, simplicity, readability, consistency, or completeness/correctness of the codebase?
- Did I report my findings clearly?
- Did I use the suggested LLM tools to their fullest extent?
- Did I follow the steps in the recommended order, and not skip any steps?
- Did I report any issues I had in finding the relevant
.qllfiles, or using the tools to analyze definitions?