Skip to content

Commit cf0b3b5

Browse files
authored
Merge pull request #18632 from hvitved/rust/type-inference
Rust: Implement basic type inference in QL
2 parents a3ef137 + c3739d4 commit cf0b3b5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+4295
-200
lines changed

rust/ql/.generated.list

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ql/.gitattributes

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @name Path resolution inconsistencies
3+
* @description Lists the path resolution inconsistencies in the database. This query is intended for internal use.
4+
* @kind table
5+
* @id rust/diagnostics/path-resolution-consistency
6+
*/
7+
8+
import codeql.rust.internal.PathResolutionConsistency
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @name Type inference inconsistencies
3+
* @description Lists the type inference inconsistencies in the database. This query is intended for internal use.
4+
* @kind table
5+
* @id rust/diagnostics/type-inference-consistency
6+
*/
7+
8+
import codeql.rust.internal.TypeInferenceConsistency

rust/ql/integration-tests/hello-project/summary.expected

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
| Files extracted - without errors % | 80 |
99
| Inconsistencies - AST | 0 |
1010
| Inconsistencies - CFG | 0 |
11+
| Inconsistencies - Path resolution | 0 |
1112
| Inconsistencies - data flow | 0 |
1213
| Lines of code extracted | 6 |
1314
| Lines of user code extracted | 6 |

rust/ql/integration-tests/hello-workspace/summary.cargo.expected

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
| Files extracted - without errors % | 100 |
99
| Inconsistencies - AST | 0 |
1010
| Inconsistencies - CFG | 0 |
11+
| Inconsistencies - Path resolution | 0 |
1112
| Inconsistencies - data flow | 0 |
1213
| Lines of code extracted | 9 |
1314
| Lines of user code extracted | 9 |

rust/ql/integration-tests/hello-workspace/summary.rust-project.expected

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
| Files extracted - without errors % | 100 |
99
| Inconsistencies - AST | 0 |
1010
| Inconsistencies - CFG | 0 |
11+
| Inconsistencies - Path resolution | 0 |
1112
| Inconsistencies - data flow | 0 |
1213
| Lines of code extracted | 9 |
1314
| Lines of user code extracted | 9 |

rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll

+2-3
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,8 @@ final class RecordExprCfgNode extends Nodes::RecordExprCfgNode {
239239
pragma[nomagic]
240240
ExprCfgNode getFieldExpr(string field) {
241241
exists(RecordExprField ref |
242-
ref = node.getRecordExprFieldList().getAField() and
243-
any(ChildMapping mapping).hasCfgChild(node, ref.getExpr(), this, result) and
244-
field = ref.getFieldName()
242+
ref = node.getFieldExpr(field) and
243+
any(ChildMapping mapping).hasCfgChild(node, ref.getExpr(), this, result)
245244
)
246245
}
247246
}

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private import codeql.dataflow.internal.DataFlowImpl
1010
private import rust
1111
private import SsaImpl as SsaImpl
1212
private import codeql.rust.controlflow.internal.Scope as Scope
13-
private import codeql.rust.elements.internal.PathResolution
13+
private import codeql.rust.internal.PathResolution
1414
private import codeql.rust.controlflow.ControlFlowGraph
1515
private import codeql.rust.controlflow.CfgNodes
1616
private import codeql.rust.dataflow.Ssa
@@ -847,7 +847,7 @@ class TupleFieldContent extends FieldContent, TTupleFieldContent {
847847

848848
predicate isStructField(Struct s, int pos) { field.isStructField(s, pos) }
849849

850-
override FieldExprCfgNode getAnAccess() { none() } // TODO
850+
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getTupleField() }
851851

852852
final override string toString() {
853853
exists(Variant v, int pos, string vname |
@@ -878,7 +878,7 @@ class RecordFieldContent extends FieldContent, TRecordFieldContent {
878878

879879
predicate isStructField(Struct s, string name) { field.isStructField(s, name) }
880880

881-
override FieldExprCfgNode getAnAccess() { none() } // TODO
881+
override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getRecordField() }
882882

883883
final override string toString() {
884884
exists(Variant v, string name, string vname |

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

+3-14
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ private import codeql.rust.elements.Resolvable
1212
* be referenced directly.
1313
*/
1414
module Impl {
15-
private import codeql.rust.elements.internal.CallableImpl::Impl
16-
private import codeql.rust.elements.internal.MethodCallExprImpl::Impl
17-
private import codeql.rust.elements.internal.CallExprImpl::Impl
18-
private import codeql.rust.elements.internal.PathExprImpl::Impl
19-
private import codeql.rust.elements.internal.PathResolution
15+
private import rust
2016

2117
pragma[nomagic]
2218
Resolvable getCallResolvable(CallExprBase call) {
@@ -30,14 +26,7 @@ module Impl {
3026
* A function or method call expression. See `CallExpr` and `MethodCallExpr` for further details.
3127
*/
3228
class CallExprBase extends Generated::CallExprBase {
33-
/**
34-
* Gets the target callable of this call, if a unique such target can
35-
* be statically resolved.
36-
*/
37-
Callable getStaticTarget() {
38-
getCallResolvable(this).resolvesAsItem(result)
39-
or
40-
result = resolvePath(this.(CallExpr).getFunction().(PathExpr).getPath())
41-
}
29+
/** Gets the static target of this call, if any. */
30+
Callable getStaticTarget() { none() } // overridden by subclasses, but cannot be made abstract
4231
}
4332
}

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

+20-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@ private import codeql.rust.elements.PathExpr
1313
*/
1414
module Impl {
1515
private import rust
16-
private import PathResolution as PathResolution
16+
private import codeql.rust.internal.PathResolution as PathResolution
17+
18+
pragma[nomagic]
19+
Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() }
20+
21+
pragma[nomagic]
22+
PathResolution::ItemNode getResolvedFunction(CallExpr ce) {
23+
result = PathResolution::resolvePath(getFunctionPath(ce))
24+
}
1725

1826
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1927
/**
@@ -28,9 +36,17 @@ module Impl {
2836
class CallExpr extends Generated::CallExpr {
2937
override string toString() { result = this.getFunction().toAbbreviatedString() + "(...)" }
3038

39+
override Callable getStaticTarget() { result = getResolvedFunction(this) }
40+
41+
/** Gets the struct that this call resolves to, if any. */
42+
Struct getStruct() { result = getResolvedFunction(this) }
43+
44+
/** Gets the variant that this call resolves to, if any. */
45+
Variant getVariant() { result = getResolvedFunction(this) }
46+
3147
pragma[nomagic]
32-
private PathResolution::ItemNode getResolvedFunction(int pos) {
33-
result = PathResolution::resolvePath(this.getFunction().(PathExpr).getPath()) and
48+
private PathResolution::ItemNode getResolvedFunctionAndPos(int pos) {
49+
result = getResolvedFunction(this) and
3450
exists(this.getArgList().getArg(pos))
3551
}
3652

@@ -42,7 +58,7 @@ module Impl {
4258
*/
4359
pragma[nomagic]
4460
TupleField getTupleField(int pos) {
45-
exists(PathResolution::ItemNode i | i = this.getResolvedFunction(pos) |
61+
exists(PathResolution::ItemNode i | i = this.getResolvedFunctionAndPos(pos) |
4662
result.isStructField(i, pos) or
4763
result.isVariantField(i, pos)
4864
)

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

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ private import codeql.rust.elements.internal.generated.FieldExpr
1111
* be referenced directly.
1212
*/
1313
module Impl {
14+
private import rust
15+
private import codeql.rust.internal.TypeInference as TypeInference
16+
1417
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1518
/**
1619
* A field access expression. For example:
@@ -19,6 +22,12 @@ module Impl {
1922
* ```
2023
*/
2124
class FieldExpr extends Generated::FieldExpr {
25+
/** Gets the record field that this access references, if any. */
26+
RecordField getRecordField() { result = TypeInference::resolveRecordFieldExpr(this) }
27+
28+
/** Gets the tuple field that this access references, if any. */
29+
TupleField getTupleField() { result = TypeInference::resolveTupleFieldExpr(this) }
30+
2231
override string toString() {
2332
exists(string abbr, string name |
2433
abbr = this.getExpr().toAbbreviatedString() and

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

+15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ private import codeql.rust.elements.internal.generated.GenericArgList
1111
* be referenced directly.
1212
*/
1313
module Impl {
14+
private import rust
15+
1416
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1517
/**
1618
* The base class for generic arguments.
@@ -22,5 +24,18 @@ module Impl {
2224
override string toString() { result = this.toAbbreviatedString() }
2325

2426
override string toAbbreviatedString() { result = "<...>" }
27+
28+
/** Gets the `i`th type argument of this list. */
29+
TypeRepr getTypeArg(int i) {
30+
result =
31+
rank[i + 1](TypeRepr res, int j |
32+
res = this.getGenericArg(j).(TypeArg).getTypeRepr()
33+
|
34+
res order by j
35+
)
36+
}
37+
38+
/** Gets a type argument of this list. */
39+
TypeRepr getATypeArg() { result = this.getTypeArg(_) }
2540
}
2641
}

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

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// generated by codegen, remove this comment if you wish to edit this file
21
/**
32
* This module provides a hand-modifiable wrapper around the generated class `GenericParamList`.
43
*
@@ -12,11 +11,26 @@ private import codeql.rust.elements.internal.generated.GenericParamList
1211
* be referenced directly.
1312
*/
1413
module Impl {
14+
private import rust
15+
16+
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1517
/**
1618
* A GenericParamList. For example:
1719
* ```rust
1820
* todo!()
1921
* ```
2022
*/
21-
class GenericParamList extends Generated::GenericParamList { }
23+
class GenericParamList extends Generated::GenericParamList {
24+
override string toString() { result = this.toAbbreviatedString() }
25+
26+
override string toAbbreviatedString() { result = "<...>" }
27+
28+
/** Gets the `i`th type parameter of this list. */
29+
TypeParam getTypeParam(int i) {
30+
result = rank[i + 1](TypeParam res, int j | res = this.getGenericParam(j) | res order by j)
31+
}
32+
33+
/** Gets a type parameter of this list. */
34+
TypeParam getATypeParam() { result = this.getTypeParam(_) }
35+
}
2236
}

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

+22
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
* INTERNAL: Do not use.
55
*/
66

7+
private import rust
78
private import codeql.rust.elements.internal.generated.MethodCallExpr
9+
private import codeql.rust.internal.PathResolution
10+
private import codeql.rust.internal.TypeInference
811

912
/**
1013
* INTERNAL: This module contains the customizable definition of `MethodCallExpr` and should not
1114
* be referenced directly.
1215
*/
1316
module Impl {
17+
private predicate isImplFunction(Function f) { f = any(ImplItemNode impl).getAnAssocItem() }
18+
1419
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1520
/**
1621
* A method call expression. For example:
@@ -20,6 +25,23 @@ module Impl {
2025
* ```
2126
*/
2227
class MethodCallExpr extends Generated::MethodCallExpr {
28+
override Function getStaticTarget() {
29+
result = resolveMethodCallExpr(this) and
30+
(
31+
// prioritize `impl` methods first
32+
isImplFunction(result)
33+
or
34+
not isImplFunction(resolveMethodCallExpr(this)) and
35+
(
36+
// then trait methods with default implementations
37+
result.hasBody()
38+
or
39+
// and finally trait methods without default implementations
40+
not resolveMethodCallExpr(this).hasBody()
41+
)
42+
)
43+
}
44+
2345
override string toString() {
2446
exists(string base, string separator |
2547
base = this.getReceiver().toAbbreviatedString() and

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

+15-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.RecordExpr
1212
*/
1313
module Impl {
1414
private import rust
15-
private import PathResolution as PathResolution
15+
private import codeql.rust.internal.PathResolution as PathResolution
1616

1717
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1818
/**
@@ -27,13 +27,23 @@ module Impl {
2727
class RecordExpr extends Generated::RecordExpr {
2828
override string toString() { result = this.getPath().toString() + " {...}" }
2929

30+
/** Gets the record expression for the field `name`. */
31+
pragma[nomagic]
32+
RecordExprField getFieldExpr(string name) {
33+
result = this.getRecordExprFieldList().getAField() and
34+
name = result.getFieldName()
35+
}
36+
37+
pragma[nomagic]
38+
private PathResolution::ItemNode getResolvedPath(string name) {
39+
result = PathResolution::resolvePath(this.getPath()) and
40+
exists(this.getFieldExpr(name))
41+
}
42+
3043
/** Gets the record field that matches the `name` field of this record expression. */
3144
pragma[nomagic]
3245
RecordField getRecordField(string name) {
33-
exists(PathResolution::ItemNode i |
34-
i = PathResolution::resolvePath(this.getPath()) and
35-
name = this.getRecordExprFieldList().getAField().getFieldName()
36-
|
46+
exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) |
3747
result.isStructField(i, name) or
3848
result.isVariantField(i, name)
3949
)

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.RecordPat
1212
*/
1313
module Impl {
1414
private import rust
15-
private import PathResolution as PathResolution
15+
private import codeql.rust.internal.PathResolution as PathResolution
1616

1717
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1818
/**
@@ -27,13 +27,16 @@ module Impl {
2727
class RecordPat extends Generated::RecordPat {
2828
override string toString() { result = this.getPath().toAbbreviatedString() + " {...}" }
2929

30+
pragma[nomagic]
31+
private PathResolution::ItemNode getResolvedPath(string name) {
32+
result = PathResolution::resolvePath(this.getPath()) and
33+
name = this.getRecordPatFieldList().getAField().getFieldName()
34+
}
35+
3036
/** Gets the record field that matches the `name` pattern of this pattern. */
3137
pragma[nomagic]
3238
RecordField getRecordField(string name) {
33-
exists(PathResolution::ItemNode i |
34-
i = PathResolution::resolvePath(this.getPath()) and
35-
name = this.getRecordPatFieldList().getAField().getFieldName()
36-
|
39+
exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) |
3740
result.isStructField(i, name) or
3841
result.isVariantField(i, name)
3942
)

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

+12
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,17 @@ module Impl {
3232
/** Gets the `i`th tuple field, if any. */
3333
pragma[nomagic]
3434
TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) }
35+
36+
/** Holds if this struct uses tuple fields. */
37+
pragma[nomagic]
38+
predicate isTuple() { this.getFieldList() instanceof TupleFieldList }
39+
40+
/**
41+
* Holds if this struct uses record fields.
42+
*
43+
* Empty structs are considered to use record fields.
44+
*/
45+
pragma[nomagic]
46+
predicate isRecord() { not this.isTuple() }
3547
}
3648
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.TupleStructPat
1212
*/
1313
module Impl {
1414
private import rust
15-
private import PathResolution as PathResolution
15+
private import codeql.rust.internal.PathResolution as PathResolution
1616

1717
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1818
/**

0 commit comments

Comments
 (0)