@@ -1643,6 +1643,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
16431643 methodCandidate ( type , name , arity , impl )
16441644}
16451645
1646+ /**
1647+ * Holds if `mc` has `rootType` as the root type of the reciever and the target
1648+ * method is named `name` and has arity `arity`
1649+ */
16461650pragma [ nomagic]
16471651private predicate isMethodCall ( MethodCall mc , Type rootType , string name , int arity ) {
16481652 rootType = mc .getTypeAt ( TypePath:: nil ( ) ) and
@@ -1841,6 +1845,140 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
18411845 else any ( )
18421846}
18431847
1848+ private module BlanketImplementation {
1849+ /**
1850+ * Holds if `impl` is a blanket implementation, that is, an implementation of a
1851+ * trait for a type parameter.
1852+ */
1853+ private TypeParamItemNode getBlanketImplementationTypeParam ( Impl impl ) {
1854+ result = impl .( ImplItemNode ) .resolveSelfTy ( ) and
1855+ result = impl .getGenericParamList ( ) .getAGenericParam ( ) and
1856+ not exists ( impl .getAttributeMacroExpansion ( ) )
1857+ }
1858+
1859+ predicate isBlanketImplementation ( Impl impl ) { exists ( getBlanketImplementationTypeParam ( impl ) ) }
1860+
1861+ private Impl getPotentialDuplicated ( string fileName , string traitName , int arity , string tpName ) {
1862+ tpName = getBlanketImplementationTypeParam ( result ) .getName ( ) and
1863+ fileName = result .getLocation ( ) .getFile ( ) .getBaseName ( ) and
1864+ traitName = result .( ImplItemNode ) .resolveTraitTy ( ) .getName ( ) and
1865+ arity = result .( ImplItemNode ) .resolveTraitTy ( ) .( Trait ) .getNumberOfGenericParams ( )
1866+ }
1867+
1868+ /**
1869+ * Holds if `impl1` and `impl2` are duplicates and `impl2` is more "canonical"
1870+ * than `impl1`.
1871+ */
1872+ predicate duplicatedImpl ( Impl impl1 , Impl impl2 ) {
1873+ exists ( string fileName , string traitName , int arity , string tpName |
1874+ impl1 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
1875+ impl2 = getPotentialDuplicated ( fileName , traitName , arity , tpName ) and
1876+ impl1 .getLocation ( ) .getFile ( ) .getAbsolutePath ( ) <
1877+ impl2 .getLocation ( ) .getFile ( ) .getAbsolutePath ( )
1878+ )
1879+ }
1880+
1881+ predicate hasNoDuplicates ( Impl impl ) {
1882+ not duplicatedImpl ( impl , _) and isBlanketImplementation ( impl )
1883+ }
1884+
1885+ /**
1886+ * We currently consider blanket implementations to be in scope "globally",
1887+ * even though they actually need to be imported to be used. One downside of
1888+ * this is that the libraries included in the database can often occur several
1889+ * times for different library versions. This causes the same blanket
1890+ * implementations to exist multiple times, and these add no useful
1891+ * information.
1892+ *
1893+ * We detect these duplicates based on some files heuristic (same trait name,
1894+ * file name, etc.). For these duplicates we select the one with the greatest
1895+ * file name (which usually is also the one with the greatest library version
1896+ * in the path)
1897+ */
1898+ Impl getCanonicalImpl ( Impl impl ) {
1899+ result =
1900+ max ( Impl impl0 , Location l |
1901+ duplicatedImpl ( impl , impl0 ) and l = impl0 .getLocation ( )
1902+ |
1903+ impl0 order by l .getFile ( ) .getAbsolutePath ( ) , l .getStartLine ( )
1904+ )
1905+ or
1906+ hasNoDuplicates ( impl ) and result = impl
1907+ }
1908+
1909+ predicate isCanonicalBlanketImplementation ( Impl impl ) { impl = getCanonicalImpl ( impl ) }
1910+
1911+ /**
1912+ * Holds if `impl` is a blanket implementation for a type parameter and the type
1913+ * parameter must implement `trait`.
1914+ */
1915+ private predicate blanketImplementationTraitBound ( Impl impl , Trait t ) {
1916+ t =
1917+ rank [ 1 ] ( Trait trait , int i |
1918+ trait = getBlanketImplementationTypeParam ( impl ) .resolveBound ( i ) and
1919+ // Exclude traits that are "trivial" in the sense that they are known to
1920+ // not narrow things down very much.
1921+ not trait .getName ( ) .getText ( ) =
1922+ [
1923+ "Sized" , "Clone" , "Fn" , "FnOnce" , "FnMut" ,
1924+ // The auto traits
1925+ "Send" , "Sync" , "Unpin" , "UnwindSafe" , "RefUnwindSafe"
1926+ ]
1927+ |
1928+ trait order by i
1929+ )
1930+ }
1931+
1932+ private predicate blanketImplementationMethod (
1933+ Impl impl , Trait trait , string name , int arity , Function f
1934+ ) {
1935+ isCanonicalBlanketImplementation ( impl ) and
1936+ blanketImplementationTraitBound ( impl , trait ) and
1937+ f .getParamList ( ) .hasSelfParam ( ) and
1938+ arity = f .getParamList ( ) .getNumberOfParams ( ) and
1939+ // Make this stronger and document
1940+ (
1941+ f = impl .( ImplItemNode ) .getAssocItem ( name )
1942+ or
1943+ f = impl .( ImplItemNode ) .resolveTraitTy ( ) .getAssocItem ( name ) and
1944+ not f = impl .( ImplItemNode ) .getAssocItem ( name )
1945+ ) and
1946+ not getBlanketImplementationTypeParam ( impl ) .resolveABound ( ) .( TraitItemNode ) .getASuccessor ( name ) =
1947+ f
1948+ }
1949+
1950+ predicate methodCallMatchesBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
1951+ // Only check method calls where we have ruled out inherent method targets.
1952+ // Ideally we would also check if non-blanket method targets have been ruled
1953+ // out.
1954+ methodCallHasNoInherentTarget ( mc ) and
1955+ exists ( string name , int arity |
1956+ isMethodCall ( mc , t , name , arity ) and
1957+ blanketImplementationMethod ( impl , trait , name , arity , f )
1958+ )
1959+ }
1960+
1961+ module SatisfiesConstraintInput implements SatisfiesConstraintInputSig< MethodCall > {
1962+ pragma [ nomagic]
1963+ predicate relevantConstraint ( MethodCall mc , Type constraint ) {
1964+ methodCallMatchesBlanketImpl ( mc , _, _, constraint .( TraitType ) .getTrait ( ) , _)
1965+ }
1966+
1967+ predicate useUniversalConditions ( ) { none ( ) }
1968+ }
1969+
1970+ predicate getBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
1971+ SatisfiesConstraint< MethodCall , SatisfiesConstraintInput > :: satisfiesConstraintType ( mc ,
1972+ TTrait ( trait ) , _, _) and
1973+ methodCallMatchesBlanketImpl ( mc , t , impl , trait , f )
1974+ }
1975+
1976+ pragma [ nomagic]
1977+ Function getMethodFromBlanketImpl ( MethodCall mc ) {
1978+ BlanketImplementation:: getBlanketImpl ( mc , _, _, _, result )
1979+ }
1980+ }
1981+
18441982/** Gets a method from an `impl` block that matches the method call `mc`. */
18451983pragma [ nomagic]
18461984private Function getMethodFromImpl ( MethodCall mc ) {
@@ -1876,6 +2014,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
18762014 // The method comes from an `impl` block targeting the type of the receiver.
18772015 result = getMethodFromImpl ( mc )
18782016 or
2017+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2018+ or
18792019 // The type of the receiver is a type parameter and the method comes from a
18802020 // trait bound on the type parameter.
18812021 result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments