Skip to content

Commit 7cfc245

Browse files
committed
blanket
1 parent bb0960e commit 7cfc245

File tree

2 files changed

+174
-16
lines changed

2 files changed

+174
-16
lines changed

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

Lines changed: 161 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,28 @@ private module MethodCallResolution {
12211221
)
12221222
}
12231223

1224+
/**
1225+
* Holds if method `m` with the name `name` and the arity `arity` exists in
1226+
* `i`, and the type of the `self` parameter is `selfType`.
1227+
*
1228+
* `strippedTypePath` points to the type `strippedType` inside `selfType`,
1229+
* which is the (possibly complex-stripped) root type of `selfType`.
1230+
*/
1231+
pragma[nomagic]
1232+
private predicate methodInfo0(
1233+
Function m, string name, int arity, ImplOrTraitItemNode i, FunctionType selfType,
1234+
TypePath strippedTypePath, Type strippedType
1235+
) {
1236+
exists(FunctionTypePosition pos |
1237+
m = i.getASuccessor(name) and
1238+
arity = m.getParamList().getNumberOfParams() and
1239+
strippedType = selfType.getTypeAt(strippedTypePath) and
1240+
isComplexRootStripped(strippedTypePath, strippedType) and
1241+
selfType.appliesTo(m, pos, i) and
1242+
pos.isSelf()
1243+
)
1244+
}
1245+
12241246
/**
12251247
* Holds if method `m` with the name `name` and the arity `arity` exists in
12261248
* `i`, and the type of the `self` parameter is `selfType`.
@@ -1269,16 +1291,17 @@ private module MethodCallResolution {
12691291
pragma[nomagic]
12701292
private predicate methodTraitInfo(string name, int arity, Trait trait) {
12711293
exists(ImplItemNode i |
1272-
methodInfo(_, name, arity, i, _, _, _) and
1294+
methodInfo0(_, name, arity, i, _, _, _) and
12731295
trait = i.resolveTraitTy()
12741296
)
12751297
or
1276-
methodInfo(_, name, arity, trait, _, _, _)
1298+
methodInfo0(_, name, arity, trait, _, _, _)
12771299
}
12781300

12791301
pragma[nomagic]
12801302
private predicate methodCallTraitCandidate(Element mc, Trait trait) {
1281-
exists(string name, int arity | mc.(MethodCall).hasNameAndArity(name, arity) |
1303+
exists(string name, int arity |
1304+
mc.(MethodCall).hasNameAndArity(name, arity) and
12821305
methodTraitInfo(name, arity, trait)
12831306
)
12841307
}
@@ -1473,7 +1496,9 @@ private module MethodCallResolution {
14731496
*/
14741497
pragma[nomagic]
14751498
Function resolveCallTarget(string derefChainBorrow) {
1476-
result = MkMethodCallCand(this, derefChainBorrow).(MethodCallCand).resolveCallTarget()
1499+
exists(MethodCallCand mcc | mcc = MkMethodCallCand(this, derefChainBorrow) |
1500+
result = mcc.resolveCallTarget()
1501+
)
14771502
}
14781503

