Skip to content

Commit e983919

Browse files
authored
Merge pull request github#3484 from calumgrant/cs/index-initializers
C#: Extract indexed initializers correctly
2 parents 9708073 + da6c37d commit e983919

File tree

7 files changed

+156
-14
lines changed

7 files changed

+156
-14
lines changed

change-notes/1.25/analysis-csharp.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ The following changes in version 1.25 affect C# analysis in all applications.
1818

1919
## Changes to code extraction
2020

21+
* Index initializers, of the form `{ [1] = "one" }`, are extracted correctly. Previously, the kind of the
22+
expression was incorrect, and the index was not extracted.
23+
2124
## Changes to libraries
2225

2326
* The class `UnboundGeneric` has been refined to only be those declarations that actually
24-
have type parameters. This means that non-generic nested types inside construced types,
27+
have type parameters. This means that non-generic nested types inside constructed types,
2528
such as `A<int>.B`, no longer are considered unbound generics. (Such nested types do,
2629
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
2730
* The data-flow library has been improved, which affects most security queries by potentially

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ static ExprKind AccessKind(Context cx, ISymbol symbol)
1818
return ExprKind.FIELD_ACCESS;
1919

2020
case SymbolKind.Property:
21-
return ExprKind.PROPERTY_ACCESS;
21+
return symbol is IPropertySymbol prop && prop.IsIndexer ?
22+
ExprKind.INDEXER_ACCESS : ExprKind.PROPERTY_ACCESS;
2223

2324
case SymbolKind.Event:
2425
return ExprKind.EVENT_ACCESS;

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,22 @@ protected override void PopulateExpression(TextWriter trapFile)
7474
CreateFromNode(new ExpressionNodeInfo(cx, assignment.Right, assignmentEntity, 0));
7575

7676
var target = cx.GetSymbolInfo(assignment.Left);
77-
if (target.Symbol == null)
78-
{
79-
cx.ModelError(assignment, "Unknown object initializer");
80-
new Unknown(new ExpressionNodeInfo(cx, assignment.Left, assignmentEntity, 1));
81-
}
82-
else
83-
{
77+
78+
// If the target is null, then assume that this is an array initializer (of the form `[...] = ...`)
79+
80+
Expression access = target.Symbol is null ?
81+
new Expression(new ExpressionNodeInfo(cx, assignment.Left, assignmentEntity, 1).SetKind(ExprKind.ARRAY_ACCESS)) :
8482
Access.Create(new ExpressionNodeInfo(cx, assignment.Left, assignmentEntity, 1), target.Symbol, false, cx.CreateEntity(target.Symbol));
83+
84+
if (assignment.Left is ImplicitElementAccessSyntax iea)
85+
{
86+
// An array/indexer initializer of the form `[...] = ...`
87+
88+
int indexChild = 0;
89+
foreach (var arg in iea.ArgumentList.Arguments)
90+
{
91+
Expression.Create(cx, arg.Expression, access, indexChild++);
92+
}
8593
}
8694
}
8795
else
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
assignedMembers
2+
| csharp6.cs:12:16:12:20 | Value | csharp6.cs:15:9:15:10 | 20 |
3+
| csharp6.cs:57:40:57:54 | DictionaryField | csharp6.cs:73:31:73:72 | { ..., ... } |
4+
| csharp6.cs:58:40:58:57 | DictionaryProperty | csharp6.cs:74:34:74:76 | { ..., ... } |
5+
| csharp6.cs:59:25:59:34 | ArrayField | csharp6.cs:75:26:75:54 | { ..., ... } |
6+
| csharp6.cs:60:25:60:37 | ArrayProperty | csharp6.cs:77:29:77:56 | { ..., ... } |
7+
| csharp6.cs:61:26:61:36 | ArrayField2 | csharp6.cs:76:27:76:56 | { ..., ... } |
8+
| csharp6.cs:62:26:62:39 | ArrayProperty2 | csharp6.cs:78:30:78:59 | { ..., ... } |
9+
indexerCalls
10+
| csharp6.cs:32:68:32:70 | access to indexer | 0 | csharp6.cs:32:69:32:69 | 2 |
11+
| csharp6.cs:32:68:32:73 | access to indexer | 0 | csharp6.cs:32:72:32:72 | 1 |
12+
| csharp6.cs:68:52:68:54 | access to indexer | 0 | csharp6.cs:68:53:68:53 | 0 |
13+
| csharp6.cs:68:52:68:54 | access to indexer | 1 | csharp6.cs:68:58:68:63 | "Zero" |
14+
| csharp6.cs:68:66:68:68 | access to indexer | 0 | csharp6.cs:68:67:68:67 | 1 |
15+
| csharp6.cs:68:66:68:68 | access to indexer | 1 | csharp6.cs:68:72:68:76 | "One" |
16+
| csharp6.cs:68:79:68:81 | access to indexer | 0 | csharp6.cs:68:80:68:80 | 2 |
17+
| csharp6.cs:68:79:68:81 | access to indexer | 1 | csharp6.cs:68:85:68:89 | "Two" |
18+
| csharp6.cs:73:33:73:35 | access to indexer | 0 | csharp6.cs:73:34:73:34 | 0 |
19+
| csharp6.cs:73:33:73:35 | access to indexer | 1 | csharp6.cs:73:39:73:44 | "Zero" |
20+
| csharp6.cs:73:47:73:49 | access to indexer | 0 | csharp6.cs:73:48:73:48 | 1 |
21+
| csharp6.cs:73:47:73:49 | access to indexer | 1 | csharp6.cs:73:53:73:57 | "One" |
22+
| csharp6.cs:73:60:73:62 | access to indexer | 0 | csharp6.cs:73:61:73:61 | 2 |
23+
| csharp6.cs:73:60:73:62 | access to indexer | 1 | csharp6.cs:73:66:73:70 | "Two" |
24+
| csharp6.cs:74:36:74:38 | access to indexer | 0 | csharp6.cs:74:37:74:37 | 3 |
25+
| csharp6.cs:74:36:74:38 | access to indexer | 1 | csharp6.cs:74:42:74:48 | "Three" |
26+
| csharp6.cs:74:51:74:53 | access to indexer | 0 | csharp6.cs:74:52:74:52 | 2 |
27+
| csharp6.cs:74:51:74:53 | access to indexer | 1 | csharp6.cs:74:57:74:61 | "Two" |
28+
| csharp6.cs:74:64:74:66 | access to indexer | 0 | csharp6.cs:74:65:74:65 | 1 |
29+
| csharp6.cs:74:64:74:66 | access to indexer | 1 | csharp6.cs:74:70:74:74 | "One" |
30+
elementAssignments
31+
| csharp6.cs:68:52:68:54 | access to indexer | csharp6.cs:68:52:68:63 | ... = ... | 0 | csharp6.cs:68:53:68:53 | 0 |
32+
| csharp6.cs:68:66:68:68 | access to indexer | csharp6.cs:68:66:68:76 | ... = ... | 0 | csharp6.cs:68:67:68:67 | 1 |
33+
| csharp6.cs:68:79:68:81 | access to indexer | csharp6.cs:68:79:68:89 | ... = ... | 0 | csharp6.cs:68:80:68:80 | 2 |
34+
| csharp6.cs:73:33:73:35 | access to indexer | csharp6.cs:73:33:73:44 | ... = ... | 0 | csharp6.cs:73:34:73:34 | 0 |
35+
| csharp6.cs:73:47:73:49 | access to indexer | csharp6.cs:73:47:73:57 | ... = ... | 0 | csharp6.cs:73:48:73:48 | 1 |
36+
| csharp6.cs:73:60:73:62 | access to indexer | csharp6.cs:73:60:73:70 | ... = ... | 0 | csharp6.cs:73:61:73:61 | 2 |
37+
| csharp6.cs:74:36:74:38 | access to indexer | csharp6.cs:74:36:74:48 | ... = ... | 0 | csharp6.cs:74:37:74:37 | 3 |
38+
| csharp6.cs:74:51:74:53 | access to indexer | csharp6.cs:74:51:74:61 | ... = ... | 0 | csharp6.cs:74:52:74:52 | 2 |
39+
| csharp6.cs:74:64:74:66 | access to indexer | csharp6.cs:74:64:74:74 | ... = ... | 0 | csharp6.cs:74:65:74:65 | 1 |
40+
| csharp6.cs:75:28:75:30 | access to array element | csharp6.cs:75:28:75:39 | ... = ... | 0 | csharp6.cs:75:29:75:29 | 0 |
41+
| csharp6.cs:75:42:75:44 | access to array element | csharp6.cs:75:42:75:52 | ... = ... | 0 | csharp6.cs:75:43:75:43 | 1 |
42+
| csharp6.cs:76:29:76:34 | access to array element | csharp6.cs:76:29:76:40 | ... = ... | 0 | csharp6.cs:76:30:76:30 | 0 |
43+
| csharp6.cs:76:29:76:34 | access to array element | csharp6.cs:76:29:76:40 | ... = ... | 1 | csharp6.cs:76:33:76:33 | 1 |
44+
| csharp6.cs:76:43:76:48 | access to array element | csharp6.cs:76:43:76:54 | ... = ... | 0 | csharp6.cs:76:44:76:44 | 1 |
45+
| csharp6.cs:76:43:76:48 | access to array element | csharp6.cs:76:43:76:54 | ... = ... | 1 | csharp6.cs:76:47:76:47 | 0 |
46+
| csharp6.cs:77:31:77:33 | access to array element | csharp6.cs:77:31:77:41 | ... = ... | 0 | csharp6.cs:77:32:77:32 | 1 |
47+
| csharp6.cs:77:44:77:46 | access to array element | csharp6.cs:77:44:77:54 | ... = ... | 0 | csharp6.cs:77:45:77:45 | 2 |
48+
| csharp6.cs:78:32:78:37 | access to array element | csharp6.cs:78:32:78:43 | ... = ... | 0 | csharp6.cs:78:33:78:33 | 0 |
49+
| csharp6.cs:78:32:78:37 | access to array element | csharp6.cs:78:32:78:43 | ... = ... | 1 | csharp6.cs:78:36:78:36 | 1 |
50+
| csharp6.cs:78:46:78:51 | access to array element | csharp6.cs:78:46:78:57 | ... = ... | 0 | csharp6.cs:78:47:78:47 | 1 |
51+
| csharp6.cs:78:46:78:51 | access to array element | csharp6.cs:78:46:78:57 | ... = ... | 1 | csharp6.cs:78:50:78:50 | 0 |
52+
arrayQualifiers
53+
| csharp6.cs:32:68:32:70 | access to indexer | csharp6.cs:32:38:32:66 | object creation of type Dictionary<Int32,String> |
54+
| csharp6.cs:32:68:32:73 | access to indexer | csharp6.cs:32:68:32:70 | access to indexer |
55+
initializers
56+
| csharp6.cs:68:50:68:91 | { ..., ... } | 0 | csharp6.cs:68:52:68:63 | ... = ... |
57+
| csharp6.cs:68:50:68:91 | { ..., ... } | 1 | csharp6.cs:68:66:68:76 | ... = ... |
58+
| csharp6.cs:68:50:68:91 | { ..., ... } | 2 | csharp6.cs:68:79:68:89 | ... = ... |
59+
| csharp6.cs:72:9:79:9 | { ..., ... } | 0 | csharp6.cs:73:13:73:72 | ... = ... |
60+
| csharp6.cs:72:9:79:9 | { ..., ... } | 1 | csharp6.cs:74:13:74:76 | ... = ... |
61+
| csharp6.cs:72:9:79:9 | { ..., ... } | 2 | csharp6.cs:75:13:75:54 | ... = ... |
62+
| csharp6.cs:72:9:79:9 | { ..., ... } | 3 | csharp6.cs:76:13:76:56 | ... = ... |
63+
| csharp6.cs:72:9:79:9 | { ..., ... } | 4 | csharp6.cs:77:13:77:56 | ... = ... |
64+
| csharp6.cs:72:9:79:9 | { ..., ... } | 5 | csharp6.cs:78:13:78:59 | ... = ... |
65+
| csharp6.cs:73:31:73:72 | { ..., ... } | 0 | csharp6.cs:73:33:73:44 | ... = ... |
66+
| csharp6.cs:73:31:73:72 | { ..., ... } | 1 | csharp6.cs:73:47:73:57 | ... = ... |
67+
| csharp6.cs:73:31:73:72 | { ..., ... } | 2 | csharp6.cs:73:60:73:70 | ... = ... |
68+
| csharp6.cs:74:34:74:76 | { ..., ... } | 0 | csharp6.cs:74:36:74:48 | ... = ... |
69+
| csharp6.cs:74:34:74:76 | { ..., ... } | 1 | csharp6.cs:74:51:74:61 | ... = ... |
70+
| csharp6.cs:74:34:74:76 | { ..., ... } | 2 | csharp6.cs:74:64:74:74 | ... = ... |
71+
| csharp6.cs:75:26:75:54 | { ..., ... } | 0 | csharp6.cs:75:28:75:39 | ... = ... |
72+
| csharp6.cs:75:26:75:54 | { ..., ... } | 1 | csharp6.cs:75:42:75:52 | ... = ... |
73+
| csharp6.cs:76:27:76:56 | { ..., ... } | 0 | csharp6.cs:76:29:76:40 | ... = ... |
74+
| csharp6.cs:76:27:76:56 | { ..., ... } | 1 | csharp6.cs:76:43:76:54 | ... = ... |
75+
| csharp6.cs:77:29:77:56 | { ..., ... } | 0 | csharp6.cs:77:31:77:41 | ... = ... |
76+
| csharp6.cs:77:29:77:56 | { ..., ... } | 1 | csharp6.cs:77:44:77:54 | ... = ... |
77+
| csharp6.cs:78:30:78:59 | { ..., ... } | 0 | csharp6.cs:78:32:78:43 | ... = ... |
78+
| csharp6.cs:78:30:78:59 | { ..., ... } | 1 | csharp6.cs:78:46:78:57 | ... = ... |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import csharp
2+
3+
query predicate assignedMembers(AssignableMember member, Expr value) {
4+
member.fromSource() and
5+
value = member.getAnAssignedValue()
6+
}
7+
8+
query predicate indexerCalls(IndexerCall indexer, int arg, Expr value) {
9+
value = indexer.getArgument(arg)
10+
}
11+
12+
query predicate elementAssignments(
13+
ElementWrite write, Assignment assignment, int index, Expr indexer
14+
) {
15+
write = assignment.getLValue() and indexer = write.getIndex(index)
16+
}
17+
18+
query predicate arrayQualifiers(ElementAccess access, Expr qualifier) {
19+
qualifier = access.getQualifier()
20+
}
21+
22+
query predicate initializers(ObjectInitializer init, int item, Expr expr) {
23+
expr = init.getMemberInitializer(item)
24+
}

csharp/ql/test/library-tests/csharp6/csharp6.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,34 @@ static void Main()
5050
int this[int i] => i;
5151
}
5252

53-
// semmle-extractor-options: /r:System.Linq.dll
53+
class IndexInitializers
54+
{
55+
class Compound
56+
{
57+
public Dictionary<int, string> DictionaryField;
58+
public Dictionary<int, string> DictionaryProperty { get; set; }
59+
public string[] ArrayField;
60+
public string[] ArrayProperty { get; set; }
61+
public string[,] ArrayField2;
62+
public string[,] ArrayProperty2 { get; set; }
63+
}
64+
65+
void Test()
66+
{
67+
// Collection initializer
68+
var dict = new Dictionary<int, string>() { [0] = "Zero", [1] = "One", [2] = "Two" };
69+
70+
// Indexed initializer
71+
var compound = new Compound()
72+
{
73+
DictionaryField = { [0] = "Zero", [1] = "One", [2] = "Two" },
74+
DictionaryProperty = { [3] = "Three", [2] = "Two", [1] = "One" },
75+
ArrayField = { [0] = "Zero", [1] = "One" },
76+
ArrayField2 = { [0, 1] = "i", [1, 0] = "1" },
77+
ArrayProperty = { [1] = "One", [2] = "Two" },
78+
ArrayProperty2 = { [0, 1] = "i", [1, 0] = "1" },
79+
};
80+
}
81+
}
82+
83+
// semmle-extractor-options: /r:System.Linq.dll /langerversion:6.0

