Skip to content

Commit 7a80205

Browse files
committed
C#: Extract explicit and implicit primary constructor initializers.
1 parent c613851 commit 7a80205

File tree

3 files changed

+74
-41
lines changed

3 files changed

+74
-41
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Generic;
13
using System.Diagnostics.CodeAnalysis;
24
using System.IO;
35
using System.Linq;
@@ -10,8 +12,16 @@ namespace Semmle.Extraction.CSharp.Entities
1012
{
1113
internal class Constructor : Method
1214
{
15+
private readonly Lazy<List<SyntaxNode>> DeclaringReferenceSyntax;
16+
1317
private Constructor(Context cx, IMethodSymbol init)
14-
: base(cx, init) { }
18+
: base(cx, init)
19+
{
20+
DeclaringReferenceSyntax = new(() =>
21+
Symbol.DeclaringSyntaxReferences
22+
.Select(r => r.GetSyntax())
23+
.ToList());
24+
}
1525

1626
public override void Populate(TextWriter trapFile)
1727
{
@@ -33,16 +43,17 @@ public override void Populate(TextWriter trapFile)
3343
protected override void ExtractInitializers(TextWriter trapFile)
3444
{
3545
// Do not extract initializers for constructed types.
36-
if (!IsSourceDeclaration)
46+
// Only extract initializers for constructors with a body and primary constructors.
47+
if (Block is null && ExpressionBody is null && !IsPrimary ||
48+
!IsSourceDeclaration)
49+
{
3750
return;
51+
}
3852

39-
var syntax = Syntax;
40-
var initializer = syntax?.Initializer;
41-
42-
if (initializer is not null)
53+
if (OrdinaryConstructorSyntax?.Initializer is ConstructorInitializerSyntax initializer)
4354
{
4455
ITypeSymbol initializerType;
45-
var symbolInfo = Context.GetSymbolInfo(initializer);
56+
var initializerInfo = Context.GetSymbolInfo(initializer);
4657

4758
switch (initializer.Kind())
4859
{
@@ -57,27 +68,14 @@ protected override void ExtractInitializers(TextWriter trapFile)
5768
return;
5869
}
5970

60-
var initInfo = new ExpressionInfo(Context,
61-
AnnotatedTypeSymbol.CreateNotAnnotated(initializerType),
62-
Context.CreateLocation(initializer.ThisOrBaseKeyword.GetLocation()),
63-
Kinds.ExprKind.CONSTRUCTOR_INIT,
64-
this,
65-
-1,
66-
false,
67-
null);
68-
69-
var init = new Expression(initInfo);
70-
71-
var target = Constructor.Create(Context, (IMethodSymbol?)symbolInfo.Symbol);
72-
if (target is null)
73-
{
74-
Context.ModelError(Symbol, "Unable to resolve call");
75-
return;
76-
}
77-
78-
trapFile.expr_call(init, target);
71+
ExtractSourceInitializer(trapFile, initializerType, (IMethodSymbol?)initializerInfo.Symbol, initializer.ArgumentList, initializer.ThisOrBaseKeyword.GetLocation());
72+
}
73+
else if (PrimaryBase is PrimaryConstructorBaseTypeSyntax primaryInitializer)
74+
{
75+
var primaryInfo = Context.GetSymbolInfo(primaryInitializer);
76+
var primarySymbol = primaryInfo.Symbol;
7977

80-
init.PopulateArguments(trapFile, initializer.ArgumentList, 0);
78+
ExtractSourceInitializer(trapFile, primarySymbol?.ContainingType, (IMethodSymbol?)primarySymbol, primaryInitializer.ArgumentList, primaryInitializer.GetLocation());
8179
}
8280
else if (Symbol.MethodKind is MethodKind.Constructor)
8381
{
@@ -113,17 +111,50 @@ protected override void ExtractInitializers(TextWriter trapFile)
113111
}
114112
}
115113

116-
private ConstructorDeclarationSyntax? Syntax
114+
private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IMethodSymbol? symbol, ArgumentListSyntax arguments, Location location)
117115
{
118-
get
116+
var initInfo = new ExpressionInfo(Context,
117+
AnnotatedTypeSymbol.CreateNotAnnotated(type),
118+
Context.CreateLocation(location),
119+
Kinds.ExprKind.CONSTRUCTOR_INIT,
120+
this,
121+
-1,
122+
false,
123+
null);
124+
125+
var init = new Expression(initInfo);
126+
127+
var target = Constructor.Create(Context, symbol);
128+
if (target is null)
119129
{
120-
return Symbol.DeclaringSyntaxReferences
121-
.Select(r => r.GetSyntax())
122-
.OfType<ConstructorDeclarationSyntax>()
123-
.FirstOrDefault();
130+
Context.ModelError(Symbol, "Unable to resolve call");
131+
return;
124132
}
133+
134+
trapFile.expr_call(init, target);
135+
136+
init.PopulateArguments(trapFile, arguments, 0);
125137
}
126138

139+
private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax =>
140+
DeclaringReferenceSyntax.Value
141+
.OfType<ConstructorDeclarationSyntax>()
142+
.FirstOrDefault();
143+
144+
private TypeDeclarationSyntax? PrimaryConstructorSyntax =>
145+
DeclaringReferenceSyntax.Value
146+
.OfType<TypeDeclarationSyntax>()
147+
.FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax);
148+
149+
private PrimaryConstructorBaseTypeSyntax? PrimaryBase =>
150+
PrimaryConstructorSyntax?
151+
.BaseList?
152+
.Types
153+
.OfType<PrimaryConstructorBaseTypeSyntax>()
154+
.FirstOrDefault();
155+
156+
private bool IsPrimary => PrimaryConstructorSyntax is not null;
157+
127158
[return: NotNullIfNotNull("constructor")]
128159
public static new Constructor? Create(Context cx, IMethodSymbol? constructor)
129160
{
@@ -158,19 +189,20 @@ public override void WriteId(EscapingTextWriter trapFile)
158189
trapFile.Write(";constructor");
159190
}
160191

161-
private ConstructorDeclarationSyntax? GetSyntax() =>
162-
Symbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<ConstructorDeclarationSyntax>().FirstOrDefault();
163-
164192
public override Microsoft.CodeAnalysis.Location? FullLocation => ReportingLocation;
165193

166194
public override Microsoft.CodeAnalysis.Location? ReportingLocation
167195
{
168196
get
169197
{
170-
var syn = GetSyntax();
171-
if (syn is not null)
198+
if (OrdinaryConstructorSyntax is not null)
199+
{
200+
return OrdinaryConstructorSyntax.Identifier.GetLocation();
201+
}
202+
203+
if (PrimaryConstructorSyntax is not null)
172204
{
173-
return syn.Identifier.GetLocation();
205+
return PrimaryConstructorSyntax.Identifier.GetLocation();
174206
}
175207

176208
if (Symbol.IsImplicitlyDeclared)

csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ protected virtual void PopulateMethodBody(TextWriter trapFile)
5454
var block = Block;
5555
var expr = ExpressionBody;
5656

57+
Context.PopulateLater(() => ExtractInitializers(trapFile));
58+
5759
if (block is not null || expr is not null)
5860
{
5961
Context.PopulateLater(
6062
() =>
6163
{
62-
ExtractInitializers(trapFile);
6364
if (block is not null)
6465
Statements.Block.Create(Context, block, this, 0);
6566
else

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public Kinds.TypeKind GetTypeKind(Context cx, bool constructUnderlyingTupleType)
5757
{
5858
return Kinds.TypeKind.TUPLE;
5959
}
60-
return Symbol.IsInlineArray()
60+
return Symbol.IsInlineArray()
6161
? Kinds.TypeKind.INLINE_ARRAY
6262
: Kinds.TypeKind.STRUCT;
6363
}

0 commit comments

Comments
 (0)