14791504
predicate receiverHasImplicitDeref(AstNode receiver) {
@@ -1561,7 +1586,7 @@ private module MethodCallResolution {
15611586
* resolve to a method in an `impl` block for the type of the receiver.
15621587
*/
15631588
pragma[nomagic]
1564-
private predicate hasNoInherentTarget() {
1589+
predicate hasNoInherentTarget() {
15651590
exists(TypePath strippedTypePath, Type strippedType, string name, int arity |
15661591
this.hasInfo(_, strippedTypePath, strippedType, name, arity) and
15671592
forall(Impl i |
@@ -1620,6 +1645,8 @@ private module MethodCallResolution {
16201645
result = this.resolveAmbigousCallTargetCand(pos, path, type) and
16211646
type = this.inferPositionalArgumentType(pos, path)
16221647
)
1648+
or
1649+
result = BlanketImplementation::getMethodFromBlanketImpl(this)
16231650
}
16241651

16251652
string toString() { result = mc_.toString() + " [" + derefChainBorrow + "]" }
@@ -1668,6 +1695,135 @@ private module MethodCallResolution {
16681695
methodInfo(_, _, _, _, constraint, _, _)
16691696
}
16701697
}
1698+
1699+
private module BlanketImplementation {
1700+
private ImplItemNode getPotentialDuplicated(
1701+
string fileName, string traitName, int arity, string tpName
1702+
) {
1703+
tpName = result.getBlanketImplementationTypeParam().getName() and
1704+
fileName = result.getLocation().getFile().getBaseName() and
1705+
traitName = result.resolveTraitTy().getName() and
1706+
arity = result.resolveTraitTy().(Trait).getNumberOfGenericParams()
1707+
}
1708+
1709+
private predicate duplicatedImpl(Impl impl1, Impl impl2) {
1710+
exists(string fileName, string traitName, int arity, string tpName |
1711+
impl1 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
1712+
impl2 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
1713+
impl1.getLocation().getFile().getAbsolutePath() <
1714+
impl2.getLocation().getFile().getAbsolutePath()
1715+
)
1716+
}
1717+
1718+
/**
1719+
* Holds if `impl` is a canonical blanket implementation.
1720+
*
1721+
* Libraries can often occur several times in the database for different
1722+
* library versions. This causes the same blanket implementations to exist
1723+
* multiple times, and these add no useful information.
1724+
*
1725+
* We detect these duplicates based on some simple heuristics (same trait
1726+
* name, file name, etc.). For these duplicates we select the one with the
1727+
* greatest file name (which usually is also the one with the greatest library
1728+
* version in the path) as the "canonical" implementation.
1729+
*/
1730+
private predicate isCanonicalImpl(Impl impl) {
1731+
not duplicatedImpl(impl, _) and impl.(ImplItemNode).isBlanketImplementation()
1732+
}
1733+
1734+
/**
1735+
* Holds if `impl` is a blanket implementation for a type parameter and
1736+
* `traitBound` is the first non-trivial trait bound of that type parameter.
1737+
*/
1738+
private predicate blanketImplementationTraitBound(ImplItemNode impl, Trait traitBound) {
1739+
traitBound =
1740+
min(Trait trait, int i |
1741+
trait = impl.getBlanketImplementationTypeParam().resolveBound(i) and
1742+
// Exclude traits that are known to not narrow things down very much.
1743+
not trait.getName().getText() =
1744+
[
1745+
"Sized", "Clone",
1746+
// The auto traits
1747+
"Send", "Sync", "Unpin", "UnwindSafe", "RefUnwindSafe"
1748+
]
1749+
|
1750+
trait order by i
1751+
)
1752+
}
1753+
1754+
/**
1755+
* Holds if `impl` is a relevant blanket implementation that requires the
1756+
* trait `traitBound` and provides `f`, a method with name `name` and arity
1757+
* `arity`.
1758+
*/
1759+
private predicate blanketImplementationMethod(
1760+
ImplItemNode impl, Trait traitBound, string name, int arity, Function f
1761+
) {
1762+
isCanonicalImpl(impl) and
1763+
blanketImplementationTraitBound(impl, traitBound) and
1764+
f.getParamList().hasSelfParam() and
1765+
arity = f.getParamList().getNumberOfParams() and
1766+
(
1767+
f = impl.getAssocItem(name)
1768+
or
1769+
// If the trait has a method with a default implementation, then that
1770+
// target is interesting as well.
1771+
not exists(impl.getAssocItem(name)) and
1772+
f = impl.resolveTraitTy().getAssocItem(name)
1773+
) and
1774+
// If the method is already available through one of the trait bounds on the
1775+
// type parameter (because they implement the trait targeted by the impl
1776+
// block) then ignore it.
1777+
not impl.getBlanketImplementationTypeParam()
1778+
.resolveABound()
1779+
.(TraitItemNode)
1780+
.getASuccessor(name) = f
1781+
}
1782+
1783+
pragma[nomagic]
1784+
predicate methodCallMatchesBlanketImpl(
1785+
MethodCallCand mcc, MethodCall mc, Type t, ImplItemNode impl, Trait traitBound,
1786+
Trait traitImpl, Function f
1787+
) {
1788+
// Only check method calls where we have ruled out inherent method targets.
1789+
// Ideally we would also check if non-blanket method targets have been ruled
1790+
// out.
1791+
mcc.hasNoInherentTarget() and
1792+
exists(string name, int arity |
1793+
mcc.hasInfo(mc, _, _, name, arity) and
1794+
// isMethodCall(mc, t, name, arity) and
1795+
t = mcc.getTypeAt(TypePath::nil()) and
1796+
blanketImplementationMethod(impl, traitBound, name, arity, f)
1797+
) and
1798+
traitImpl = impl.resolveTraitTy()
1799+
}
1800+
1801+
module SatisfiesConstraintInput implements SatisfiesConstraintInputSig<MethodCallCand> {
1802+
pragma[nomagic]
1803+
predicate relevantConstraint(MethodCallCand mcc, Type constraint) {
1804+
exists(MethodCall mc, Trait traitBound, Trait traitImpl |
1805+
methodCallMatchesBlanketImpl(mcc, mc, _, _, traitBound, traitImpl, _) and
1806+
methodCallVisibleTraitCandidate(mc, traitImpl) and
1807+
traitBound = constraint.(TraitType).getTrait()
1808+
)
1809+
}
1810+
1811+
predicate useUniversalConditions() { none() }
1812+
}
1813+
1814+
predicate hasBlanketImpl(
1815+
MethodCallCand mcc, MethodCall mc, Type t, Impl impl, Trait traitBound, Function f
1816+
) {
1817+
SatisfiesConstraint<MethodCallCand, SatisfiesConstraintInput>::satisfiesConstraintType(mcc,
1818+
TTrait(traitBound), _, _) and
1819+
methodCallMatchesBlanketImpl(mcc, mc, t, impl, traitBound, _, f)
1820+
}
1821+
1822+
pragma[nomagic]
1823+
Function getMethodFromBlanketImpl(MethodCallCand mcc) {
1824+
hasBlanketImpl(mcc, _, _, _, _, result)
1825+
}
1826+
}
16711827
}
16721828

16731829
/**

rust/ql/test/library-tests/type-inference/type-inference.expected

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,19 @@ inferType
4343
| blanket_impl.rs:49:18:49:25 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
4444
| blanket_impl.rs:49:20:49:21 | x2 | | blanket_impl.rs:6:5:7:14 | S1 |
4545
| blanket_impl.rs:50:18:50:19 | S1 | | blanket_impl.rs:6:5:7:14 | S1 |
46+
| blanket_impl.rs:50:18:50:19 | S1 | | file://:0:0:0:0 | & |
4647
| blanket_impl.rs:51:18:51:25 | "{x3:?}\\n" | | file://:0:0:0:0 | & |
4748
| blanket_impl.rs:51:18:51:25 | "{x3:?}\\n" | &T | {EXTERNAL LOCATION} | str |
4849
| blanket_impl.rs:51:18:51:25 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
4950
| blanket_impl.rs:51:18:51:25 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
5051
| blanket_impl.rs:52:18:52:22 | (...) | | file://:0:0:0:0 | & |
5152
| blanket_impl.rs:52:18:52:22 | (...) | &T | blanket_impl.rs:6:5:7:14 | S1 |
53+
| blanket_impl.rs:52:18:52:22 | (...) | &T | file://:0:0:0:0 | & |
5254
| blanket_impl.rs:52:19:52:21 | &S1 | | file://:0:0:0:0 | & |
5355
| blanket_impl.rs:52:19:52:21 | &S1 | &T | blanket_impl.rs:6:5:7:14 | S1 |
56+
| blanket_impl.rs:52:19:52:21 | &S1 | &T | file://:0:0:0:0 | & |
5457
| blanket_impl.rs:52:20:52:21 | S1 | | blanket_impl.rs:6:5:7:14 | S1 |
58+
| blanket_impl.rs:52:20:52:21 | S1 | | file://:0:0:0:0 | & |
5559
| blanket_impl.rs:53:18:53:25 | "{x4:?}\\n" | | file://:0:0:0:0 | & |
5660
| blanket_impl.rs:53:18:53:25 | "{x4:?}\\n" | &T | {EXTERNAL LOCATION} | str |
5761
| blanket_impl.rs:53:18:53:25 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
@@ -120,7 +124,11 @@ inferType
120124
| blanket_impl.rs:132:13:132:23 | my_try_flag | | blanket_impl.rs:98:5:100:5 | MyTryFlag |
121125
| blanket_impl.rs:132:27:132:50 | MyTryFlag {...} | | blanket_impl.rs:98:5:100:5 | MyTryFlag |
122126
| blanket_impl.rs:132:45:132:48 | true | | {EXTERNAL LOCATION} | bool |
127+
| blanket_impl.rs:133:13:133:18 | result | | {EXTERNAL LOCATION} | Option |
128+
| blanket_impl.rs:133:13:133:18 | result | T | {EXTERNAL LOCATION} | bool |
123129
| blanket_impl.rs:133:22:133:32 | my_try_flag | | blanket_impl.rs:98:5:100:5 | MyTryFlag |
130+
| blanket_impl.rs:133:22:133:54 | my_try_flag.try_read_flag_twice() | | {EXTERNAL LOCATION} | Option |
131+
| blanket_impl.rs:133:22:133:54 | my_try_flag.try_read_flag_twice() | T | {EXTERNAL LOCATION} | bool |
124132
| blanket_impl.rs:135:13:135:19 | my_flag | | blanket_impl.rs:109:5:111:5 | MyFlag |
125133
| blanket_impl.rs:135:23:135:43 | MyFlag {...} | | blanket_impl.rs:109:5:111:5 | MyFlag |
126134
| blanket_impl.rs:135:38:135:41 | true | | {EXTERNAL LOCATION} | bool |
@@ -899,10 +907,13 @@ inferType
899907
| dyn_type.rs:103:28:105:5 | &... | | file://:0:0:0:0 | & |
900908
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:10:1:13:1 | dyn GenericGet |
901909
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
910+
| dyn_type.rs:103:28:105:5 | &... | &T.A | {EXTERNAL LOCATION} | String |
902911
| dyn_type.rs:103:28:105:5 | &... | &T.dyn(A) | {EXTERNAL LOCATION} | String |
903912
| dyn_type.rs:103:29:105:5 | GenStruct {...} | | dyn_type.rs:33:1:36:1 | GenStruct |
913+
| dyn_type.rs:103:29:105:5 | GenStruct {...} | A | {EXTERNAL LOCATION} | String |
904914
| dyn_type.rs:104:16:104:17 | "" | | file://:0:0:0:0 | & |
905915
| dyn_type.rs:104:16:104:17 | "" | &T | {EXTERNAL LOCATION} | str |
916+
| dyn_type.rs:104:16:104:29 | "".to_string() | | {EXTERNAL LOCATION} | String |
906917
| dyn_type.rs:107:21:107:45 | &... | | file://:0:0:0:0 | & |
907918
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:15:1:19:1 | dyn AssocTrait |
908919
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
@@ -1535,8 +1546,10 @@ inferType
15351546
| main.rs:439:21:439:37 | MyThing {...} | | main.rs:224:5:227:5 | MyThing |
15361547
| main.rs:439:21:439:37 | MyThing {...} | A | main.rs:235:5:236:14 | S1 |
15371548
| main.rs:439:34:439:35 | S1 | | main.rs:235:5:236:14 | S1 |
1549+
| main.rs:440:13:440:13 | i | | main.rs:235:5:236:14 | S1 |
15381550
| main.rs:440:17:440:21 | thing | | main.rs:224:5:227:5 | MyThing |
15391551
| main.rs:440:17:440:21 | thing | A | main.rs:235:5:236:14 | S1 |
1552+
| main.rs:440:17:440:34 | thing.convert_to() | | main.rs:235:5:236:14 | S1 |
15401553
| main.rs:441:13:441:13 | j | | main.rs:235:5:236:14 | S1 |
15411554
| main.rs:441:17:441:33 | convert_to(...) | | main.rs:235:5:236:14 | S1 |
15421555
| main.rs:441:28:441:32 | thing | | main.rs:224:5:227:5 | MyThing |
@@ -6877,14 +6890,3 @@ inferType
68776890
| pattern_matching.rs:827:5:827:7 | f(...) | | {EXTERNAL LOCATION} | Option |
68786891
| pattern_matching.rs:827:5:827:7 | f(...) | T | file://:0:0:0:0 | () |
68796892
testFailures
6880-
| blanket_impl.rs:50:34:50:60 | //... | Missing result: target=Clone1duplicate |
6881-
| blanket_impl.rs:52:37:52:63 | //... | Missing result: target=Clone1duplicate |
6882-
| blanket_impl.rs:133:57:133:99 | //... | Missing result: target=TryFlagExt::try_read_flag_twice |
6883-
| blanket_impl.rs:174:23:174:42 | //... | Missing result: target=execute1 |
6884-
| blanket_impl.rs:177:44:177:63 | //... | Missing result: target=execute2 |
6885-
| blanket_impl.rs:178:52:178:71 | //... | Missing result: target=execute2 |
6886-
| dyn_type.rs:104:32:104:52 | //... | Missing result: target=to_string |
6887-
| main.rs:368:28:368:52 | //... | Missing result: target=T::convert_to |
6888-
| main.rs:440:37:440:71 | //... | Missing result: target=T::convert_to |
6889-
| main.rs:440:37:440:71 | //... | Missing result: type=i:S1 |
6890-
| main.rs:1379:47:1379:89 | //... | Missing result: target=to_string |

0 commit comments

Comments
 (0)