csharp/ql/test/library-tests/standalone/controlflow/cfg.expected

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
| ControlFlow.cs:10:22:10:26 | access to property (unknown) | ControlFlow.cs:10:29:10:42 | "This is true" |
1818
| ControlFlow.cs:10:29:10:42 | "This is true" | ControlFlow.cs:10:9:10:43 | Call (unknown target) |
1919
| ControlFlow.cs:10:29:10:42 | "This is true" | ControlFlow.cs:10:9:10:43 | call to method |
20-
| ControlFlow.cs:12:9:12:86 | Call (unknown target) | ControlFlow.cs:12:37:12:47 | Expression |
20+
| ControlFlow.cs:12:9:12:86 | Call (unknown target) | ControlFlow.cs:12:51:12:62 | access to field Empty |
2121
| ControlFlow.cs:12:9:12:87 | ...; | ControlFlow.cs:12:9:12:86 | Call (unknown target) |
2222
| ControlFlow.cs:12:35:12:86 | { ..., ... } | ControlFlow.cs:7:10:7:10 | exit F |
23-
| ControlFlow.cs:12:37:12:47 | Expression | ControlFlow.cs:12:51:12:62 | access to field Empty |
24-
| ControlFlow.cs:12:37:12:62 | ... = ... | ControlFlow.cs:12:65:12:75 | Expression |
23+
| ControlFlow.cs:12:37:12:62 | ... = ... | ControlFlow.cs:12:79:12:79 | access to local variable v |
2524
| ControlFlow.cs:12:51:12:62 | access to field Empty | ControlFlow.cs:12:37:12:62 | ... = ... |
26-
| ControlFlow.cs:12:65:12:75 | Expression | ControlFlow.cs:12:79:12:79 | access to local variable v |
2725
| ControlFlow.cs:12:65:12:84 | ... = ... | ControlFlow.cs:12:35:12:86 | { ..., ... } |
2826
| ControlFlow.cs:12:79:12:79 | access to local variable v | ControlFlow.cs:12:79:12:84 | Call (unknown target) |
2927
| ControlFlow.cs:12:79:12:79 | access to local variable v | ControlFlow.cs:12:79:12:84 | access to property (unknown) |

0 commit comments

Comments
 (0)