1
1
private import csharp
2
+ private import dotnet
2
3
private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
3
4
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
4
5
private import semmle.code.csharp.frameworks.system.linq.Expressions
@@ -8,7 +9,7 @@ private import CaptureModels
8
9
/**
9
10
* Holds if `t` is a subtype (reflexive/transitive) of `IEnumerable<T>`, where `T` = `tp`.
10
11
*/
11
- private predicate isGenericCollectionType ( ValueOrRefType t , TypeParameter tp ) {
12
+ private predicate genericCollectionType ( ValueOrRefType t , TypeParameter tp ) {
12
13
exists ( ConstructedGeneric t2 |
13
14
t2 = t .getABaseType * ( ) and
14
15
t2 .getUnboundDeclaration ( ) instanceof
@@ -18,118 +19,213 @@ private predicate isGenericCollectionType(ValueOrRefType t, TypeParameter tp) {
18
19
}
19
20
20
21
/**
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`.
23
23
*/
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
+ }
30
27
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
+ }
41
34
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
+ }
53
41
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
+ }
60
45
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
67
54
(
68
- // Parameter of type t
69
- p .getType ( ) = t
55
+ // Parameter of type tp
56
+ p .getType ( ) = tp
70
57
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 )
73
60
)
74
- }
61
+ )
62
+ }
75
63
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
+ }
90
70
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
+ }
105
85
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 ) }
123
197
124
198
/**
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`.
126
215
*/
127
216
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
+ )
129
225
}
130
226
}
131
227
132
228
/**
133
- * Returns the Theorems for Free summaries for `api`.
229
+ * Returns the Theorems for Free inspired typed based summaries for `api`.
134
230
*/
135
231
string captureFlow ( TheoremTargetApi api ) { result = api .getSummaries ( ) }
0 commit comments