1+ using System ;
2+ using System . Collections . Generic ;
13using System . Diagnostics . CodeAnalysis ;
24using System . IO ;
35using System . Linq ;
@@ -10,8 +12,16 @@ namespace Semmle.Extraction.CSharp.Entities
1012{
1113 internal class Constructor : Method
1214 {
15+ private readonly List < SyntaxNode > declaringReferenceSyntax ;
16+
1317 private Constructor ( Context cx , IMethodSymbol init )
14- : base ( cx , init ) { }
18+ : base ( cx , init )
19+ {
20+ declaringReferenceSyntax =
21+ Symbol . DeclaringSyntaxReferences
22+ . Select ( r => r . GetSyntax ( ) )
23+ . ToList ( ) ;
24+ }
1525
1626 public override void Populate ( TextWriter trapFile )
1727 {
@@ -22,6 +32,12 @@ public override void Populate(TextWriter trapFile)
2232 trapFile . constructors ( this , Symbol . ContainingType . Name , ContainingType , ( Constructor ) OriginalDefinition ) ;
2333 trapFile . constructor_location ( this , Location ) ;
2434
35+ if ( IsPrimary )
36+ {
37+ // Create a synthetic empty body for primary constructors.
38+ Statements . SyntheticEmptyBlock . Create ( Context , this , 0 , Location ) ;
39+ }
40+
2541 if ( Symbol . IsImplicitlyDeclared )
2642 {
2743 var lineCounts = new LineCounts ( ) { Total = 2 , Code = 1 , Comment = 0 } ;
@@ -33,68 +49,79 @@ public override void Populate(TextWriter trapFile)
3349 protected override void ExtractInitializers ( TextWriter trapFile )
3450 {
3551 // Do not extract initializers for constructed types.
36- if ( ! IsSourceDeclaration )
52+ // Only extract initializers for constructors with a body and primary constructors.
53+ if ( Block is null && ExpressionBody is null && ! IsPrimary ||
54+ ! IsSourceDeclaration )
55+ {
3756 return ;
57+ }
3858
39- var syntax = Syntax ;
40- var initializer = syntax ? . Initializer ;
41-
42- if ( initializer is null )
59+ if ( OrdinaryConstructorSyntax ? . Initializer is ConstructorInitializerSyntax initializer )
4360 {
44- if ( Symbol . MethodKind is MethodKind . Constructor )
61+ ITypeSymbol initializerType ;
62+ var initializerInfo = Context . GetSymbolInfo ( initializer ) ;
63+
64+ switch ( initializer . Kind ( ) )
4565 {
46- var baseType = Symbol . ContainingType . BaseType ;
47- if ( baseType is null )
48- {
49- if ( Symbol . ContainingType . SpecialType != SpecialType . System_Object )
50- {
51- Context . ModelError ( Symbol , "Unable to resolve base type in implicit constructor initializer" ) ;
52- }
66+ case SyntaxKind . BaseConstructorInitializer :
67+ initializerType = Symbol . ContainingType . BaseType ! ;
68+ break ;
69+ case SyntaxKind . ThisConstructorInitializer :
70+ initializerType = Symbol . ContainingType ;
71+ break ;
72+ default :
73+ Context . ModelError ( initializer , "Unknown initializer" ) ;
5374 return ;
54- }
75+ }
5576
56- var baseConstructor = baseType . InstanceConstructors . FirstOrDefault ( c => c . Arity is 0 ) ;
77+ ExtractSourceInitializer ( trapFile , initializerType , ( IMethodSymbol ? ) initializerInfo . Symbol , initializer . ArgumentList , initializer . ThisOrBaseKeyword . GetLocation ( ) ) ;
78+ }
79+ else if ( PrimaryBase is PrimaryConstructorBaseTypeSyntax primaryInitializer )
80+ {
81+ var primaryInfo = Context . GetSymbolInfo ( primaryInitializer ) ;
82+ var primarySymbol = primaryInfo . Symbol ;
5783
58- if ( baseConstructor is null )
84+ ExtractSourceInitializer ( trapFile , primarySymbol ? . ContainingType , ( IMethodSymbol ? ) primarySymbol , primaryInitializer . ArgumentList , primaryInitializer . GetLocation ( ) ) ;
85+ }
86+ else if ( Symbol . MethodKind is MethodKind . Constructor )
87+ {
88+ var baseType = Symbol . ContainingType . BaseType ;
89+ if ( baseType is null )
90+ {
91+ if ( Symbol . ContainingType . SpecialType != SpecialType . System_Object )
5992 {
60- Context . ModelError ( Symbol , "Unable to resolve implicit constructor initializer call" ) ;
61- return ;
93+ Context . ModelError ( Symbol , "Unable to resolve base type in implicit constructor initializer" ) ;
6294 }
63-
64- var baseConstructorTarget = Create ( Context , baseConstructor ) ;
65- var info = new ExpressionInfo ( Context ,
66- AnnotatedTypeSymbol . CreateNotAnnotated ( baseType ) ,
67- Location ,
68- Kinds . ExprKind . CONSTRUCTOR_INIT ,
69- this ,
70- - 1 ,
71- isCompilerGenerated : true ,
72- null ) ;
73-
74- trapFile . expr_call ( new Expression ( info ) , baseConstructorTarget ) ;
95+ return ;
7596 }
76- return ;
77- }
7897
79- ITypeSymbol initializerType ;
80- var symbolInfo = Context . GetSymbolInfo ( initializer ) ;
98+ var baseConstructor = baseType . InstanceConstructors . FirstOrDefault ( c => c . Arity is 0 ) ;
8199
82- switch ( initializer . Kind ( ) )
83- {
84- case SyntaxKind . BaseConstructorInitializer :
85- initializerType = Symbol . ContainingType . BaseType ! ;
86- break ;
87- case SyntaxKind . ThisConstructorInitializer :
88- initializerType = Symbol . ContainingType ;
89- break ;
90- default :
91- Context . ModelError ( initializer , "Unknown initializer" ) ;
100+ if ( baseConstructor is null )
101+ {
102+ Context . ModelError ( Symbol , "Unable to resolve implicit constructor initializer call" ) ;
92103 return ;
104+ }
105+
106+ var baseConstructorTarget = Create ( Context , baseConstructor ) ;
107+ var info = new ExpressionInfo ( Context ,
108+ AnnotatedTypeSymbol . CreateNotAnnotated ( baseType ) ,
109+ Location ,
110+ Kinds . ExprKind . CONSTRUCTOR_INIT ,
111+ this ,
112+ - 1 ,
113+ isCompilerGenerated : true ,
114+ null ) ;
115+
116+ trapFile . expr_call ( new Expression ( info ) , baseConstructorTarget ) ;
93117 }
118+ }
94119
120+ private void ExtractSourceInitializer ( TextWriter trapFile , ITypeSymbol ? type , IMethodSymbol ? symbol , ArgumentListSyntax arguments , Location location )
121+ {
95122 var initInfo = new ExpressionInfo ( Context ,
96- AnnotatedTypeSymbol . CreateNotAnnotated ( initializerType ) ,
97- Context . CreateLocation ( initializer . ThisOrBaseKeyword . GetLocation ( ) ) ,
123+ AnnotatedTypeSymbol . CreateNotAnnotated ( type ) ,
124+ Context . CreateLocation ( location ) ,
98125 Kinds . ExprKind . CONSTRUCTOR_INIT ,
99126 this ,
100127 - 1 ,
@@ -103,7 +130,7 @@ protected override void ExtractInitializers(TextWriter trapFile)
103130
104131 var init = new Expression ( initInfo ) ;
105132
106- var target = Constructor . Create ( Context , ( IMethodSymbol ? ) symbolInfo . Symbol ) ;
133+ var target = Constructor . Create ( Context , symbol ) ;
107134 if ( target is null )
108135 {
109136 Context . ModelError ( Symbol , "Unable to resolve call" ) ;
@@ -112,19 +139,27 @@ protected override void ExtractInitializers(TextWriter trapFile)
112139
113140 trapFile . expr_call ( init , target ) ;
114141
115- init . PopulateArguments ( trapFile , initializer . ArgumentList , 0 ) ;
142+ init . PopulateArguments ( trapFile , arguments , 0 ) ;
116143 }
117144
118- private ConstructorDeclarationSyntax ? Syntax
119- {
120- get
121- {
122- return Symbol . DeclaringSyntaxReferences
123- . Select ( r => r . GetSyntax ( ) )
124- . OfType < ConstructorDeclarationSyntax > ( )
125- . FirstOrDefault ( ) ;
126- }
127- }
145+ private ConstructorDeclarationSyntax ? OrdinaryConstructorSyntax =>
146+ declaringReferenceSyntax
147+ . OfType < ConstructorDeclarationSyntax > ( )
148+ . FirstOrDefault ( ) ;
149+
150+ private TypeDeclarationSyntax ? PrimaryConstructorSyntax =>
151+ declaringReferenceSyntax
152+ . OfType < TypeDeclarationSyntax > ( )
153+ . FirstOrDefault ( t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax ) ;
154+
155+ private PrimaryConstructorBaseTypeSyntax ? PrimaryBase =>
156+ PrimaryConstructorSyntax ?
157+ . BaseList ?
158+ . Types
159+ . OfType < PrimaryConstructorBaseTypeSyntax > ( )
160+ . FirstOrDefault ( ) ;
161+
162+ private bool IsPrimary => PrimaryConstructorSyntax is not null ;
128163
129164 [ return : NotNullIfNotNull ( nameof ( constructor ) ) ]
130165 public static new Constructor ? Create ( Context cx , IMethodSymbol ? constructor )
@@ -160,19 +195,20 @@ public override void WriteId(EscapingTextWriter trapFile)
160195 trapFile . Write ( ";constructor" ) ;
161196 }
162197
163- private ConstructorDeclarationSyntax ? GetSyntax ( ) =>
164- Symbol . DeclaringSyntaxReferences . Select ( r => r . GetSyntax ( ) ) . OfType < ConstructorDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
165-
166198 public override Microsoft . CodeAnalysis . Location ? FullLocation => ReportingLocation ;
167199
168200 public override Microsoft . CodeAnalysis . Location ? ReportingLocation
169201 {
170202 get
171203 {
172- var syn = GetSyntax ( ) ;
173- if ( syn is not null )
204+ if ( OrdinaryConstructorSyntax is not null )
205+ {
206+ return OrdinaryConstructorSyntax . Identifier . GetLocation ( ) ;
207+ }
208+
209+ if ( PrimaryConstructorSyntax is not null )
174210 {
175- return syn . Identifier . GetLocation ( ) ;
211+ return PrimaryConstructorSyntax . Identifier . GetLocation ( ) ;
176212 }
177213
178214 if ( Symbol . IsImplicitlyDeclared )
0 commit comments