11private import csharp
2+ private import dotnet
23private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
34private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
45private import semmle.code.csharp.frameworks.system.linq.Expressions
@@ -8,7 +9,7 @@ private import CaptureModels
89/**
910 * Holds if `t` is a subtype (reflexive/transitive) of `IEnumerable<T>`, where `T` = `tp`.
1011 */
11- private predicate isGenericCollectionType ( ValueOrRefType t , TypeParameter tp ) {
12+ private predicate genericCollectionType ( ValueOrRefType t , TypeParameter tp ) {
1213 exists ( ConstructedGeneric t2 |
1314 t2 = t .getABaseType * ( ) and
1415 t2 .getUnboundDeclaration ( ) instanceof
@@ -18,118 +19,213 @@ private predicate isGenericCollectionType(ValueOrRefType t, TypeParameter tp) {
1819}
1920
2021/**
21- * A class of callables that are relevant generating summaries for based
22- * on the Theorems for Free approach.
22+ * Holds if `tp` is a type parameter of `generic`.
2323 */
24- class TheoremTargetApi extends Specific:: TargetApiSpecific {
25- TheoremTargetApi ( ) { Specific:: isRelevantForTheoremModels ( this ) }
26-
27- private predicate isClassTypeParameter ( TypeParameter t ) {
28- t = this .getDeclaringType ( ) .( UnboundGeneric ) .getATypeParameter ( )
29- }
24+ private predicate unboundGeneric ( UnboundGeneric generic , TypeParameter tp ) {
25+ tp = generic .getATypeParameter ( )
26+ }
3027
31- bindingset [ t]
32- private string getAccess ( TypeParameter t ) {
33- exists ( string access |
34- if isGenericCollectionType ( this .getDeclaringType ( ) , t )
35- then access = ".Element"
36- else access = ".SyntheticField[ArgType" + t .getIndex ( ) + "]"
37- |
38- result = Specific:: qualifierString ( ) + access
39- )
40- }
28+ /**
29+ * Holds if `tp` is a type parameter of the immediate type declaring `callable`.
30+ */
31+ private predicate classTypeParameter ( DotNet:: Callable callable , TypeParameter tp ) {
32+ unboundGeneric ( callable .getDeclaringType ( ) , tp )
33+ }
4134
42- bindingset [ t]
43- private string getReturnAccess ( TypeParameter t ) {
44- exists ( string access |
45- (
46- if isGenericCollectionType ( this .getReturnType ( ) , t )
47- then access = ".Element"
48- else access = ""
49- ) and
50- result = "ReturnValue" + access
51- )
52- }
35+ /**
36+ * Holds if `tp` is type parameter of `callable` or the type declaring `callable`.
37+ */
38+ private predicate localTypeParameter ( DotNet:: Callable callable , TypeParameter tp ) {
39+ classTypeParameter ( callable , tp ) or unboundGeneric ( callable , tp )
40+ }
5341
54- /**
55- * Holds if `this` returns a value of type `t` or a collection of type `t`.
56- */
57- private predicate returns ( TypeParameter t ) {
58- this .getReturnType ( ) = t or isGenericCollectionType ( this .getReturnType ( ) , t )
59- }
42+ private predicate constructedGeneric ( ConstructedType t , TypeParameter tp ) {
43+ t .getATypeArgument ( ) = tp
44+ }
6045
61- /**
62- * Holds if `this` has a parameter `p`, which is of type `t`
63- * or collection of type `t`.
64- */
65- private predicate parameter ( TypeParameter t , Parameter p ) {
66- p = this .getAParameter ( ) and
46+ /**
47+ * Holds if `callable` has a parameter of type `tp`
48+ * or collection parameterized over type `tp`.
49+ */
50+ private predicate parameter ( DotNet:: Callable callable , string input , TypeParameter tp ) {
51+ exists ( Parameter p |
52+ input = Specific:: parameterAccess ( p ) and
53+ p = callable .getAParameter ( ) and
6754 (
68- // Parameter of type t
69- p .getType ( ) = t
55+ // Parameter of type tp
56+ p .getType ( ) = tp
7057 or
71- // Parameter is a collection of type t
72- isGenericCollectionType ( p .getType ( ) , t )
58+ // Parameter is a collection of type tp
59+ genericCollectionType ( p .getType ( ) , tp )
7360 )
74- }
61+ )
62+ }
7563
76- /**
77- * Gets the string representation of a summary for `this`, where this has a signature like
78- * this : T -> S
79- * where T is type parameter for the class declaring `this`.
80- * Important cases are S = unit (setter) and S = T (both getter and setter).
81- */
82- private string getSetterSummary ( ) {
83- exists ( TypeParameter t , Parameter p |
84- this .isClassTypeParameter ( t ) and
85- this .parameter ( t , p )
86- |
87- result = asValueModel ( this , Specific:: parameterAccess ( p ) , this .getAccess ( t ) )
88- )
89- }
64+ /**
65+ * Gets the string representation of a synthetic field corresponding to `tp`.
66+ */
67+ private string getSyntheticField ( TypeParameter tp ) {
68+ result = ".SyntheticField[ArgType" + tp .getIndex ( ) + "]"
69+ }
9070
91- /**
92- * Gets the string representation of a summary for `this`, where this has a signature like
93- * this : S -> T
94- * where T is type parameter for the class declaring `this`.
95- * Important cases are S = unit (getter) and S = T (both getter and setter).
96- */
97- private string getGetterSummary ( ) {
98- exists ( TypeParameter t |
99- this . isClassTypeParameter ( t ) and
100- this . returns ( t )
101- |
102- result = asValueModel ( this , this . getAccess ( t ) , this . getReturnAccess ( t ) )
103- )
104- }
71+ /**
72+ * Gets a models as data string representation of, how a value of type `tp`
73+ * can be read or stored implicitly in relation to `callable`.
74+ */
75+ private string implicit ( DotNet :: Callable callable , TypeParameter tp ) {
76+ classTypeParameter ( callable , tp ) and
77+ exists ( string access |
78+ if genericCollectionType ( callable . getDeclaringType ( ) , tp )
79+ then access = ".Element"
80+ else access = getSyntheticField ( tp )
81+ |
82+ result = Specific :: qualifierString ( ) + access
83+ )
84+ }
10585
106- /**
107- * Gets the string representation of a summary for `this`, where this has a signature like
108- * this : (T -> V1) -> V2
109- * where T is type parameter for the class declaring `this`.
110- */
111- private string getApplySummary ( ) {
112- exists ( TypeParameter t , Parameter p1 , Parameter p2 |
113- this .isClassTypeParameter ( t ) and
114- p1 = this .getAParameter ( ) and
115- p2 = p1 .getType ( ) .( SystemLinqExpressions:: DelegateExtType ) .getDelegateType ( ) .getAParameter ( ) and
116- p2 .getType ( ) = t
117- |
118- result =
119- asValueModel ( this , this .getAccess ( t ) ,
120- Specific:: parameterAccess ( p1 ) + ".Parameter[" + p2 .getPosition ( ) + "]" )
121- )
122- }
86+ /**
87+ * Holds if `callable` has a delegate type parameter `tp` at parameter position `position`.
88+ */
89+ private predicate delegate ( DotNet:: Callable callable , DelegateType dt , int position ) {
90+ exists ( Parameter p |
91+ p = callable .getAParameter ( ) and
92+ dt = p .getType ( ) .( SystemLinqExpressions:: DelegateExtType ) .getDelegateType ( ) and
93+ position = p .getPosition ( )
94+ )
95+ }
96+
97+ /**
98+ * Gets models as data input/output access relative to the type parameter `tp` in the
99+ * type `t` in the scope of `callable`.
100+ *
101+ * Note: This predicate has to be inlined as `callable` is not related to `return` or `tp`
102+ * in every disjunction.
103+ */
104+ pragma [ inline]
105+ private string getAccess ( DotNet:: Callable callable , Type return , TypeParameter tp ) {
106+ return = tp and result = ""
107+ or
108+ genericCollectionType ( return , tp ) and result = ".Element"
109+ or
110+ not genericCollectionType ( return , tp ) and
111+ (
112+ constructedGeneric ( return , tp )
113+ or
114+ callable .getDeclaringType ( ) = return and unboundGeneric ( return , tp )
115+ ) and
116+ result = getSyntheticField ( tp )
117+ }
118+
119+ /**
120+ * Holds if `input` is a models as data string representation of, how a value of type `tp`
121+ * (or a generic parameterized over `tp`) can be generated by a delegate parameter of `callable`.
122+ */
123+ private predicate source ( DotNet:: Callable callable , string input , TypeParameter tp ) {
124+ exists ( DelegateType dt , int position , Type return , string access |
125+ delegate ( callable , dt , position ) and
126+ return = dt .getReturnType ( ) and
127+ access = getAccess ( callable , return , tp ) and
128+ input = "Argument[" + position + "].ReturnValue" + access
129+ )
130+ }
131+
132+ /**
133+ * Holds if `input` is a models as data string representation of, how a
134+ * value of type `tp` (or a generic parameterized over `tp`)
135+ * can be provided as input to `callable`.
136+ * This includes
137+ * (1) The implicit synthetic field(s) of the declaring type of `callable`.
138+ * (2) The parameters of `callable`.
139+ * (3) Any delegate parameters of `callable`.
140+ */
141+ private predicate input ( DotNet:: Callable callable , string input , TypeParameter tp ) {
142+ input = implicit ( callable , tp )
143+ or
144+ parameter ( callable , input , tp )
145+ or
146+ source ( callable , input , tp )
147+ }
148+
149+ /**
150+ * Holds if `callable` returns a value of type `tp` (or a generic parameterized over `tp`) and `output`
151+ * is a models as data string representation of, how data is routed to the return.
152+ */
153+ private predicate returns ( DotNet:: Callable callable , TypeParameter tp , string output ) {
154+ exists ( Type return , string access | return = callable .getReturnType ( ) |
155+ access = getAccess ( callable , return , tp ) and
156+ output = "ReturnValue" + access
157+ )
158+ }
159+
160+ /**
161+ * Holds if `callable` has a delegate parameter that accepts a value of type `tp`
162+ * and `output` is the models as data string representation of, how data is routed to
163+ * the delegate parameter.
164+ */
165+ private predicate sink ( DotNet:: Callable callable , TypeParameter tp , string output ) {
166+ exists ( DelegateType dt , int position , Type t , Parameter p |
167+ delegate ( callable , dt , position ) and
168+ p = dt .getAParameter ( ) and
169+ t = p .getType ( ) and
170+ t = tp and
171+ output = "Argument[" + position + "]" + ".Parameter[" + p .getPosition ( ) + "]"
172+ )
173+ }
174+
175+ /**
176+ * Holds if `output` is a models as data string representation of, how values of type `tp`
177+ * (or generics parameterized over `tp`) can be routed.
178+ * This includes
179+ * (1) The implicit synthetic field(s) of the declaring type of `callable`.
180+ * (2) The return of `callable`.
181+ * (3) Any delegate parameters of `callable`.
182+ */
183+ private predicate output ( DotNet:: Callable callable , TypeParameter tp , string output ) {
184+ output = implicit ( callable , tp )
185+ or
186+ returns ( callable , tp , output )
187+ or
188+ sink ( callable , tp , output )
189+ }
190+
191+ /**
192+ * A class of callables that are relevant generating summaries for based
193+ * on the Theorems for Free approach.
194+ */
195+ class TheoremTargetApi extends Specific:: TargetApiSpecific {
196+ TheoremTargetApi ( ) { Specific:: isRelevantForTheoremModels ( this ) }
123197
124198 /**
125- * Gets the string representation of all summaries based on the Theorems for Free approach.
199+ * Gets the string representation of all type based summaries inspired by
200+ * the Theorems for Free approach.
201+ *
202+ * Basic example signatures could be
203+ * this : T -> \alpha
204+ * this : \beta -> T
205+ * where T is type parameter on `this` or on the declaring type of `this`.
206+ *
207+ * Important special cases are \alpha = unit (setter),
208+ * \alpha = T (getter, setter and id) and \beta = unit (getter).
209+ *
210+ * Complex example signatures could be
211+ * this : (T -> S) -> S
212+ * this : S1 x (S1 -> S2) -> S2
213+ * where T is type parameter of the class declaring `this` and S, S1 and S2 are type parameters
214+ * of `this`.
126215 */
127216 string getSummaries ( ) {
128- result = [ this .getSetterSummary ( ) , this .getGetterSummary ( ) , this .getApplySummary ( ) ]
217+ exists ( TypeParameter tp , string input , string output |
218+ localTypeParameter ( this , tp ) and
219+ input ( this , input , tp ) and
220+ output ( this , tp , output ) and
221+ input != output
222+ |
223+ result = asValueModel ( this , input , output )
224+ )
129225 }
130226}
131227
132228/**
133- * Returns the Theorems for Free summaries for `api`.
229+ * Returns the Theorems for Free inspired typed based summaries for `api`.
134230 */
135231string captureFlow ( TheoremTargetApi api ) { result = api .getSummaries ( ) }
0 commit comments