@@ -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 receiver 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,130 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
21532157 else any ( )
21542158}
21552159
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+
21562284/** Gets a method from an `impl` block that matches the method call `mc`. */
21572285pragma [ nomagic]
21582286private Function getMethodFromImpl ( MethodCall mc ) {
@@ -2188,6 +2316,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
21882316 // The method comes from an `impl` block targeting the type of the receiver.
21892317 result = getMethodFromImpl ( mc )
21902318 or
2319+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2320+ or
21912321 // The type of the receiver is a type parameter and the method comes from a
21922322 // trait bound on the type parameter.
21932323 result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments