Skip to content

Rust: Type inference for pattern matching #20020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 11, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Rust: Handle (Enum::)Variant::<TypeArg> type mentions
  • Loading branch information
hvitved committed Jul 11, 2025
commit edf6c7fbd66acaedfa49d4d648b9622fdd7f66ad
23 changes: 4 additions & 19 deletions rust/ql/lib/codeql/rust/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,7 @@ private module StructExprMatchingInput implements MatchingInputSig {
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
// `Foo<Bar>::Variant { ... }`.
apos.isStructPos() and
exists(Path p, TypeMention tm |
p = this.getPath() and
if resolvePath(p) instanceof Variant then tm = p.getQualifier() else tm = p
|
result = tm.resolveTypeAt(path)
)
result = this.getPath().(TypeMention).resolveTypeAt(path)
}

Declaration getTarget() { result = resolvePath(this.getPath()) }
Expand Down Expand Up @@ -1242,12 +1237,7 @@ private module StructPatMatchingInput implements MatchingInputSig {
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
// `let Foo<Bar>::Variant { ... } = ...`.
apos.isStructPos() and
exists(Path p, TypeMention tm |
p = this.getPath() and
if resolvePath(p) instanceof Variant then tm = p.getQualifier() else tm = p
|
result = tm.resolveTypeAt(path)
)
result = this.getPath().(TypeMention).resolveTypeAt(path)
}

Declaration getTarget() { result = resolvePath(this.getPath()) }
Expand Down Expand Up @@ -1297,14 +1287,9 @@ private module TupleStructPatMatchingInput implements MatchingInputSig {
result = inferType(this.getNodeAt(apos), path)
or
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
// `let Option::<Foo>(x) = ...`.
// `let Option::<Foo>::Some(x) = ...`.
apos.isSelf() and
exists(Path p, TypeMention tm |
p = this.getPath() and
if resolvePath(p) instanceof Variant then tm = p.getQualifier() else tm = p
|
result = tm.resolveTypeAt(path)
)
result = this.getPath().(TypeMention).resolveTypeAt(path)
}

Declaration getTarget() { result = resolvePath(this.getPath()) }
Expand Down
12 changes: 10 additions & 2 deletions rust/ql/lib/codeql/rust/internal/TypeMention.qll
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@ class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
class PathTypeMention extends TypeMention, Path {
TypeItemNode resolved;

PathTypeMention() { resolved = resolvePath(this) }
PathTypeMention() {
resolved = resolvePath(this)
or
resolved = resolvePath(this).(Variant).getEnum()
}

ItemNode getResolved() { result = resolved }
TypeItemNode getResolved() { result = resolved }

pragma[nomagic]
private TypeAlias getResolvedTraitAlias(string name) {
Expand Down Expand Up @@ -99,6 +103,10 @@ class PathTypeMention extends TypeMention, Path {
this = node.getASelfPath() and
result = node.(ImplItemNode).getSelfPath().getSegment().getGenericArgList().getTypeArg(i)
)
or
// `Option::<i32>::Some` is valid in addition to `Option::Some::<i32>`
resolvePath(this) instanceof Variant and
result = this.getQualifier().getSegment().getGenericArgList().getTypeArg(i)
}

private TypeMention getPositionalTypeArgument(int i) {
Expand Down
8 changes: 4 additions & 4 deletions rust/ql/test/library-tests/type-inference/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2462,18 +2462,18 @@ pub mod pattern_matching {
_ => (),
}

let opt1 = Some(Default::default()); // $ MISSING: type=opt1:T.i32 method=default
let opt1 = Some(Default::default()); // $ type=opt1:T.i32 method=default
#[rustfmt::skip]
let _ = if let Some::<i32>(x) = opt1
{
x; // $ MISSING: type=x:i32
x; // $ type=x:i32
};

let opt2 = Some(Default::default()); // $ MISSING: type=opt2:T.i32 method=default
let opt2 = Some(Default::default()); // $ type=opt2:T.i32 method=default
#[rustfmt::skip]
let _ = if let Option::Some::<i32>(x) = opt2
{
x; // $ MISSING: type=x:i32
x; // $ type=x:i32
};

let opt3 = Some(Default::default()); // $ type=opt3:T.i32 method=default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4303,13 +4303,27 @@ inferType
| main.rs:2462:13:2462:13 | _ | T1.T2.&T | {EXTERNAL LOCATION} | str |
| main.rs:2462:13:2462:13 | _ | T2 | {EXTERNAL LOCATION} | bool |
| main.rs:2465:13:2465:16 | opt1 | | {EXTERNAL LOCATION} | Option |
| main.rs:2465:13:2465:16 | opt1 | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2465:20:2465:43 | Some(...) | | {EXTERNAL LOCATION} | Option |
| main.rs:2465:20:2465:43 | Some(...) | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2465:25:2465:42 | ...::default(...) | | {EXTERNAL LOCATION} | i32 |
| main.rs:2467:24:2467:37 | Some::<...>(...) | | {EXTERNAL LOCATION} | Option |
| main.rs:2467:24:2467:37 | Some::<...>(...) | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2467:36:2467:36 | x | | {EXTERNAL LOCATION} | i32 |
| main.rs:2467:41:2467:44 | opt1 | | {EXTERNAL LOCATION} | Option |
| main.rs:2467:41:2467:44 | opt1 | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2469:13:2469:13 | x | | {EXTERNAL LOCATION} | i32 |
| main.rs:2472:13:2472:16 | opt2 | | {EXTERNAL LOCATION} | Option |
| main.rs:2472:13:2472:16 | opt2 | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2472:20:2472:43 | Some(...) | | {EXTERNAL LOCATION} | Option |
| main.rs:2472:20:2472:43 | Some(...) | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2472:25:2472:42 | ...::default(...) | | {EXTERNAL LOCATION} | i32 |
| main.rs:2474:24:2474:45 | ...::Some::<...>(...) | | {EXTERNAL LOCATION} | Option |
| main.rs:2474:24:2474:45 | ...::Some::<...>(...) | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2474:44:2474:44 | x | | {EXTERNAL LOCATION} | i32 |
| main.rs:2474:49:2474:52 | opt2 | | {EXTERNAL LOCATION} | Option |
| main.rs:2474:49:2474:52 | opt2 | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2476:13:2476:13 | x | | {EXTERNAL LOCATION} | i32 |
| main.rs:2479:13:2479:16 | opt3 | | {EXTERNAL LOCATION} | Option |
| main.rs:2479:13:2479:16 | opt3 | T | {EXTERNAL LOCATION} | i32 |
| main.rs:2479:20:2479:43 | Some(...) | | {EXTERNAL LOCATION} | Option |
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy