@@ -1643,6 +1643,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
1643
1643
methodCandidate ( type , name , arity , impl )
1644
1644
}
1645
1645
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
+ */
1646
1650
pragma [ nomagic]
1647
1651
private predicate isMethodCall ( MethodCall mc , Type rootType , string name , int arity ) {
1648
1652
rootType = mc .getTypeAt ( TypePath:: nil ( ) ) and
@@ -1841,6 +1845,142 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
1841
1845
else any ( )
1842
1846
}
1843
1847
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
+ min ( 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
+ (
1940
+ f = impl .( ImplItemNode ) .getAssocItem ( name )
1941
+ or
1942
+ // If the the trait has a method with a default implementation, then that
1943
+ // target is interesting as well.
1944
+ not exists ( impl .( ImplItemNode ) .getAssocItem ( name ) ) and
1945
+ f = impl .( ImplItemNode ) .resolveTraitTy ( ) .getAssocItem ( name )
1946
+ ) and
1947
+ // If the method is already available through one of the trait bounds on the
1948
+ // type parameter (because they share a common trait ancestor) then ignore
1949
+ // it.
1950
+ not getBlanketImplementationTypeParam ( impl ) .resolveABound ( ) .( TraitItemNode ) .getASuccessor ( name ) =
1951
+ f
1952
+ }
1953
+
1954
+ predicate methodCallMatchesBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
1955
+ // Only check method calls where we have ruled out inherent method targets.
1956
+ // Ideally we would also check if non-blanket method targets have been ruled
1957
+ // out.
1958
+ methodCallHasNoInherentTarget ( mc ) and
1959
+ exists ( string name , int arity |
1960
+ isMethodCall ( mc , t , name , arity ) and
1961
+ blanketImplementationMethod ( impl , trait , name , arity , f )
1962
+ )
1963
+ }
1964
+
1965
+ module SatisfiesConstraintInput implements SatisfiesConstraintInputSig< MethodCall > {
1966
+ pragma [ nomagic]
1967
+ predicate relevantConstraint ( MethodCall mc , Type constraint ) {
1968
+ methodCallMatchesBlanketImpl ( mc , _, _, constraint .( TraitType ) .getTrait ( ) , _)
1969
+ }
1970
+
1971
+ predicate useUniversalConditions ( ) { none ( ) }
1972
+ }
1973
+
1974
+ predicate hasBlanketImpl ( MethodCall mc , Type t , Impl impl , Trait trait , Function f ) {
1975
+ SatisfiesConstraint< MethodCall , SatisfiesConstraintInput > :: satisfiesConstraintType ( mc ,
1976
+ TTrait ( trait ) , _, _) and
1977
+ methodCallMatchesBlanketImpl ( mc , t , impl , trait , f )
1978
+ }
1979
+
1980
+ pragma [ nomagic]
1981
+ Function getMethodFromBlanketImpl ( MethodCall mc ) { hasBlanketImpl ( mc , _, _, _, result ) }
1982
+ }
1983
+
1844
1984
/** Gets a method from an `impl` block that matches the method call `mc`. */
1845
1985
pragma [ nomagic]
1846
1986
private Function getMethodFromImpl ( MethodCall mc ) {
@@ -1876,6 +2016,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
1876
2016
// The method comes from an `impl` block targeting the type of the receiver.
1877
2017
result = getMethodFromImpl ( mc )
1878
2018
or
2019
+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2020
+ or
1879
2021
// The type of the receiver is a type parameter and the method comes from a
1880
2022
// trait bound on the type parameter.
1881
2023
result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments