From 564a3bd4440b01e271d07ce513309c4dd0100833 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 10 Feb 2026 15:30:56 +0100 Subject: [PATCH 1/2] Rust: Simplify `inferMethodCallTypeSelf` --- .../internal/typeinference/TypeInference.qll | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index e7e4bbb717cb..4ed15a2683ef 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -2604,27 +2604,30 @@ private Type inferMethodCallTypeNonSelf(AstNode n, boolean isReturn, TypePath pa } /** - * Gets the type of `n` at `path` after applying `derefChain` and `borrow`, - * where `n` is the `self` argument of a method call. + * Gets the type of `n` at `path` after applying `derefChain`, where `n` is the + * `self` argument of a method call. * * The predicate recursively pops the head of `derefChain` until it becomes * empty, at which point the inferred type can be applied back to `n`. */ pragma[nomagic] -private Type inferMethodCallTypeSelf( - AstNode n, DerefChain derefChain, BorrowKind borrow, TypePath path -) { - exists(MethodCallMatchingInput::AccessPosition apos, string derefChainBorrow | - result = inferMethodCallType0(_, apos, n, derefChainBorrow, path) and +private Type inferMethodCallTypeSelf(AstNode n, DerefChain derefChain, TypePath path) { + exists( + MethodCallMatchingInput::AccessPosition apos, string derefChainBorrow, BorrowKind borrow, + TypePath path0 + | + result = inferMethodCallType0(_, apos, n, derefChainBorrow, path0) and apos.isSelf() and MethodCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) - ) - or - // adjust for implicit borrow - exists(TypePath path0, BorrowKind borrow0 | - result = inferMethodCallTypeSelf(n, derefChain, borrow0, path0) and - path0.isCons(borrow0.getRefType().getPositionalTypeParameter(0), path) and - borrow.isNoBorrow() + | + borrow.isNoBorrow() and + path = path0 + or + // adjust for implicit borrow + exists(TypePath prefix | + prefix = TypePath::singleton(borrow.getRefType().getPositionalTypeParameter(0)) and + path0 = prefix.appendInverse(path) + ) ) or // adjust for implicit deref @@ -2632,9 +2635,8 @@ private Type inferMethodCallTypeSelf( DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType, TypePath selfPath | - t0 = inferMethodCallTypeSelf(n, derefChain0, borrow, path0) and + t0 = inferMethodCallTypeSelf(n, derefChain0, path0) and derefChain0.isCons(impl, derefChain) and - borrow.isNoBorrow() and selfParamType = impl.resolveSelfTypeAt(selfPath) | result = selfParamType and @@ -2653,7 +2655,7 @@ private Type inferMethodCallTypeSelf( private Type inferMethodCallTypePreCheck(AstNode n, boolean isReturn, TypePath path) { result = inferMethodCallTypeNonSelf(n, isReturn, path) or - result = inferMethodCallTypeSelf(n, DerefChain::nil(), TNoBorrowKind(), path) and + result = inferMethodCallTypeSelf(n, DerefChain::nil(), path) and isReturn = false } From 49f24ca8ecff1c81b0a90b11e75fe2cc3d3526f2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 10 Feb 2026 15:33:03 +0100 Subject: [PATCH 2/2] Rust: Avoid using `regexpCapture` with multiple capture groups --- .../internal/typeinference/TypeInference.qll | 8 +++---- shared/util/codeql/util/UnboundList.qll | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 4ed15a2683ef..9a8f99a49b6e 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -2477,10 +2477,10 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi additional predicate decodeDerefChainBorrow( string derefChainBorrow, DerefChain derefChain, BorrowKind borrow ) { - exists(string regexp | - regexp = "^(.*);(.*)$" and - derefChain = derefChainBorrow.regexpCapture(regexp, 1) and - borrow.toString() = derefChainBorrow.regexpCapture(regexp, 2) + exists(int i | + i = derefChainBorrow.indexOf(";") and + derefChain = derefChainBorrow.prefix(i) and + borrow.toString() = derefChainBorrow.suffix(i + 1) ) } diff --git a/shared/util/codeql/util/UnboundList.qll b/shared/util/codeql/util/UnboundList.qll index 5cfe06d02ee3..79fac6506d6a 100644 --- a/shared/util/codeql/util/UnboundList.qll +++ b/shared/util/codeql/util/UnboundList.qll @@ -78,6 +78,9 @@ module Make Input> { /** Holds if this list is empty. */ predicate isEmpty() { this = "" } + bindingset[this] + private int stringLength() { result = super.length() } + /** Gets the length of this list. */ bindingset[this] pragma[inline_late] @@ -115,19 +118,23 @@ module Make Input> { /** Holds if this list starts with `e`, followed by `suffix`. */ bindingset[this] predicate isCons(Element e, UnboundList suffix) { - exists(string regexp | regexp = "^([0-9]+)\\.(.*)$" | - e = decode(this.regexpCapture(regexp, 1)) and - suffix = this.regexpCapture(regexp, 2) + exists(string elem | + // it is more efficient to not create a capture group for the suffix, since + // `regexpCapture` will then always join in both groups, only to afterwards filter + // based on the requested group (the group number is not part of the binding set + // of `regexpCapture`) + elem = this.regexpCapture("^([0-9]+)\\..*$", 1) and + e = decode(elem) and + suffix = this.suffix(elem.length() + 1) ) } /** Holds if this list starts with `prefix`, followed by `e`. */ bindingset[this] predicate isSnoc(UnboundList prefix, Element e) { - exists(string regexp | regexp = "^(|.+\\.)([0-9]+)\\.$" | - prefix = this.regexpCapture(regexp, 1) and - e = decode(this.regexpCapture(regexp, 2)) - ) + // same remark as above about not using multiple capture groups + prefix = this.regexpCapture("^(|.+\\.)[0-9]+\\.$", 1) and + e = decode(this.substring(prefix.stringLength(), this.stringLength() - 1)) } /** Gets the head of this list, if any. */