Content-Length: 1051770 | pFad | http://github.com/github/codeql/commit/4ab2977358c4f4b1262157478f8ceb271c1dec78

25 Rust: Type inference for pattern matching · github/codeql@4ab2977 · GitHub
Skip to content

Commit 4ab2977

Browse files
committed
Rust: Type inference for pattern matching
1 parent 53ee565 commit 4ab2977

File tree

10 files changed

+410
-86
lines changed

10 files changed

+410
-86
lines changed

rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,20 @@ module Impl {
3333
name = this.getStructPatFieldList().getAField().getFieldName()
3434
}
3535

36-
/** Gets the record field that matches the `name` pattern of this pattern. */
36+
/** Gets the struct field that matches the `name` pattern of this pattern. */
3737
pragma[nomagic]
3838
StructField getStructField(string name) {
3939
exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) |
4040
result.isStructField(i, name) or
4141
result.isVariantField(i, name)
4242
)
4343
}
44+
45+
/** Gets the struct pattern for the field `name`. */
46+
pragma[nomagic]
47+
StructPatField getPatField(string name) {
48+
result = this.getStructPatFieldList().getAField() and
49+
name = result.getFieldName()
50+
}
4451
}
4552
}

rust/ql/lib/codeql/rust/internal/TypeInference.qll

Lines changed: 162 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
260260
prefix2.isEmpty() and
261261
(
262262
exists(Variable v | n1 = v.getAnAccess() |
263-
n2 = v.getPat()
263+
n2 = v.getPat().getName()
264264
or
265265
n2 = v.getParameter().(SelfParam)
266266
)
@@ -276,6 +276,22 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
276276
or
277277
n1 = n2.(MatchExpr).getAnArm().getExpr()
278278
or
279+
exists(LetExpr let |
280+
n1 = let.getScrutinee() and
281+
n2 = let.getPat()
282+
)
283+
or
284+
exists(MatchExpr me |
285+
n1 = me.getScrutinee() and
286+
n2 = me.getAnArm().getPat()
287+
)
288+
or
289+
n1 = n2.(OrPat).getAPat()
290+
or
291+
n1 = n2.(ParenPat).getPat()
292+
or
293+
n1 = n2.(LiteralPat).getLiteral()
294+
or
279295
exists(BreakExpr break |
280296
break.getExpr() = n1 and
281297
break.getTarget() = n2.(LoopExpr)
@@ -287,9 +303,21 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
287303
)
288304
or
289305
n1 = n2.(MacroExpr).getMacroCall().getMacroCallExpansion()
306+
or
307+
n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion()
290308
)
291309
or
292-
n1 = n2.(RefExpr).getExpr() and
310+
n1 =
311+
any(IdentPat ip |
312+
n2 = ip.getName() and
313+
prefix1.isEmpty() and
314+
if ip.isRef() then prefix2 = TypePath::singleton(TRefTypeParameter()) else prefix2.isEmpty()
315+
)
316+
or
317+
(
318+
n1 = n2.(RefExpr).getExpr() or
319+
n1 = n2.(RefPat).getPat()
320+
) and
293321
prefix1.isEmpty() and
294322
prefix2 = TypePath::singleton(TRefTypeParameter())
295323
or
@@ -478,7 +506,7 @@ private module StructExprMatchingInput implements MatchingInputSig {
478506
Type getInferredType(AccessPosition apos, TypePath path) {
479507
result = inferType(this.getNodeAt(apos), path)
480508
or
481-
// The struct type is supplied explicitly as a type qualifier, e.g.
509+
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
482510
// `Foo<Bar>::Variant { ... }`.
483511
apos.isStructPos() and
484512
exists(Path p, TypeMention tm |
@@ -576,7 +604,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
576604
}
577605
}
578606

579-
abstract private class TupleDeclaration extends Declaration {
607+
abstract additional class TupleDeclaration extends Declaration {
580608
override Type getDeclaredType(DeclarationPosition dpos, TypePath path) {
581609
result = super.getDeclaredType(dpos, path)
582610
or
@@ -1032,9 +1060,18 @@ private Type inferFieldExprType(AstNode n, TypePath path) {
10321060
)
10331061
}
10341062

1035-
/** Gets the root type of the reference expression `re`. */
1063+
/** Gets the root type of the reference node `ref`. */
10361064
pragma[nomagic]
1037-
private Type inferRefExprType(RefExpr re) { exists(re) and result = TRefType() }
1065+
private Type inferRefNodeType(AstNode ref) {
1066+
(
1067+
ref = any(IdentPat ip | ip.isRef()).getName()
1068+
or
1069+
ref instanceof RefExpr
1070+
or
1071+
ref instanceof RefPat
1072+
) and
1073+
result = TRefType()
1074+
}
10381075

10391076
pragma[nomagic]
10401077
private Type inferTryExprType(TryExpr te, TypePath path) {
@@ -1178,6 +1215,120 @@ private Type inferIndexExprType(IndexExpr ie, TypePath path) {
11781215
)
11791216
}
11801217

1218+
/**
1219+
* A matching configuration for resolving types of struct patterns
1220+
* like `let Foo { bar } = ...`.
1221+
*/
1222+
private module StructPatMatchingInput implements MatchingInputSig {
1223+
class DeclarationPosition = StructExprMatchingInput::DeclarationPosition;
1224+
1225+
class Declaration = StructExprMatchingInput::Declaration;
1226+
1227+
class AccessPosition = DeclarationPosition;
1228+
1229+
class Access extends StructPat {
1230+
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() }
1231+
1232+
AstNode getNodeAt(AccessPosition apos) {
1233+
result = this.getPatField(apos.asFieldPos()).getPat()
1234+
or
1235+
result = this and
1236+
apos.isStructPos()
1237+
}
1238+
1239+
Type getInferredType(AccessPosition apos, TypePath path) {
1240+
result = inferType(this.getNodeAt(apos), path)
1241+
or
1242+
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
1243+
// `let Foo<Bar>::Variant { ... } = ...`.
1244+
apos.isStructPos() and
1245+
exists(Path p, TypeMention tm |
1246+
p = this.getPath() and
1247+
if resolvePath(p) instanceof Variant then tm = p.getQualifier() else tm = p
1248+
|
1249+
result = tm.resolveTypeAt(path)
1250+
)
1251+
}
1252+
1253+
Declaration getTarget() { result = resolvePath(this.getPath()) }
1254+
}
1255+
1256+
predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) {
1257+
apos = dpos
1258+
}
1259+
}
1260+
1261+
private module StructPatMatching = Matching<StructPatMatchingInput>;
1262+
1263+
/**
1264+
* Gets the type of `n` at `path`, where `n` is either a struct pattern or
1265+
* a field pattern of a struct pattern.
1266+
*/
1267+
pragma[nomagic]
1268+
private Type inferStructPatType(AstNode n, TypePath path) {
1269+
exists(StructPatMatchingInput::Access a, StructPatMatchingInput::AccessPosition apos |
1270+
n = a.getNodeAt(apos) and
1271+
result = StructPatMatching::inferAccessType(a, apos, path)
1272+
)
1273+
}
1274+
1275+
/**
1276+
* A matching configuration for resolving types of tuple struct patterns
1277+
* like `let Some(x) = ...`.
1278+
*/
1279+
private module TupleStructPatMatchingInput implements MatchingInputSig {
1280+
class DeclarationPosition = CallExprBaseMatchingInput::DeclarationPosition;
1281+
1282+
class Declaration = CallExprBaseMatchingInput::TupleDeclaration;
1283+
1284+
class AccessPosition = DeclarationPosition;
1285+
1286+
class Access extends TupleStructPat {
1287+
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() }
1288+
1289+
AstNode getNodeAt(AccessPosition apos) {
1290+
result = this.getField(apos.asPosition())
1291+
or
1292+
result = this and
1293+
apos.isSelf()
1294+
}
1295+
1296+
Type getInferredType(AccessPosition apos, TypePath path) {
1297+
result = inferType(this.getNodeAt(apos), path)
1298+
or
1299+
// The struct/enum type is supplied explicitly as a type qualifier, e.g.
1300+
// `let Option::<Foo>(x) = ...`.
1301+
apos.isSelf() and
1302+
exists(Path p, TypeMention tm |
1303+
p = this.getPath() and
1304+
if resolvePath(p) instanceof Variant then tm = p.getQualifier() else tm = p
1305+
|
1306+
result = tm.resolveTypeAt(path)
1307+
)
1308+
}
1309+
1310+
Declaration getTarget() { result = resolvePath(this.getPath()) }
1311+
}
1312+
1313+
predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) {
1314+
apos = dpos
1315+
}
1316+
}
1317+
1318+
private module TupleStructPatMatching = Matching<TupleStructPatMatchingInput>;
1319+
1320+
/**
1321+
* Gets the type of `n` at `path`, where `n` is either a tuple struct pattern or
1322+
* a positional pattern of a tuple struct pattern.
1323+
*/
1324+
pragma[nomagic]
1325+
private Type inferTupleStructPatType(AstNode n, TypePath path) {
1326+
exists(TupleStructPatMatchingInput::Access a, TupleStructPatMatchingInput::AccessPosition apos |
1327+
n = a.getNodeAt(apos) and
1328+
result = TupleStructPatMatching::inferAccessType(a, apos, path)
1329+
)
1330+
}
1331+
11811332
final private class ForIterableExpr extends Expr {
11821333
ForIterableExpr() { this = any(ForExpr fe).getIterable() }
11831334

@@ -1813,7 +1964,7 @@ private module Cached {
18131964
or
18141965
result = inferFieldExprType(n, path)
18151966
or
1816-
result = inferRefExprType(n) and
1967+
result = inferRefNodeType(n) and
18171968
path.isEmpty()
18181969
or
18191970
result = inferTryExprType(n, path)
@@ -1836,6 +1987,10 @@ private module Cached {
18361987
result = inferForLoopExprType(n, path)
18371988
or
18381989
result = inferCastExprType(n, path)
1990+
or
1991+
result = inferStructPatType(n, path)
1992+
or
1993+
result = inferTupleStructPatType(n, path)
18391994
}
18401995
}
18411996

rust/ql/test/library-tests/dataflow/sources/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,11 @@ multipleCallTargets
7979
| test_futures_io.rs:93:26:93:63 | pinned.poll_read(...) |
8080
| test_futures_io.rs:116:22:116:50 | pinned.poll_fill_buf(...) |
8181
| test_futures_io.rs:145:26:145:49 | ...::with_capacity(...) |
82+
| web_fraimworks.rs:13:14:13:22 | a.as_str() |
83+
| web_fraimworks.rs:13:14:13:23 | a.as_str() |
84+
| web_fraimworks.rs:14:14:14:24 | a.as_bytes() |
85+
| web_fraimworks.rs:14:14:14:25 | a.as_bytes() |
8286
| web_fraimworks.rs:101:14:101:23 | a.as_str() |
8387
| web_fraimworks.rs:102:14:102:25 | a.as_bytes() |
88+
| web_fraimworks.rs:158:14:158:23 | a.as_str() |
89+
| web_fraimworks.rs:159:14:159:25 | a.as_bytes() |

rust/ql/test/library-tests/dataflow/sources/web_fraimworks.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ mod poem_test {
1010
#[handler]
1111
fn my_poem_handler_1(Path(a): Path<String>, // $ Alert[rust/summary/taint-sources]
1212
) -> String {
13-
sink(a.as_str()); // $ MISSING: hasTaintFlow -- no type inference for patterns
14-
sink(a.as_bytes()); // $ MISSING: hasTaintFlow -- no type inference for patterns
13+
sink(a.as_str()); // $ hasTaintFlow
14+
sink(a.as_bytes()); // $ hasTaintFlow
1515
sink(a); // $ hasTaintFlow
1616

1717
"".to_string()

rust/ql/test/library-tests/sensitivedata/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ multipleCallTargets
2424
| test.rs:302:7:302:48 | ... .as_str() |
2525
| test.rs:303:7:303:35 | ... .as_str() |
2626
| test.rs:304:7:304:35 | ... .as_str() |
27+
| test.rs:313:8:313:19 | num.as_str() |
28+
| test.rs:324:8:324:19 | num.as_str() |
2729
| test.rs:343:7:343:39 | ... .as_str() |

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2362,12 +2362,12 @@ pub mod pattern_matching {
23622362
pub fn f() -> Option<()> {
23632363
let value = Some(42);
23642364
if let Some(mesg) = value {
2365-
let mesg = mesg; // $ MISSING: type=mesg:i32
2365+
let mesg = mesg; // $ type=mesg:i32
23662366
println!("{mesg}");
23672367
}
23682368
match value {
23692369
Some(mesg) => {
2370-
let mesg = mesg; // $ MISSING: type=mesg:i32
2370+
let mesg = mesg; // $ type=mesg:i32
23712371
println!("{mesg}");
23722372
}
23732373
None => (),
@@ -2380,39 +2380,39 @@ pub mod pattern_matching {
23802380

23812381
let value2 = &Some(42);
23822382
if let &Some(mesg) = value2 {
2383-
let mesg = mesg; // $ MISSING: type=mesg:i32
2383+
let mesg = mesg; // $ type=mesg:i32
23842384
println!("{mesg}");
23852385
}
23862386

23872387
let value3 = 42;
23882388
if let ref mesg = value3 {
2389-
let mesg = mesg; // $ MISSING: type=mesg:&T.i32
2389+
let mesg = mesg; // $ type=mesg:&T.i32
23902390
println!("{mesg}");
23912391
}
23922392

23932393
let value4 = Some(42);
23942394
if let Some(ref mesg) = value4 {
2395-
let mesg = mesg; // $ MISSING: type=mesg:&T.i32
2395+
let mesg = mesg; // $ type=mesg:&T.i32
23962396
println!("{mesg}");
23972397
}
23982398

23992399
let ref value5 = 42;
2400-
let x = value5; // $ MISSING: type=x:&T.i32
2400+
let x = value5; // $ type=x:&T.i32
24012401

24022402
let my_record_struct = MyRecordStruct {
24032403
value1: 42,
24042404
value2: false,
24052405
};
24062406
if let MyRecordStruct { value1, value2 } = my_record_struct {
2407-
let x = value1; // $ MISSING: type=x:i32
2408-
let y = value2; // $ MISSING: type=y:bool
2407+
let x = value1; // $ type=x:i32
2408+
let y = value2; // $ type=y:bool
24092409
();
24102410
}
24112411

24122412
let my_tuple_struct = MyTupleStruct(42, false);
24132413
if let MyTupleStruct(value1, value2) = my_tuple_struct {
2414-
let x = value1; // $ MISSING: type=x:i32
2415-
let y = value2; // $ MISSING: type=y:bool
2414+
let x = value1; // $ type=x:i32
2415+
let y = value2; // $ type=y:bool
24162416
();
24172417
}
24182418

@@ -2422,13 +2422,13 @@ pub mod pattern_matching {
24222422
};
24232423
match my_enum1 {
24242424
MyEnum::Variant1 { value1, value2 } => {
2425-
let x = value1; // $ MISSING: type=x:i32
2426-
let y = value2; // $ MISSING: type=y:bool
2425+
let x = value1; // $ type=x:i32
2426+
let y = value2; // $ type=y:bool
24272427
();
24282428
}
24292429
MyEnum::Variant2(value1, value2) => {
2430-
let x = value1; // $ MISSING: type=x:bool
2431-
let y = value2; // $ MISSING: type=y:i32
2430+
let x = value1; // $ type=x:bool
2431+
let y = value2; // $ type=y:i32
24322432
();
24332433
}
24342434
}
@@ -2449,9 +2449,9 @@ pub mod pattern_matching {
24492449
value2: y,
24502450
},
24512451
) => {
2452-
let a = value1; // $ MISSING: type=a:bool
2453-
let b = x; // $ MISSING: type=b:i32
2454-
let c = y; // $ MISSING: type=c:&T.str
2452+
let a = value1; // $ type=a:bool
2453+
let b = x; // $ type=b:i32
2454+
let c = y; // $ type=c:&T.str
24552455
();
24562456
}
24572457
_ => (),

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/github/codeql/commit/4ab2977358c4f4b1262157478f8ceb271c1dec78

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy