@@ -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+ */
19181922pragma [ nomagic]
19191923private 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`. */
21572310pragma [ nomagic]
21582311private 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 ( ) )
0 commit comments