mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge pull request #15625 from michaelnebel/csharp/primaryconstructorinitializer
C# 12: Primary constructor inititalizers.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -10,8 +12,16 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
internal class Constructor : Method
|
||||
{
|
||||
private readonly List<SyntaxNode> declaringReferenceSyntax;
|
||||
|
||||
private Constructor(Context cx, IMethodSymbol init)
|
||||
: base(cx, init) { }
|
||||
: base(cx, init)
|
||||
{
|
||||
declaringReferenceSyntax =
|
||||
Symbol.DeclaringSyntaxReferences
|
||||
.Select(r => r.GetSyntax())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override void Populate(TextWriter trapFile)
|
||||
{
|
||||
@@ -22,6 +32,12 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.constructors(this, Symbol.ContainingType.Name, ContainingType, (Constructor)OriginalDefinition);
|
||||
trapFile.constructor_location(this, Location);
|
||||
|
||||
if (IsPrimary)
|
||||
{
|
||||
// Create a synthetic empty body for primary constructors.
|
||||
Statements.SyntheticEmptyBlock.Create(Context, this, 0, Location);
|
||||
}
|
||||
|
||||
if (Symbol.IsImplicitlyDeclared)
|
||||
{
|
||||
var lineCounts = new LineCounts() { Total = 2, Code = 1, Comment = 0 };
|
||||
@@ -33,68 +49,79 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
protected override void ExtractInitializers(TextWriter trapFile)
|
||||
{
|
||||
// Do not extract initializers for constructed types.
|
||||
if (!IsSourceDeclaration)
|
||||
return;
|
||||
|
||||
var syntax = Syntax;
|
||||
var initializer = syntax?.Initializer;
|
||||
|
||||
if (initializer is null)
|
||||
// Only extract initializers for constructors with a body and primary constructors.
|
||||
if (Block is null && ExpressionBody is null && !IsPrimary ||
|
||||
!IsSourceDeclaration)
|
||||
{
|
||||
if (Symbol.MethodKind is MethodKind.Constructor)
|
||||
return;
|
||||
}
|
||||
|
||||
if (OrdinaryConstructorSyntax?.Initializer is ConstructorInitializerSyntax initializer)
|
||||
{
|
||||
ITypeSymbol initializerType;
|
||||
var initializerInfo = Context.GetSymbolInfo(initializer);
|
||||
|
||||
switch (initializer.Kind())
|
||||
{
|
||||
var baseType = Symbol.ContainingType.BaseType;
|
||||
if (baseType is null)
|
||||
{
|
||||
if (Symbol.ContainingType.SpecialType != SpecialType.System_Object)
|
||||
{
|
||||
Context.ModelError(Symbol, "Unable to resolve base type in implicit constructor initializer");
|
||||
}
|
||||
case SyntaxKind.BaseConstructorInitializer:
|
||||
initializerType = Symbol.ContainingType.BaseType!;
|
||||
break;
|
||||
case SyntaxKind.ThisConstructorInitializer:
|
||||
initializerType = Symbol.ContainingType;
|
||||
break;
|
||||
default:
|
||||
Context.ModelError(initializer, "Unknown initializer");
|
||||
return;
|
||||
}
|
||||
|
||||
var baseConstructor = baseType.InstanceConstructors.FirstOrDefault(c => c.Arity is 0);
|
||||
|
||||
if (baseConstructor is null)
|
||||
{
|
||||
Context.ModelError(Symbol, "Unable to resolve implicit constructor initializer call");
|
||||
return;
|
||||
}
|
||||
|
||||
var baseConstructorTarget = Create(Context, baseConstructor);
|
||||
var info = new ExpressionInfo(Context,
|
||||
AnnotatedTypeSymbol.CreateNotAnnotated(baseType),
|
||||
Location,
|
||||
Kinds.ExprKind.CONSTRUCTOR_INIT,
|
||||
this,
|
||||
-1,
|
||||
isCompilerGenerated: true,
|
||||
null);
|
||||
|
||||
trapFile.expr_call(new Expression(info), baseConstructorTarget);
|
||||
}
|
||||
return;
|
||||
|
||||
ExtractSourceInitializer(trapFile, initializerType, (IMethodSymbol?)initializerInfo.Symbol, initializer.ArgumentList, initializer.ThisOrBaseKeyword.GetLocation());
|
||||
}
|
||||
|
||||
ITypeSymbol initializerType;
|
||||
var symbolInfo = Context.GetSymbolInfo(initializer);
|
||||
|
||||
switch (initializer.Kind())
|
||||
else if (PrimaryBase is PrimaryConstructorBaseTypeSyntax primaryInitializer)
|
||||
{
|
||||
case SyntaxKind.BaseConstructorInitializer:
|
||||
initializerType = Symbol.ContainingType.BaseType!;
|
||||
break;
|
||||
case SyntaxKind.ThisConstructorInitializer:
|
||||
initializerType = Symbol.ContainingType;
|
||||
break;
|
||||
default:
|
||||
Context.ModelError(initializer, "Unknown initializer");
|
||||
return;
|
||||
}
|
||||
var primaryInfo = Context.GetSymbolInfo(primaryInitializer);
|
||||
var primarySymbol = primaryInfo.Symbol;
|
||||
|
||||
ExtractSourceInitializer(trapFile, primarySymbol?.ContainingType, (IMethodSymbol?)primarySymbol, primaryInitializer.ArgumentList, primaryInitializer.GetLocation());
|
||||
}
|
||||
else if (Symbol.MethodKind is MethodKind.Constructor)
|
||||
{
|
||||
var baseType = Symbol.ContainingType.BaseType;
|
||||
if (baseType is null)
|
||||
{
|
||||
if (Symbol.ContainingType.SpecialType != SpecialType.System_Object)
|
||||
{
|
||||
Context.ModelError(Symbol, "Unable to resolve base type in implicit constructor initializer");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var baseConstructor = baseType.InstanceConstructors.FirstOrDefault(c => c.Arity is 0);
|
||||
|
||||
if (baseConstructor is null)
|
||||
{
|
||||
Context.ModelError(Symbol, "Unable to resolve implicit constructor initializer call");
|
||||
return;
|
||||
}
|
||||
|
||||
var baseConstructorTarget = Create(Context, baseConstructor);
|
||||
var info = new ExpressionInfo(Context,
|
||||
AnnotatedTypeSymbol.CreateNotAnnotated(baseType),
|
||||
Location,
|
||||
Kinds.ExprKind.CONSTRUCTOR_INIT,
|
||||
this,
|
||||
-1,
|
||||
isCompilerGenerated: true,
|
||||
null);
|
||||
|
||||
trapFile.expr_call(new Expression(info), baseConstructorTarget);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractSourceInitializer(TextWriter trapFile, ITypeSymbol? type, IMethodSymbol? symbol, ArgumentListSyntax arguments, Location location)
|
||||
{
|
||||
var initInfo = new ExpressionInfo(Context,
|
||||
AnnotatedTypeSymbol.CreateNotAnnotated(initializerType),
|
||||
Context.CreateLocation(initializer.ThisOrBaseKeyword.GetLocation()),
|
||||
AnnotatedTypeSymbol.CreateNotAnnotated(type),
|
||||
Context.CreateLocation(location),
|
||||
Kinds.ExprKind.CONSTRUCTOR_INIT,
|
||||
this,
|
||||
-1,
|
||||
@@ -103,7 +130,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
var init = new Expression(initInfo);
|
||||
|
||||
var target = Constructor.Create(Context, (IMethodSymbol?)symbolInfo.Symbol);
|
||||
var target = Constructor.Create(Context, symbol);
|
||||
if (target is null)
|
||||
{
|
||||
Context.ModelError(Symbol, "Unable to resolve call");
|
||||
@@ -112,19 +139,27 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
trapFile.expr_call(init, target);
|
||||
|
||||
init.PopulateArguments(trapFile, initializer.ArgumentList, 0);
|
||||
init.PopulateArguments(trapFile, arguments, 0);
|
||||
}
|
||||
|
||||
private ConstructorDeclarationSyntax? Syntax
|
||||
{
|
||||
get
|
||||
{
|
||||
return Symbol.DeclaringSyntaxReferences
|
||||
.Select(r => r.GetSyntax())
|
||||
.OfType<ConstructorDeclarationSyntax>()
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
private ConstructorDeclarationSyntax? OrdinaryConstructorSyntax =>
|
||||
declaringReferenceSyntax
|
||||
.OfType<ConstructorDeclarationSyntax>()
|
||||
.FirstOrDefault();
|
||||
|
||||
private TypeDeclarationSyntax? PrimaryConstructorSyntax =>
|
||||
declaringReferenceSyntax
|
||||
.OfType<TypeDeclarationSyntax>()
|
||||
.FirstOrDefault(t => t is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax);
|
||||
|
||||
private PrimaryConstructorBaseTypeSyntax? PrimaryBase =>
|
||||
PrimaryConstructorSyntax?
|
||||
.BaseList?
|
||||
.Types
|
||||
.OfType<PrimaryConstructorBaseTypeSyntax>()
|
||||
.FirstOrDefault();
|
||||
|
||||
private bool IsPrimary => PrimaryConstructorSyntax is not null;
|
||||
|
||||
[return: NotNullIfNotNull(nameof(constructor))]
|
||||
public static new Constructor? Create(Context cx, IMethodSymbol? constructor)
|
||||
@@ -160,19 +195,20 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.Write(";constructor");
|
||||
}
|
||||
|
||||
private ConstructorDeclarationSyntax? GetSyntax() =>
|
||||
Symbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<ConstructorDeclarationSyntax>().FirstOrDefault();
|
||||
|
||||
public override Microsoft.CodeAnalysis.Location? FullLocation => ReportingLocation;
|
||||
|
||||
public override Microsoft.CodeAnalysis.Location? ReportingLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
var syn = GetSyntax();
|
||||
if (syn is not null)
|
||||
if (OrdinaryConstructorSyntax is not null)
|
||||
{
|
||||
return syn.Identifier.GetLocation();
|
||||
return OrdinaryConstructorSyntax.Identifier.GetLocation();
|
||||
}
|
||||
|
||||
if (PrimaryConstructorSyntax is not null)
|
||||
{
|
||||
return PrimaryConstructorSyntax.Identifier.GetLocation();
|
||||
}
|
||||
|
||||
if (Symbol.IsImplicitlyDeclared)
|
||||
|
||||
@@ -54,12 +54,13 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
var block = Block;
|
||||
var expr = ExpressionBody;
|
||||
|
||||
Context.PopulateLater(() => ExtractInitializers(trapFile));
|
||||
|
||||
if (block is not null || expr is not null)
|
||||
{
|
||||
Context.PopulateLater(
|
||||
() =>
|
||||
{
|
||||
ExtractInitializers(trapFile);
|
||||
if (block is not null)
|
||||
Statements.Block.Create(Context, block, this, 0);
|
||||
else
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Semmle.Extraction.Entities;
|
||||
using Semmle.Extraction.Kinds;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Entities.Statements
|
||||
{
|
||||
internal class SyntheticEmptyBlock : Statement<BlockSyntax>
|
||||
{
|
||||
private SyntheticEmptyBlock(Context cx, BlockSyntax block, IStatementParentEntity parent, int child, Location location)
|
||||
: base(cx, block, StmtKind.BLOCK, parent, child, location) { }
|
||||
|
||||
public static SyntheticEmptyBlock Create(Context cx, IStatementParentEntity parent, int child, Location location)
|
||||
{
|
||||
var block = SyntaxFactory.Block();
|
||||
var ret = new SyntheticEmptyBlock(cx, block, parent, child, location);
|
||||
ret.TryPopulate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override void PopulateStatement(TextWriter trapFile) { }
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
return Kinds.TypeKind.TUPLE;
|
||||
}
|
||||
return Symbol.IsInlineArray()
|
||||
return Symbol.IsInlineArray()
|
||||
? Kinds.TypeKind.INLINE_ARRAY
|
||||
: Kinds.TypeKind.STRUCT;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user