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