@@ -1915,6 +1915,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
1915
1915
methodCandidate ( type , name , arity , impl )
1916
1916
}
1917
1917
1918
+ /**
1919
+ * Holds if `mc` has `rootType` as the root type of the receiver and the target
1920
+ * method is named `name` and has arity `arity`
1921
+ */
1918
1922
pragma [ nomagic]
1919
1923
private predicate isMethodCall ( MethodCall mc , Type rootType , string name , int arity ) {
1920
1924
rootType = mc .getTypeAt ( TypePath:: nil ( ) ) and
@@ -2153,6 +2157,130 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
2153
2157
else any ( )
2154
2158
}
2155
2159
2160
+ private module BlanketImplementation {
2161
+ private ImplItemNode getPotentialDuplicated (
2162
+ string fileName , string traitName , int arity , string tpName
2163
+ ) {
2164
+ tpName = result .getBlanketImplementationTypeParam ( ) .getName ( ) and
2165
+ fileName = result .getLocation ( ) .getFile ( ) .getBaseName ( ) and
2166
+ traitName = result .resolveTraitTy ( ) .getName ( ) and
2167
+ arity = result .resolveTraitTy ( ) .( Trait ) .getNumberOfGenericParams ( )
2168
+ }
2169
+
2170
+ private predicate duplicatedImpl ( Impl impl1 , Impl impl2 ) {
2171
+ exists ( string fileName , string traitName , int arity , string tpName |
2172
+ impl1 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2173
+ impl2 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2174
+ impl1 .getLocation ( ) .getFile ( ) .getAbsolutePath ( ) <
2175
+ impl2 .getLocation ( ) .getFile ( ) .getAbsolutePath ( )
2176
+ )
2177
+ }
2178
+
2179
+ /**
2180
+ * Holds if `impl` is a canonical blanket implementation.
2181
+ *
2182
+ * Libraries can often occur several times in the database for different
2183
+ * library versions. This causes the same blanket implementations to exist
2184
+ * multiple times, and these add no useful information.
2185
+ *
2186
+ * We detect these duplicates based on some simple heuristics (same trait
2187
+ * name, file name, etc.). For these duplicates we select the one with the
2188
+ * greatest file name (which usually is also the one with the greatest library
2189
+ * version in the path) as the "canonical" implementation.
2190
+ */
2191
+ private predicate isCanonicalImpl ( Impl impl ) {
2192
+ not duplicatedImpl ( impl , _) and impl .( ImplItemNode ) .isBlanketImplementation ( )
2193
+ }
2194
+
2195
+ /**
2196
+ * Holds if `impl` is a blanket implementation for a type parameter and
2197
+ * `traitBound` is the first non-trivial trait bound of that type parameter.
2198
+ */
2199
+ private predicate blanketImplementationTraitBound ( ImplItemNode impl , Trait traitBound ) {
2200
+ traitBound =
2201
+ min ( Trait trait , int i |
2202
+ trait = impl .getBlanketImplementationTypeParam ( ) .resolveBound ( i ) and
2203
+ // Exclude traits that are known to not narrow things down very much.
2204
+ not trait .getName ( ) .getText ( ) =
2205
+ [
2206
+ "Sized" , "Clone" ,
2207
+ // The auto traits
2208
+ "Send" , "Sync" , "Unpin" , "UnwindSafe" , "RefUnwindSafe"
2209
+ ]
2210
+ |
2211
+ trait order by i
2212
+ )
2213
+ }
2214
+
2215
+ /**
2216
+ * Holds if `impl` is a relevant blanket implementation that requires the
2217
+ * trait `traitBound` and provides `f`, a method with name `name` and arity
2218
+ * `arity`.
2219
+ */
2220
+ private predicate blanketImplementationMethod (
2221
+ ImplItemNode impl , Trait traitBound , string name , int arity , Function f
2222
+ ) {
2223
+ isCanonicalImpl ( impl ) and
2224
+ blanketImplementationTraitBound ( impl , traitBound ) and
2225
+ f .getParamList ( ) .hasSelfParam ( ) and
2226
+ arity = f .getParamList ( ) .getNumberOfParams ( ) and
2227
+ (
2228
+ f = impl .getAssocItem ( name )
2229
+ or
2230
+ // If the trait has a method with a default implementation, then that
2231
+ // target is interesting as well.
2232
+ not exists ( impl .getAssocItem ( name ) ) and
2233
+ f = impl .resolveTraitTy ( ) .getAssocItem ( name )
2234
+ ) and
2235
+ // If the method is already available through one of the trait bounds on the
2236
+ // type parameter (because they implement the trait targeted by the impl
2237
+ // block) then ignore it.
2238
+ not impl .getBlanketImplementationTypeParam ( ) .resolveABound ( ) .( TraitItemNode ) .getASuccessor ( name ) =
2239
+ f
2240
+ }
2241
+
2242
+ pragma [ nomagic]
2243
+ predicate methodCallMatchesBlanketImpl (
2244
+ MethodCall mc , Type t , ImplItemNode impl , Trait traitBound , Trait traitImpl , Function f
2245
+ ) {
2246
+ // Only check method calls where we have ruled out inherent method targets.
2247
+ // Ideally we would also check if non-blanket method targets have been ruled
2248
+ // out.
2249
+ methodCallHasNoInherentTarget ( mc ) and
2250
+ exists ( string name , int arity |
2251
+ isMethodCall ( mc , t , name , arity ) and
2252
+ blanketImplementationMethod ( impl , traitBound , name , arity , f )
2253
+ ) and
2254
+ traitImpl = impl .resolveTraitTy ( )
2255
+ }
2256
+
2257
+ private predicate relevantTraitVisible ( Element mc , Trait trait ) {
2258
+ methodCallMatchesBlanketImpl ( mc , _, _, _, trait , _)
2259
+ }
2260
+
2261
+ module SatisfiesConstraintInput implements SatisfiesConstraintInputSig< MethodCall > {
2262
+ pragma [ nomagic]
2263
+ predicate relevantConstraint ( MethodCall mc , Type constraint ) {
2264
+ exists ( Trait traitBound , Trait traitImpl |
2265
+ methodCallMatchesBlanketImpl ( mc , _, _, traitBound , traitImpl , _) and
2266
+ TraitIsVisible< relevantTraitVisible / 2 > :: traitIsVisible ( mc , traitImpl ) and
2267
+ traitBound = constraint .( TraitType ) .getTrait ( )
2268
+ )
2269
+ }
2270
+
2271
+ predicate useUniversalConditions ( ) { none ( ) }
2272
+ }
2273
+
2274
+ predicate hasBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait traitBound , Function f ) {
2275
+ SatisfiesConstraint< MethodCall , SatisfiesConstraintInput > :: satisfiesConstraintType ( mc ,
2276
+ TTrait ( traitBound ) , _, _) and
2277
+ methodCallMatchesBlanketImpl ( mc , t , impl , traitBound , _, f )
2278
+ }
2279
+
2280
+ pragma [ nomagic]
2281
+ Function getMethodFromBlanketImpl ( MethodCall mc ) { hasBlanketImpl ( mc , _, _, _, result ) }
2282
+ }
2283
+
2156
2284
/** Gets a method from an `impl` block that matches the method call `mc`. */
2157
2285
pragma [ nomagic]
2158
2286
private Function getMethodFromImpl ( MethodCall mc ) {
@@ -2188,6 +2316,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
2188
2316
// The method comes from an `impl` block targeting the type of the receiver.
2189
2317
result = getMethodFromImpl ( mc )
2190
2318
or
2319
+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2320
+ or
2191
2321
// The type of the receiver is a type parameter and the method comes from a
2192
2322
// trait bound on the type parameter.
2193
2323
result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments