@@ -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,142 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
21412145 else any ( )
21422146}
21432147
2148+ private module BlanketImplementation {
2149+ /**
2150+ * Holds if `impl` is a blanket implementation, that is, an implementation of a
2151+ * trait for a type parameter.
2152+ */
2153+ private TypeParamItemNode getBlanketImplementationTypeParam ( Impl impl ) {
2154+ result = impl .( ImplItemNode ) .resolveSelfTy ( ) and
2155+ result = impl .getGenericParamList ( ) .getAGenericParam ( ) and
2156+ not exists ( impl .getAttributeMacroExpansion ( ) )
2157+ }
2158+
2159+ predicate isBlanketImplementation ( Impl impl ) { exists ( getBlanketImplementationTypeParam ( impl ) ) }
2160+
2161+ private Impl getPotentialDuplicated ( string fileName , string traitName , int arity , string tpName ) {
2162+ tpName = getBlanketImplementationTypeParam ( result ) .getName ( ) and
2163+ fileName = result .getLocation ( ) .getFile ( ) .getBaseName ( ) and
2164+ traitName = result .( ImplItemNode ) .resolveTraitTy ( ) .getName ( ) and
2165+ arity = result .( ImplItemNode ) .resolveTraitTy ( ) .( Trait ) .getNumberOfGenericParams ( )
2166+ }
2167+
2168+ /**
2169+ * Holds if `impl1` and `impl2` are duplicates and `impl2` is more "canonical"
2170+ * than `impl1`.
2171+ */
2172+ predicate duplicatedImpl ( Impl impl1 , Impl impl2 ) {
2173+ exists ( string fileName , string traitName , int arity , string tpName |
2174+ impl1 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2175+ impl2 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2176+ impl1 .getLocation ( ) .getFile ( ) .getAbsolutePath ( ) <
2177+ impl2 .getLocation ( ) .getFile ( ) .getAbsolutePath ( )
2178+ )
2179+ }
2180+
2181+ predicate hasNoDuplicates ( Impl impl ) {
2182+ not duplicatedImpl ( impl , _) and isBlanketImplementation ( impl )
2183+ }
2184+
2185+ /**
2186+ * We currently consider blanket implementations to be in scope "globally",
2187+ * even though they actually need to be imported to be used. One downside of
2188+ * this is that the libraries included in the database can often occur several
2189+ * times for different library versions. This causes the same blanket
2190+ * implementations to exist multiple times, and these add no useful
2191+ * information.
2192+ *
2193+ * We detect these duplicates based on some files heuristic (same trait name,
2194+ * file name, etc.). For these duplicates we select the one with the greatest
2195+ * file name (which usually is also the one with the greatest library version
2196+ * in the path)
2197+ */
2198+ Impl getCanonicalImpl ( Impl impl ) {
2199+ result =
2200+ max ( Impl impl0 , Location l |
2201+ duplicatedImpl ( impl , impl0 ) and l = impl0 .getLocation ( )
2202+ |
2203+ impl0 order by l .getFile ( ) .getAbsolutePath ( ) , l .getStartLine ( )
2204+ )
2205+ or
2206+ hasNoDuplicates ( impl ) and result = impl
2207+ }
2208+
2209+ predicate isCanonicalBlanketImplementation ( Impl impl ) { impl = getCanonicalImpl ( impl ) }
2210+
2211+ /**
2212+ * Holds if `impl` is a blanket implementation for a type parameter and the type
2213+ * parameter must implement `trait`.
2214+ */
2215+ private predicate blanketImplementationTraitBound ( Impl impl , Trait t ) {
2216+ t =
2217+ min ( Trait trait , int i |
2218+ trait = getBlanketImplementationTypeParam ( impl ) .resolveBound ( i ) and
2219+ // Exclude traits that are "trivial" in the sense that they are known to
2220+ // not narrow things down very much.
2221+ not trait .getName ( ) .getText ( ) =
2222+ [
2223+ "Sized" , "Clone" , "Fn" , "FnOnce" , "FnMut" ,
2224+ // The auto traits
2225+ "Send" , "Sync" , "Unpin" , "UnwindSafe" , "RefUnwindSafe"
2226+ ]
2227+ |
2228+ trait order by i
2229+ )
2230+ }
2231+
2232+ private predicate blanketImplementationMethod (
2233+ Impl impl , Trait trait , string name , int arity , Function f
2234+ ) {
2235+ isCanonicalBlanketImplementation ( impl ) and
2236+ blanketImplementationTraitBound ( impl , trait ) and
2237+ f .getParamList ( ) .hasSelfParam ( ) and
2238+ arity = f .getParamList ( ) .getNumberOfParams ( ) and
2239+ (
2240+ f = impl .( ImplItemNode ) .getAssocItem ( name )
2241+ or
2242+ // If the the trait has a method with a default implementation, then that
2243+ // target is interesting as well.
2244+ not exists ( impl .( ImplItemNode ) .getAssocItem ( name ) ) and
2245+ f = impl .( ImplItemNode ) .resolveTraitTy ( ) .getAssocItem ( name )
2246+ ) and
2247+ // If the method is already available through one of the trait bounds on the
2248+ // type parameter (because they share a common trait ancestor) then ignore
2249+ // it.
2250+ not getBlanketImplementationTypeParam ( impl ) .resolveABound ( ) .( TraitItemNode ) .getASuccessor ( name ) =
2251+ f
2252+ }
2253+
2254+ predicate methodCallMatchesBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
2255+ // Only check method calls where we have ruled out inherent method targets.
2256+ // Ideally we would also check if non-blanket method targets have been ruled
2257+ // out.
2258+ methodCallHasNoInherentTarget ( mc ) and
2259+ exists ( string name , int arity |
2260+ isMethodCall ( mc , t , name , arity ) and
2261+ blanketImplementationMethod ( impl , trait , name , arity , f )
2262+ )
2263+ }
2264+
2265+ module SatisfiesConstraintInput implements SatisfiesConstraintInputSig< MethodCall > {
2266+ pragma [ nomagic]
2267+ predicate relevantConstraint ( MethodCall mc , Type constraint ) {
2268+ methodCallMatchesBlanketImpl ( mc , _, _, constraint .( TraitType ) .getTrait ( ) , _)
2269+ }
2270+
2271+ predicate useUniversalConditions ( ) { none ( ) }
2272+ }
2273+
2274+ predicate hasBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
2275+ SatisfiesConstraint< MethodCall , SatisfiesConstraintInput > :: satisfiesConstraintType ( mc ,
2276+ TTrait ( trait ) , _, _) and
2277+ methodCallMatchesBlanketImpl ( mc , t , impl , trait , f )
2278+ }
2279+
2280+ pragma [ nomagic]
2281+ Function getMethodFromBlanketImpl ( MethodCall mc ) { hasBlanketImpl ( mc , _, _, _, result ) }
2282+ }
2283+
21442284/** Gets a method from an `impl` block that matches the method call `mc`. */
21452285pragma [ nomagic]
21462286private Function getMethodFromImpl ( MethodCall mc ) {
@@ -2176,6 +2316,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
21762316 // The method comes from an `impl` block targeting the type of the receiver.
21772317 result = getMethodFromImpl ( mc )
21782318 or
2319+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2320+ or
21792321 // The type of the receiver is a type parameter and the method comes from a
21802322 // trait bound on the type parameter.
21812323 result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments