@@ -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,140 @@ 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
+ 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
+
1844
1982
/** Gets a method from an `impl` block that matches the method call `mc`. */
1845
1983
pragma [ nomagic]
1846
1984
private Function getMethodFromImpl ( MethodCall mc ) {
@@ -1876,6 +2014,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
1876
2014
// The method comes from an `impl` block targeting the type of the receiver.
1877
2015
result = getMethodFromImpl ( mc )
1878
2016
or
2017
+ result = BlanketImplementation:: getMethodFromBlanketImpl ( mc )
2018
+ or
1879
2019
// The type of the receiver is a type parameter and the method comes from a
1880
2020
// trait bound on the type parameter.
1881
2021
result = getTypeParameterMethod ( mc .getTypeAt ( TypePath:: nil ( ) ) , mc .getMethodName ( ) )
0 commit comments