Skip to content

Commit 29ba013

Browse files
committed
Rust: Add support for resolving methods from blanket implementations
1 parent d10cdfb commit 29ba013

File tree

7 files changed

+206
-9
lines changed

7 files changed

+206
-9
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,14 @@ final class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
10061006
Path getABoundPath() { result = this.getTypeBoundAt(_, _).getTypeRepr().(PathTypeRepr).getPath() }
10071007

10081008
pragma[nomagic]
1009+
ItemNode resolveBound(int index) {
1010+
result =
1011+
rank[index + 1](int i, int j |
1012+
|
1013+
resolvePath(this.getTypeBoundAt(i, j).getTypeRepr().(PathTypeRepr).getPath()) order by i, j
1014+
)
1015+
}
1016+
10091017
ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) }
10101018

10111019
/**

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

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
19151915
methodCandidate(type, name, arity, impl)
19161916
}
19171917

1918+
/**
1919+
* Holds if `mc` has `rootType` as the root type of the reciever and the target
1920+
* method is named `name` and has arity `arity`
1921+
*/
19181922
pragma[nomagic]
19191923
private predicate isMethodCall(MethodCall mc, Type rootType, string name, int arity) {
19201924
rootType = mc.getTypeAt(TypePath::nil()) and
@@ -2153,6 +2157,155 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
21532157
else any()
21542158
}
21552159

