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