@@ -1863,6 +1863,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
1863
1863
methodCandidate ( type , name , arity , impl )
1864
1864
}
1865
1865
1866
+ /**
1867
+ * Holds if `mc` has `rootType` as the root type of the reciever and the target
1868
+ * method is named `name` and has arity `arity`
1869
+ */
1866
1870
pragma [ nomagic]
1867
1871
private predicate isMethodCall ( MethodCall mc , Type rootType , string name , int arity ) {
1868
1872
rootType = mc .getTypeAt ( TypePath:: nil ( ) ) and
@@ -2061,6 +2065,142 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
2061
2065
else any ( )
2062
2066
}
2063
2067
2068
+ private module BlanketImplementation {
2069
+ /**
2070
+ * Holds if `impl` is a blanket implementation, that is, an implementation of a
2071
+ * trait for a type parameter.
2072
+ */
2073
+ private TypeParamItemNode getBlanketImplementationTypeParam ( Impl impl ) {
2074
+ result = impl .( ImplItemNode ) .resolveSelfTy ( ) and
2075
+ result = impl .getGenericParamList ( ) .getAGenericParam ( ) and
2076
+ not exists ( impl .getAttributeMacroExpansion ( ) )
2077
+ }
2078
+
2079
+ predicate isBlanketImplementation ( Impl impl ) { exists ( getBlanketImplementationTypeParam ( impl ) ) }
2080
+
2081
+ private Impl getPotentialDuplicated ( string fileName , string traitName , int arity , string tpName ) {
2082
+ tpName = getBlanketImplementationTypeParam ( result ) .getName ( ) and
2083
+ fileName = result .getLocation ( ) .getFile ( ) .getBaseName ( ) and
2084
+ traitName = result .( ImplItemNode ) .resolveTraitTy ( ) .getName ( ) and
2085
+ arity = result .( ImplItemNode ) .resolveTraitTy ( ) .( Trait ) .getNumberOfGenericParams ( )
2086
+ }
2087
+
2088
+ /**
2089
+ * Holds if `impl1` and `impl2` are duplicates and `impl2` is more "canonical"
2090
+ * than `impl1`.
2091
+ */
2092
+ predicate duplicatedImpl ( Impl impl1 , Impl impl2 ) {
2093
+ exists ( string fileName , string traitName , int arity , string tpName |
2094
+ impl1 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2095
+ impl2 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
2096
+ impl1 .getLocation ( ) .getFile ( ) .getAbsolutePath ( ) <
2097
+ impl2 .getLocation ( ) .getFile ( ) .getAbsolutePath ( )
2098
+ )
2099
+ }
2100
+
2101
+ predicate hasNoDuplicates ( Impl impl ) {
2102
+ not duplicatedImpl ( impl , _) and isBlanketImplementation ( impl )
2103
+ }
2104
+
2105
+ /**
2106
+ * We currently consider blanket implementations to be in scope "globally",
2107
+ * even though they actually need to be imported to be used. One downside of
2108
+ * this is that the libraries included in the database can often occur several
2109
+ * times for different library versions. This causes the same blanket
2110
+ * implementations to exist multiple times, and these add no useful
2111
+ * information.
2112
+ *
2113
+ * We detect these duplicates based on some files heuristic (same trait name,
2114
+ * file name, etc.). For these duplicates we select the one with the greatest
2115
+ * file name (which usually is also the one with the greatest library version
2116
+ * in the path)
2117
+ */
2118
+ Impl getCanonicalImpl ( Impl impl ) {
2119
+ result =
2120
+ max ( Impl impl0 , Location l |
2121
+ duplicatedImpl ( impl , impl0 ) and l = impl0 .getLocation ( )
2122
+ |
2123
+ impl0 order by l .getFile ( ) .getAbsolutePath ( ) , l .getStartLine ( )
2124
+ )
2125
+ or
2126
+ hasNoDuplicates ( impl ) and result = impl
2127
+ }
2128
+
2129
+ predicate isCanonicalBlanketImplementation ( Impl impl ) { impl = getCanonicalImpl ( impl ) }
2130
+
2131
+ /**
2132
+ * Holds if `impl` is a blanket implementation for a type parameter and the type
2133
+ * parameter must implement `trait`.
2134
+ */
2135
+ private predicate blanketImplementationTraitBound ( Impl impl , Trait t ) {
2136
+ t =
2137
+ min ( Trait trait , int i |
2138
+ trait = getBlanketImplementationTypeParam ( impl ) .resolveBound ( i ) and
2139
+ // Exclude traits that are "trivial" in the sense that they are known to
2140
+ // not narrow things down very much.
2141
+ not trait .getName ( ) .getText ( ) =
2142
+ [
2143
+ "Sized" , "Clone" , "Fn" , "FnOnce" , "FnMut" ,
2144
+ // The auto traits
2145
+ "Send" , "Sync" , "Unpin" , "UnwindSafe" , "RefUnwindSafe"
2146
+ ]
2147
+ |
2148
+ trait order by i
2149
+ )
2150
+ }
2151
+
2152
+ private predicate blanketImplementationMethod (
2153
+ Impl impl , Trait trait , string name , int arity , Function f
2154
+ ) {
2155
+ isCanonicalBlanketImplementation ( impl ) and
2156
+ blanketImplementationTraitBound ( impl , trait ) and
2157
+ f .getParamList ( ) .hasSelfParam ( ) and
2158
+ arity = f .getParamList ( ) .getNumberOfParams ( ) and
2159
+ (
2160
+ f = impl .( ImplItemNode ) .getAssocItem ( name )
2161
+ or
2162
+ // If the the trait has a method with a default implementation, then that
2163
+ // target is interesting as well.
2164
+ not exists ( impl .( ImplItemNode ) .getAssocItem ( name ) ) and
2165
+ f = impl .( ImplItemNode ) .resolveTraitTy ( ) .getAssocItem ( name )
2166
+ ) and
2167
+ // If the method is already available through one of the trait bounds on the
2168
+ // type parameter (because they share a common trait ancestor) then ignore
2169
+ // it.
2170
+ not getBlanketImplementationTypeParam ( impl ) .resolveABound ( ) .( TraitItemNode ) .getASuccessor ( name ) =
2171
+ f
2172
+ }
2173
+
2174
+ predicate methodCallMatchesBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
2175
+ // Only check method calls where we have ruled out inherent method targets.
2176
+ // Ideally we would also check if non-blanket method targets have been ruled
2177
+ // out.
2178
+ methodCallHasNoInherentTarget ( mc ) and
2179
+ exists ( string name , int arity |
2180
+ isMethodCall ( mc , t , name , arity ) and
2181
+ blanketImplementationMethod ( impl , trait , name , arity , f )
2182
+ )
2183
+ }
2184
+
2185
+ module SatisfiesConstraintInput implements SatisfiesConstraintInputSig< MethodCall > {
2186
+ pragma [ nomagic]
2187
+ predicate relevantConstraint ( MethodCall mc , Type constraint ) {
2188
+ methodCallMatchesBlanketImpl ( mc , _, _, constraint .( TraitType ) .getTrait ( ) , _)
2189
+ }
2190
+
2191
+ predicate useUniversalConditions ( ) { none ( ) }
2192
+ }
2193
+
2194
+ predicate hasBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
2195
+ SatisfiesConstraint< MethodCall , SatisfiesConstraintInput > :: satisfiesConstraintType ( mc ,
2196
+ TTrait ( trait ) , _, _) and
2197
+ methodCallMatchesBlanketImpl ( mc , t , impl , trait , f )
2198
+ }
2199
+
2200
+ pragma [ nomagic]
2201
+ Function getMethodFromBlanketImpl ( MethodCall mc ) { hasBlanketImpl ( mc , _, _, _, result ) }
2202
+ }
2203
+
2064
2204
/** Gets a method from an `impl` block that matches the method call `mc`. */
2065
2205
pragma [ nomagic]
2066
2206
private Function getMethodFromImpl ( MethodCall mc ) {
@@ -2096,6 +2236,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
2096
2236
// The method comes from an `impl` block targeting the type of the receiver.
2097
2237
result = getMethodFromImpl ( mc )
2098
2238
or
2239
+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2240
+ or
2099
2241
// The type of the receiver is a type parameter and the method comes from a
2100
2242
// trait bound on the type parameter.
2101
2243
result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments