diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 91af72fce4e..4376df7caf8 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -846,9 +846,11 @@ module RustDataFlow implements InputSig { /** Provides logic related to captured variables. */ module VariableCapture { + private import codeql.rust.internal.CachedStages private import codeql.dataflow.VariableCapture as SharedVariableCapture private predicate closureFlowStep(ExprCfgNode e1, ExprCfgNode e2) { + Stages::DataFlowStage::ref() and e1 = getALastEvalNode(e2) or exists(Ssa::Definition def | diff --git a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll index c33b1f3dd6e..dea172a7266 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll @@ -33,6 +33,7 @@ module Impl { * Gets the nearest enclosing parent of this node, which is also an `AstNode`, * if any. */ + cached AstNode getParentNode() { result = getParentOfAstStep*(getImmediateParent(this)) } /** Gets the immediately enclosing callable of this node, if any. */ diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll index 88a1b815f6b..9bb2029cd44 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll @@ -68,36 +68,36 @@ module Impl { * where `definingNode` is the entire `Either::Left(x) | Either::Right(x)` * pattern. */ + cached private predicate variableDecl(AstNode definingNode, Name name, string text) { - ( - exists(SelfParam sp | - name = sp.getName() and - definingNode = name and - text = name.getText() and - // exclude self parameters from functions without a body as these are - // trait method declarations without implementations - not exists(Function f | not f.hasBody() and f.getParamList().getSelfParam() = sp) - ) - or - exists(IdentPat pat | - name = pat.getName() and - ( - definingNode = getOutermostEnclosingOrPat(pat) - or - not exists(getOutermostEnclosingOrPat(pat)) and definingNode = name - ) and - text = name.getText() and - // exclude for now anything starting with an uppercase character, which may be a reference to - // an enum constant (e.g. `None`). This excludes static and constant variables (UPPERCASE), - // which we don't appear to recognize yet anyway. This also assumes programmers follow the - // naming guidelines, which they generally do, but they're not enforced. - not text.charAt(0).isUppercase() and - // exclude parameters from functions without a body as these are trait method declarations - // without implementations - not exists(Function f | not f.hasBody() and f.getParamList().getAParam().getPat() = pat) and - // exclude parameters from function pointer types (e.g. `x` in `fn(x: i32) -> i32`) - not exists(FnPtrTypeRepr fp | fp.getParamList().getParam(_).getPat() = pat) - ) + Cached::ref() and + exists(SelfParam sp | + name = sp.getName() and + definingNode = name and + text = name.getText() and + // exclude self parameters from functions without a body as these are + // trait method declarations without implementations + not exists(Function f | not f.hasBody() and f.getParamList().getSelfParam() = sp) + ) + or + exists(IdentPat pat | + name = pat.getName() and + ( + definingNode = getOutermostEnclosingOrPat(pat) + or + not exists(getOutermostEnclosingOrPat(pat)) and definingNode = name + ) and + text = name.getText() and + // exclude for now anything starting with an uppercase character, which may be a reference to + // an enum constant (e.g. `None`). This excludes static and constant variables (UPPERCASE), + // which we don't appear to recognize yet anyway. This also assumes programmers follow the + // naming guidelines, which they generally do, but they're not enforced. + not text.charAt(0).isUppercase() and + // exclude parameters from functions without a body as these are trait method declarations + // without implementations + not exists(Function f | not f.hasBody() and f.getParamList().getAParam().getPat() = pat) and + // exclude parameters from function pointer types (e.g. `x` in `fn(x: i32) -> i32`) + not exists(FnPtrTypeRepr fp | fp.getParamList().getParam(_).getPat() = pat) ) } @@ -156,8 +156,12 @@ module Impl { predicate isCaptured() { this.getAnAccess().isCapture() } /** Gets the parameter that introduces this variable, if any. */ + cached ParamBase getParameter() { - result = this.getSelfParam() or result.(Param).getPat() = getAVariablePatAncestor(this) + Cached::ref() and + result = this.getSelfParam() + or + result.(Param).getPat() = getAVariablePatAncestor(this) } /** Hold is this variable is mutable. */ @@ -614,12 +618,18 @@ module Impl { /** A variable write. */ class VariableWriteAccess extends VariableAccess { - VariableWriteAccess() { assignmentExprDescendant(this) } + cached + VariableWriteAccess() { + Cached::ref() and + assignmentExprDescendant(this) + } } /** A variable read. */ class VariableReadAccess extends VariableAccess { + cached VariableReadAccess() { + Cached::ref() and not this instanceof VariableWriteAccess and not this = any(RefExpr re).getExpr() and not this = any(CompoundAssignmentExpr cae).getLhs() @@ -638,6 +648,22 @@ module Impl { cached private module Cached { + cached + predicate ref() { 1 = 1 } + + cached + predicate backref() { + 1 = 1 + or + variableDecl(_, _, _) + or + exists(VariableReadAccess a) + or + exists(VariableWriteAccess a) + or + exists(any(Variable v).getParameter()) + } + cached newtype TVariable = MkVariable(AstNode definingNode, string name) { variableDecl(definingNode, _, name) } diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index 41e81919569..4041b2731f9 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -123,6 +123,10 @@ module Stages { exists(any(ItemNode i).getASuccessor(_)) or exists(any(ItemNode i).getASuccessorRec(_)) + or + exists(any(ImplOrTraitItemNode i).getASelfPath()) + or + any(TypeParamItemNode i).hasTraitBound() } } diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 9adf6662d8c..31fbf7fe4cf 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -384,7 +384,9 @@ abstract class ImplOrTraitItemNode extends ItemNode { } /** Gets a `Self` path that refers to this item. */ + cached Path getASelfPath() { + Stages::PathResolutionStage::ref() and isUnqualifiedSelfPath(result) and this = unqualifiedPathLookup(result, _) } @@ -578,7 +580,7 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr { override TypeParam getTypeParam(int i) { none() } } -private class TypeParamItemNode extends ItemNode instanceof TypeParam { +class TypeParamItemNode extends ItemNode instanceof TypeParam { pragma[nomagic] Path getABoundPath() { result = super.getTypeBoundList().getABound().getTypeRepr().(PathTypeRepr).getPath() @@ -598,8 +600,9 @@ private class TypeParamItemNode extends ItemNode instanceof TypeParam { * impl Foo where T: Trait { ... } // has trait bound * ``` */ - pragma[nomagic] + cached predicate hasTraitBound() { + Stages::PathResolutionStage::ref() and exists(this.getABoundPath()) or exists(ItemNode declaringItem, WherePred wp |