diff --git a/rust/ql/lib/codeql/rust/internal/Definitions.qll b/rust/ql/lib/codeql/rust/internal/Definitions.qll new file mode 100644 index 000000000000..6b380dae3321 --- /dev/null +++ b/rust/ql/lib/codeql/rust/internal/Definitions.qll @@ -0,0 +1,126 @@ +/** + * Provides classes and predicates related to jump-to-definition links + * in the code viewer. + */ + +private import codeql.rust.elements.Variable +private import codeql.rust.elements.Locatable +private import codeql.rust.elements.FormatArgsExpr +private import codeql.rust.elements.FormatArgsArg +private import codeql.rust.elements.Format +private import codeql.rust.elements.MacroCall +private import codeql.rust.elements.NamedFormatArgument +private import codeql.rust.elements.PositionalFormatArgument +private import codeql.Locations + +/** An element with an associated definition. */ +abstract class Use extends Locatable { + /** Gets the definition associated with this element. */ + abstract Definition getDefinition(); + + /** + * Gets the type of use. + */ + abstract string getUseType(); +} + +cached +private module Cached { + cached + newtype TDef = + TVariable(Variable v) or + TFormatArgsArgName(Name name) { name = any(FormatArgsArg a).getName() } or + TFormatArgsArgIndex(Expr e) { e = any(FormatArgsArg a).getExpr() } + + /** + * Gets an element, of kind `kind`, that element `use` uses, if any. + */ + cached + Definition definitionOf(Use use, string kind) { + result = use.getDefinition() and + kind = use.getUseType() and + not result.getLocation() = any(MacroCall m).getLocation() + } +} + +predicate definitionOf = Cached::definitionOf/2; + +/** A definition */ +class Definition extends Cached::TDef { + /** Gets the location of this variable. */ + Location getLocation() { + result = this.asVariable().getLocation() or + result = this.asName().getLocation() or + result = this.asExpr().getLocation() + } + + /** Gets this definition as a `Variable` */ + Variable asVariable() { this = Cached::TVariable(result) } + + /** Gets this definition as a `Name` */ + Name asName() { this = Cached::TFormatArgsArgName(result) } + + /** Gets this definition as an `Expr` */ + Expr asExpr() { this = Cached::TFormatArgsArgIndex(result) } + + /** Gets the string representation of this element. */ + string toString() { + result = this.asExpr().toString() or + result = this.asVariable().toString() or + result = this.asName().getText() + } +} + +private class LocalVariableUse extends Use instanceof VariableAccess { + private Variable def; + + LocalVariableUse() { this = def.getAnAccess() } + + override Definition getDefinition() { result.asVariable() = def } + + override string getUseType() { result = "local variable" } +} + +private class NamedFormatArgumentUse extends Use instanceof NamedFormatArgument { + private Name def; + + NamedFormatArgumentUse() { + exists(FormatArgsExpr parent | + parent = this.getParent().getParent() and + parent.getAnArg().getName() = def and + this.getName() = def.getText() + ) + } + + override Definition getDefinition() { result.asName() = def } + + override string getUseType() { result = "format argument" } +} + +private class PositionalFormatUse extends Use instanceof Format { + PositionalFormatUse() { not exists(this.getArgumentRef()) } + + override Definition getDefinition() { + exists(FormatArgsExpr parent, int index | parent.getFormat(_) = this | + this = rank[index + 1](PositionalFormatUse f, int i | parent.getFormat(i) = f | f order by i) and + result.asExpr() = parent.getArg(index).getExpr() + ) + } + + override string getUseType() { result = "format argument" } +} + +private class PositionalFormatArgumentUse extends Use instanceof PositionalFormatArgument { + private Expr def; + + PositionalFormatArgumentUse() { + exists(FormatArgsExpr parent | + parent = this.getParent().getParent() and + def = parent.getArg(this.getIndex()).getExpr() + ) + } + + override Definition getDefinition() { result.asExpr() = def } + + override string getUseType() { result = "format argument" } +} diff --git a/rust/ql/lib/ide-contextual-queries/localDefinitions.ql b/rust/ql/lib/ide-contextual-queries/localDefinitions.ql index 5c2cb01f178c..677a876987b0 100644 --- a/rust/ql/lib/ide-contextual-queries/localDefinitions.ql +++ b/rust/ql/lib/ide-contextual-queries/localDefinitions.ql @@ -3,21 +3,17 @@ * @description Generates use-definition pairs that provide the data * for jump-to-definition in the code viewer. * @kind definitions - * @id rus/ide-jump-to-definition + * @id rust/ide-jump-to-definition * @tags ide-contextual-queries/local-definitions */ import codeql.IDEContextual -import codeql.rust.elements.Variable -import codeql.rust.elements.Locatable +import codeql.rust.internal.Definitions external string selectedSourceFile(); -predicate localVariable(Locatable e, Variable def) { e = def.getAnAccess() } - -from Locatable e, Variable def, string kind +from Use use, Definition def, string kind where - e.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) and - localVariable(e, def) and - kind = "local variable" -select e, def, kind + def = definitionOf(use, kind) and + use.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +select use, def, kind diff --git a/rust/ql/lib/ide-contextual-queries/localReferences.ql b/rust/ql/lib/ide-contextual-queries/localReferences.ql new file mode 100644 index 000000000000..87e4cc5ca846 --- /dev/null +++ b/rust/ql/lib/ide-contextual-queries/localReferences.ql @@ -0,0 +1,19 @@ +/** + * @name Find-references links + * @description Generates use-definition pairs that provide the data + * for find-references in the code viewer. + * @kind definitions + * @id rust/ide-find-references + * @tags ide-contextual-queries/local-references + */ + +import codeql.IDEContextual +import codeql.rust.internal.Definitions + +external string selectedSourceFile(); + +from Use use, Definition def, string kind +where + def = definitionOf(use, kind) and + def.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +select use, def, kind diff --git a/rust/ql/test/library-tests/definitions/Definitions.expected b/rust/ql/test/library-tests/definitions/Definitions.expected new file mode 100644 index 000000000000..11f50118d22b --- /dev/null +++ b/rust/ql/test/library-tests/definitions/Definitions.expected @@ -0,0 +1,19 @@ +| main.rs:2:9:2:13 | width | main.rs:5:29:5:33 | width | local variable | +| main.rs:2:9:2:13 | width | main.rs:6:41:6:45 | width | local variable | +| main.rs:2:9:2:13 | width | main.rs:7:36:7:40 | width | local variable | +| main.rs:3:9:3:17 | precision | main.rs:5:36:5:44 | precision | local variable | +| main.rs:3:9:3:17 | precision | main.rs:6:48:6:56 | precision | local variable | +| main.rs:4:9:4:13 | value | main.rs:6:34:6:38 | value | local variable | +| main.rs:4:9:4:13 | value | main.rs:7:29:7:33 | value | local variable | +| main.rs:5:50:5:54 | value | main.rs:5:22:5:26 | value | format argument | +| main.rs:6:34:6:38 | value | main.rs:6:22:6:22 | 0 | format argument | +| main.rs:6:41:6:45 | width | main.rs:6:25:6:25 | 1 | format argument | +| main.rs:6:48:6:56 | precision | main.rs:6:28:6:28 | 2 | format argument | +| main.rs:7:29:7:33 | value | main.rs:7:21:7:22 | {} | format argument | +| main.rs:7:36:7:40 | width | main.rs:7:24:7:25 | {} | format argument | +| main.rs:8:9:8:14 | people | main.rs:9:22:9:27 | people | local variable | +| main.rs:10:31:10:31 | 1 | main.rs:10:19:10:20 | {} | format argument | +| main.rs:10:31:10:31 | 1 | main.rs:10:23:10:23 | 0 | format argument | +| main.rs:10:34:10:34 | 2 | main.rs:10:16:10:16 | 1 | format argument | +| main.rs:10:34:10:34 | 2 | main.rs:10:26:10:27 | {} | format argument | +| main.rs:11:40:11:42 | "x" | main.rs:11:31:11:35 | {:<5} | format argument | diff --git a/rust/ql/test/library-tests/definitions/Definitions.ql b/rust/ql/test/library-tests/definitions/Definitions.ql new file mode 100644 index 000000000000..021d5f07e4a3 --- /dev/null +++ b/rust/ql/test/library-tests/definitions/Definitions.ql @@ -0,0 +1,5 @@ +import codeql.rust.internal.Definitions + +from Definition def, Use use, string kind +where def = definitionOf(use, kind) +select def, use, kind diff --git a/rust/ql/test/library-tests/definitions/main.rs b/rust/ql/test/library-tests/definitions/main.rs new file mode 100644 index 000000000000..a0ee62882807 --- /dev/null +++ b/rust/ql/test/library-tests/definitions/main.rs @@ -0,0 +1,12 @@ +fn main() { + let width = 4; + let precision = 2; + let value = 10; + println!("Value {value:#width$.precision$}", value = 10.5); + println!("Value {0:#1$.2$}", value, width, precision); + println!("Value {} {}", value, width); + let people = "Rustaceans"; + println!("Hello {people}!"); + println!("{1} {} {0} {}", 1, 2); + assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); +}