2160+
private module BlanketImplementation {
2161+
/**
2162+
* Gets the type parameter for which `impl` is a blanket implementation, if
2163+
* any.
2164+
*/
2165+
private TypeParamItemNode getBlanketImplementationTypeParam(Impl impl) {
2166+
result = impl.(ImplItemNode).resolveSelfTy() and
2167+
result = impl.getGenericParamList().getAGenericParam() and
2168+
// This impl block is not superseded by the expansion of an attribute macro.
2169+
not exists(impl.getAttributeMacroExpansion())
2170+
}
2171+
2172+
predicate isBlanketImplementation(Impl impl) { exists(getBlanketImplementationTypeParam(impl)) }
2173+
2174+
private Impl getPotentialDuplicated(string fileName, string traitName, int arity, string tpName) {
2175+
tpName = getBlanketImplementationTypeParam(result).getName() and
2176+
fileName = result.getLocation().getFile().getBaseName() and
2177+
traitName = result.(ImplItemNode).resolveTraitTy().getName() and
2178+
arity = result.(ImplItemNode).resolveTraitTy().(Trait).getNumberOfGenericParams()
2179+
}
2180+
2181+
/**
2182+
* Holds if `impl1` and `impl2` are duplicates and `impl2` is strictly more
2183+
* "canonical" than `impl1`.
2184+
*
2185+
* Libraries can often occur several times in the database for different
2186+
* library versions. This causes the same blanket implementations to exist
2187+
* multiple times, and these add no useful information.
2188+
*
2189+
* We detect these duplicates based on some simple heuristics (same trait
2190+
* name, file name, etc.). For these duplicates we select the one with the
2191+
* greatest file name (which usually is also the one with the greatest library
2192+
* version in the path)
2193+
*/
2194+
predicate duplicatedImpl(Impl impl1, Impl impl2) {
2195+
exists(string fileName, string traitName, int arity, string tpName |
2196+
impl1 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
2197+
impl2 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
2198+
impl1.getLocation().getFile().getAbsolutePath() <
2199+
impl2.getLocation().getFile().getAbsolutePath()
2200+
)
2201+
}
2202+
2203+
predicate isCanonicalImpl(Impl impl) {
2204+
not duplicatedImpl(impl, _) and isBlanketImplementation(impl)
2205+
}
2206+
2207+
Impl getCanonicalImpl(Impl impl) {
2208+
result =
2209+
max(Impl impl0, Location l |
2210+
duplicatedImpl(impl, impl0) and l = impl0.getLocation()
2211+
|
2212+
impl0 order by l.getFile().getAbsolutePath(), l.getStartLine()
2213+
)
2214+
or
2215+
isCanonicalImpl(impl) and result = impl
2216+
}
2217+
2218+
predicate isCanonicalBlanketImplementation(Impl impl) { impl = getCanonicalImpl(impl) }
2219+
2220+
/**
2221+
* Holds if `impl` is a blanket implementation for a type parameter and the type
2222+
* parameter must implement `trait`.
2223+
*/
2224+
private predicate blanketImplementationTraitBound(Impl impl, Trait t) {
2225+
t =
2226+
min(Trait trait, int i |
2227+
trait = getBlanketImplementationTypeParam(impl).resolveBound(i) and
2228+
// Exclude traits that are known to not narrow things down very much.
2229+
not trait.getName().getText() =
2230+
[
2231+
"Sized", "Clone",
2232+
// The auto traits
2233+
"Send", "Sync", "Unpin", "UnwindSafe", "RefUnwindSafe"
2234+
]
2235+
|
2236+
trait order by i
2237+
)
2238+
}
2239+
2240+
/**
2241+
* Holds if `impl` is a relevant blanket implementation that requires the
2242+
* trait `trait` and provides `f`, a method with name `name` and arity
2243+
* `arity`.
2244+
*/
2245+
private predicate blanketImplementationMethod(
2246+
ImplItemNode impl, Trait trait, string name, int arity, Function f
2247+
) {
2248+
isCanonicalBlanketImplementation(impl) and
2249+
blanketImplementationTraitBound(impl, trait) and
2250+
f.getParamList().hasSelfParam() and
2251+
arity = f.getParamList().getNumberOfParams() and
2252+
(
2253+
f = impl.getAssocItem(name)
2254+
or
2255+
// If the trait has a method with a default implementation, then that
2256+
// target is interesting as well.
2257+
not exists(impl.getAssocItem(name)) and
2258+
f = impl.resolveTraitTy().getAssocItem(name)
2259+
) and
2260+
// If the method is already available through one of the trait bounds on the
2261+
// type parameter (because they share a common ancestor trait) then ignore
2262+
// it.
2263+
not getBlanketImplementationTypeParam(impl).resolveABound().(TraitItemNode).getASuccessor(name) =
2264+
f
2265+
}
2266+
2267+
predicate methodCallMatchesBlanketImpl(MethodCall mc, Type t, Impl impl, Trait trait, Function f) {
2268+
// Only check method calls where we have ruled out inherent method targets.
2269+
// Ideally we would also check if non-blanket method targets have been ruled
2270+
// out.
2271+
methodCallHasNoInherentTarget(mc) and
2272+
exists(string name, int arity |
2273+
isMethodCall(mc, t, name, arity) and
2274+
blanketImplementationMethod(impl, trait, name, arity, f)
2275+
)
2276+
}
2277+
2278+
private predicate relevantTraitVisible(Element mc, Trait trait) {
2279+
exists(ImplItemNode impl |
2280+
methodCallMatchesBlanketImpl(mc, _, impl, _, _) and
2281+
trait = impl.resolveTraitTy()
2282+
)
2283+
}
2284+
2285+
module SatisfiesConstraintInput implements SatisfiesConstraintInputSig<MethodCall> {
2286+
pragma[nomagic]
2287+
predicate relevantConstraint(MethodCall mc, Type constraint) {
2288+
exists(Trait trait, Trait trait2, ImplItemNode impl |
2289+
methodCallMatchesBlanketImpl(mc, _, impl, trait, _) and
2290+
TraitIsVisible<relevantTraitVisible/2>::traitIsVisible(mc, pragma[only_bind_into](trait2)) and
2291+
trait2 = pragma[only_bind_into](impl.resolveTraitTy()) and
2292+
trait = constraint.(TraitType).getTrait()
2293+
)
2294+
}
2295+
2296+
predicate useUniversalConditions() { none() }
2297+
}
2298+
2299+
predicate hasBlanketImpl(MethodCall mc, Type t, Impl impl, Trait trait, Function f) {
2300+
SatisfiesConstraint<MethodCall, SatisfiesConstraintInput>::satisfiesConstraintType(mc,
2301+
TTrait(trait), _, _) and
2302+
methodCallMatchesBlanketImpl(mc, t, impl, trait, f)
2303+
}
2304+
2305+
pragma[nomagic]
2306+
Function getMethodFromBlanketImpl(MethodCall mc) { hasBlanketImpl(mc, _, _, _, result) }
2307+
}
2308+
21562309
/** Gets a method from an `impl` block that matches the method call `mc`. */
21572310
pragma[nomagic]
21582311
private Function getMethodFromImpl(MethodCall mc) {
@@ -2188,6 +2341,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
21882341
// The method comes from an `impl` block targeting the type of the receiver.
21892342
result = getMethodFromImpl(mc)
21902343
or
2344+
result = BlanketImplementation::getMethodFromBlanketImpl(mc)
2345+
or
21912346
// The type of the receiver is a type parameter and the method comes from a
21922347
// trait bound on the type parameter.
21932348
result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ mod basic_blanket_impl {
3232
pub fn test_basic_blanket() {
3333
let x = S1.clone1(); // $ target=S1::clone1
3434
println!("{x:?}");
35-
let y = S1.duplicate(); // $ MISSING: target=Clone1duplicate
35+
let y = S1.duplicate(); // $ target=Clone1duplicate
3636
println!("{y:?}");
3737
}
3838
}
@@ -109,7 +109,7 @@ mod extension_trait_blanket_impl {
109109

110110
fn test() {
111111
let my_try_flag = MyTryFlag { flag: true };
112-
let result = my_try_flag.try_read_flag_twice(); // $ MISSING: target=TryFlagExt::try_read_flag_twice
112+
let result = my_try_flag.try_read_flag_twice(); // $ target=TryFlagExt::try_read_flag_twice
113113

114114
let my_flag = MyFlag { flag: true };
115115
// Here `TryFlagExt::try_read_flag_twice` is since there is a blanket
@@ -150,11 +150,11 @@ pub mod sql_exec {
150150
pub fn f() {
151151
let c = MySqlConnection {}; // $ certainType=c:MySqlConnection
152152

153-
c.execute1(); // $ MISSING: target=execute1
153+
c.execute1(); // $ target=execute1
154154
MySqlConnection::execute1(&c); // $ MISSING: target=execute1
155155

156-
c.execute2("SELECT * FROM users"); // $ MISSING: target=execute2
157-
c.execute2::<&str>("SELECT * FROM users"); // $ MISSING: target=execute2
156+
c.execute2("SELECT * FROM users"); // $ target=execute2
157+
c.execute2::<&str>("SELECT * FROM users"); // $ target=execute2
158158
MySqlConnection::execute2(&c, "SELECT * FROM users"); // $ MISSING: target=execute2
159159
MySqlConnection::execute2::<&str>(&c, "SELECT * FROM users"); // $ MISSING: target=execute2
160160
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn test_assoc_type(obj: &dyn AssocTrait<i64, AP = bool>) {
101101
pub fn test() {
102102
test_basic_dyn_trait(&MyStruct { value: 42 }); // $ target=test_basic_dyn_trait
103103
test_generic_dyn_trait(&GenStruct {
104-
value: "".to_string(),
104+
value: "".to_string(), // $ target=to_string
105105
}); // $ target=test_generic_dyn_trait
106106
test_poly_dyn_trait(); // $ target=test_poly_dyn_trait
107107
test_assoc_type(&GenStruct { value: 100 }); // $ target=test_assoc_type

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ mod method_non_parametric_trait_impl {
365365

366366
fn type_bound_type_parameter_impl<TP: MyTrait<S1>>(thing: TP) -> S1 {
367367
// The trait bound on `TP` makes the implementation of `ConvertTo` valid
368-
thing.convert_to() // $ MISSING: target=T::convert_to
368+
thing.convert_to() // $ target=T::convert_to
369369
}
370370

371371
pub fn f() {
@@ -437,7 +437,7 @@ mod method_non_parametric_trait_impl {
437437
let x = get_snd_fst(c); // $ type=x:S1 target=get_snd_fst
438438

439439
let thing = MyThing { a: S1 };
440-
let i = thing.convert_to(); // $ MISSING: type=i:S1 target=T::convert_to
440+
let i = thing.convert_to(); // $ type=i:S1 target=T::convert_to
441441
let j = convert_to(thing); // $ type=j:S1 target=convert_to
442442
}
443443
}
@@ -1376,7 +1376,7 @@ mod method_call_type_conversion {
13761376
let t = x7.m1(); // $ target=m1 type=t:& type=t:&T.S2
13771377
println!("{:?}", x7);
13781378

1379-
let x9: String = "Hello".to_string(); // $ certainType=x9:String
1379+
let x9: String = "Hello".to_string(); // $ certainType=x9:String target=to_string
13801380

13811381
// Implicit `String` -> `str` conversion happens via the `Deref` trait:
13821382
// https://doc.rust-lang.org/std/string/struct.String.html#deref.

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ inferType
2323
| blanket_impl.rs:34:18:34:24 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
2424
| blanket_impl.rs:34:18:34:24 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
2525
| blanket_impl.rs:34:20:34:20 | x | | blanket_impl.rs:4:5:5:14 | S1 |
26+
| blanket_impl.rs:35:13:35:13 | y | | blanket_impl.rs:4:5:5:14 | S1 |
2627
| blanket_impl.rs:35:17:35:18 | S1 | | blanket_impl.rs:4:5:5:14 | S1 |
28+
| blanket_impl.rs:35:17:35:30 | S1.duplicate() | | blanket_impl.rs:4:5:5:14 | S1 |
2729
| blanket_impl.rs:36:18:36:24 | "{y:?}\\n" | | file://:0:0:0:0 | & |
2830
| blanket_impl.rs:36:18:36:24 | "{y:?}\\n" | &T | {EXTERNAL LOCATION} | str |
2931
| blanket_impl.rs:36:18:36:24 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
3032
| blanket_impl.rs:36:18:36:24 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
33+
| blanket_impl.rs:36:20:36:20 | y | | blanket_impl.rs:4:5:5:14 | S1 |
3134
| blanket_impl.rs:47:22:47:26 | SelfParam | | file://:0:0:0:0 | & |
3235
| blanket_impl.rs:47:22:47:26 | SelfParam | &T | blanket_impl.rs:46:5:48:5 | Self [trait Flag] |
3336
| blanket_impl.rs:51:26:51:30 | SelfParam | | file://:0:0:0:0 | & |
@@ -78,7 +81,11 @@ inferType
7881
| blanket_impl.rs:111:13:111:23 | my_try_flag | | blanket_impl.rs:77:5:79:5 | MyTryFlag |
7982
| blanket_impl.rs:111:27:111:50 | MyTryFlag {...} | | blanket_impl.rs:77:5:79:5 | MyTryFlag |
8083
| blanket_impl.rs:111:45:111:48 | true | | {EXTERNAL LOCATION} | bool |
84+
| blanket_impl.rs:112:13:112:18 | result | | {EXTERNAL LOCATION} | Option |
85+
| blanket_impl.rs:112:13:112:18 | result | T | {EXTERNAL LOCATION} | bool |
8186
| blanket_impl.rs:112:22:112:32 | my_try_flag | | blanket_impl.rs:77:5:79:5 | MyTryFlag |
87+
| blanket_impl.rs:112:22:112:54 | my_try_flag.try_read_flag_twice() | | {EXTERNAL LOCATION} | Option |
88+
| blanket_impl.rs:112:22:112:54 | my_try_flag.try_read_flag_twice() | T | {EXTERNAL LOCATION} | bool |
8289
| blanket_impl.rs:114:13:114:19 | my_flag | | blanket_impl.rs:88:5:90:5 | MyFlag |
8390
| blanket_impl.rs:114:23:114:43 | MyFlag {...} | | blanket_impl.rs:88:5:90:5 | MyFlag |
8491
| blanket_impl.rs:114:38:114:41 | true | | {EXTERNAL LOCATION} | bool |
@@ -756,10 +763,13 @@ inferType
756763
| dyn_type.rs:103:28:105:5 | &... | | file://:0:0:0:0 | & |
757764
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:10:1:13:1 | dyn GenericGet |
758765
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
766+
| dyn_type.rs:103:28:105:5 | &... | &T.A | {EXTERNAL LOCATION} | String |
759767
| dyn_type.rs:103:28:105:5 | &... | &T.dyn(A) | {EXTERNAL LOCATION} | String |
760768
| dyn_type.rs:103:29:105:5 | GenStruct {...} | | dyn_type.rs:33:1:36:1 | GenStruct |
769+
| dyn_type.rs:103:29:105:5 | GenStruct {...} | A | {EXTERNAL LOCATION} | String |
761770
| dyn_type.rs:104:16:104:17 | "" | | file://:0:0:0:0 | & |
762771
| dyn_type.rs:104:16:104:17 | "" | &T | {EXTERNAL LOCATION} | str |
772+
| dyn_type.rs:104:16:104:29 | "".to_string() | | {EXTERNAL LOCATION} | String |
763773
| dyn_type.rs:107:21:107:45 | &... | | file://:0:0:0:0 | & |
764774
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:15:1:19:1 | dyn AssocTrait |
765775
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
@@ -1392,8 +1402,10 @@ inferType
13921402
| main.rs:439:21:439:37 | MyThing {...} | | main.rs:224:5:227:5 | MyThing |
13931403
| main.rs:439:21:439:37 | MyThing {...} | A | main.rs:235:5:236:14 | S1 |
13941404
| main.rs:439:34:439:35 | S1 | | main.rs:235:5:236:14 | S1 |
1405+
| main.rs:440:13:440:13 | i | | main.rs:235:5:236:14 | S1 |
13951406
| main.rs:440:17:440:21 | thing | | main.rs:224:5:227:5 | MyThing |
13961407
| main.rs:440:17:440:21 | thing | A | main.rs:235:5:236:14 | S1 |
1408+
| main.rs:440:17:440:34 | thing.convert_to() | | main.rs:235:5:236:14 | S1 |
13971409
| main.rs:441:13:441:13 | j | | main.rs:235:5:236:14 | S1 |
13981410
| main.rs:441:17:441:33 | convert_to(...) | | main.rs:235:5:236:14 | S1 |
13991411
| main.rs:441:28:441:32 | thing | | main.rs:224:5:227:5 | MyThing |
@@ -3319,14 +3331,22 @@ inferType
33193331
| main.rs:1706:13:1709:13 | Vec2 {...} | | main.rs:1586:5:1591:5 | Vec2 |
33203332
| main.rs:1707:20:1707:23 | self | | main.rs:1586:5:1591:5 | Vec2 |
33213333
| main.rs:1707:20:1707:25 | self.x | | {EXTERNAL LOCATION} | i64 |
3334+
| main.rs:1707:20:1707:33 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
33223335
| main.rs:1707:20:1707:33 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3336+
| main.rs:1707:20:1707:33 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
33233337
| main.rs:1707:29:1707:31 | rhs | | main.rs:1586:5:1591:5 | Vec2 |
3338+
| main.rs:1707:29:1707:33 | rhs.x | | {EXTERNAL LOCATION} | NonZero |
33243339
| main.rs:1707:29:1707:33 | rhs.x | | {EXTERNAL LOCATION} | i64 |
3340+
| main.rs:1707:29:1707:33 | rhs.x | T | {EXTERNAL LOCATION} | i64 |
33253341
| main.rs:1708:20:1708:23 | self | | main.rs:1586:5:1591:5 | Vec2 |
33263342
| main.rs:1708:20:1708:25 | self.y | | {EXTERNAL LOCATION} | i64 |
3343+
| main.rs:1708:20:1708:33 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
33273344
| main.rs:1708:20:1708:33 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3345+
| main.rs:1708:20:1708:33 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
33283346
| main.rs:1708:29:1708:31 | rhs | | main.rs:1586:5:1591:5 | Vec2 |
3347+
| main.rs:1708:29:1708:33 | rhs.y | | {EXTERNAL LOCATION} | NonZero |
33293348
| main.rs:1708:29:1708:33 | rhs.y | | {EXTERNAL LOCATION} | i64 |
3349+
| main.rs:1708:29:1708:33 | rhs.y | T | {EXTERNAL LOCATION} | i64 |
33303350
| main.rs:1714:25:1714:33 | SelfParam | | file://:0:0:0:0 | & |
33313351
| main.rs:1714:25:1714:33 | SelfParam | &T | main.rs:1586:5:1591:5 | Vec2 |
33323352
| main.rs:1714:36:1714:38 | rhs | | main.rs:1586:5:1591:5 | Vec2 |
@@ -3664,9 +3684,13 @@ inferType
36643684
| main.rs:1857:26:1857:30 | 33i64 | | {EXTERNAL LOCATION} | i64 |
36653685
| main.rs:1857:26:1857:38 | ... & ... | | {EXTERNAL LOCATION} | i64 |
36663686
| main.rs:1857:34:1857:38 | 34i64 | | {EXTERNAL LOCATION} | i64 |
3687+
| main.rs:1858:13:1858:21 | i64_bitor | | {EXTERNAL LOCATION} | NonZero |
36673688
| main.rs:1858:13:1858:21 | i64_bitor | | {EXTERNAL LOCATION} | i64 |
3689+
| main.rs:1858:13:1858:21 | i64_bitor | T | {EXTERNAL LOCATION} | i64 |
36683690
| main.rs:1858:25:1858:29 | 35i64 | | {EXTERNAL LOCATION} | i64 |
3691+
| main.rs:1858:25:1858:37 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
36693692
| main.rs:1858:25:1858:37 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3693+
| main.rs:1858:25:1858:37 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
36703694
| main.rs:1858:33:1858:37 | 36i64 | | {EXTERNAL LOCATION} | i64 |
36713695
| main.rs:1859:13:1859:22 | i64_bitxor | | {EXTERNAL LOCATION} | i64 |
36723696
| main.rs:1859:26:1859:30 | 37i64 | | {EXTERNAL LOCATION} | i64 |

shared/typeinference/codeql/typeinference/internal/TypeInference.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,15 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
919919
signature module SatisfiesConstraintInputSig<HasTypeTreeSig HasTypeTree> {
920920
/** Holds if it is relevant to know if `term` satisfies `constraint`. */
921921
predicate relevantConstraint(HasTypeTree term, Type constraint);
922+
923+
/**
924+
* Holds if constraints that are satisfied through conditions that are
925+
* universally quantified type parameters should be used. Such type
926+
* parameters might have type parameter constraints, and these are _not_
927+
* checked. Hence using these represent a trade-off between too many
928+
* constraints and too few constraints being satisfied.
929+
*/
930+
default predicate useUniversalConditions() { any() }
922931
}
923932

924933
module SatisfiesConstraint<
@@ -961,6 +970,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
961970
TypeMention constraintMention
962971
) {
963972
exists(Type type | hasTypeConstraint(tt, type, constraint) |
973+
useUniversalConditions() and
964974
not exists(countConstraintImplementations(type, constraint)) and
965975
conditionSatisfiesConstraintTypeAt(abs, condition, constraintMention, _, _) and
966976
resolveTypeMentionRoot(condition) = abs.getATypeParameter() and

0 commit comments

Comments
 (0)