Skip to content

Commit

Permalink
Finalize the design for the shape of the BaseTypeSyntax for records a…
Browse files Browse the repository at this point in the history
…nd implement SemanticModel APIs around it. (#45351)

 Finalize the design for the shape of the BaseTypeSyntax for records and implement SemanticModel APIs around it.

Closes #44795.
  • Loading branch information
AlekseyTs authored Jun 23, 2020
1 parent c4674ce commit 09db48d
Show file tree
Hide file tree
Showing 34 changed files with 1,038 additions and 171 deletions.
10 changes: 4 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3777,9 +3777,8 @@ private static bool IsNegativeConstantForArraySize(BoundExpression expression)
/// </summary>
/// <param name="initializerArgumentListOpt">
/// Null for implicit,
/// BaseConstructorInitializerSyntax.ArgumentList, or
/// ThisConstructorInitializerSyntax.ArgumentList, or
/// SimpleBaseTypeSyntax.ArgumentList for explicit.</param>
/// <see cref="ConstructorInitializerSyntax.ArgumentList"/>, or
/// <see cref="PrimaryConstructorBaseTypeSyntax.ArgumentList"/> for explicit.</param>
/// <param name="constructor">Constructor containing the initializer.</param>
/// <param name="diagnostics">Accumulates errors (e.g. unable to find constructor to invoke).</param>
/// <returns>A bound expression for the constructor initializer call.</returns>
Expand Down Expand Up @@ -3921,9 +3920,8 @@ private BoundExpression BindConstructorInitializerCore(
errorLocation = initializerSyntax.ThisOrBaseKeyword.GetLocation();
break;

case SimpleBaseTypeSyntax baseWithArguments:
Debug.Assert(baseWithArguments.Parent?.Parent is RecordDeclarationSyntax recordDecl && recordDecl.BaseList.Types.FirstOrDefault() == baseWithArguments);
nonNullSyntax = initializerArgumentListOpt;
case PrimaryConstructorBaseTypeSyntax baseWithArguments:
nonNullSyntax = baseWithArguments;
errorLocation = initializerArgumentListOpt.GetLocation();
break;

Expand Down
6 changes: 2 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3322,7 +3322,7 @@ private BoundNode BindRecordConstructorBody(RecordDeclarationSyntax recordDecl,
Debug.Assert(bodyBinder != null);

BoundExpressionStatement initializer = null;
if (recordDecl.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments)
if (recordDecl.PrimaryConstructorBaseType is PrimaryConstructorBaseTypeSyntax baseWithArguments)
{
initializer = bodyBinder.BindConstructorInitializer(baseWithArguments, diagnostics);
}
Expand All @@ -3334,10 +3334,8 @@ private BoundNode BindRecordConstructorBody(RecordDeclarationSyntax recordDecl,
expressionBody: null);
}

internal BoundExpressionStatement BindConstructorInitializer(SimpleBaseTypeSyntax initializer, DiagnosticBag diagnostics)
internal virtual BoundExpressionStatement BindConstructorInitializer(PrimaryConstructorBaseTypeSyntax initializer, DiagnosticBag diagnostics)
{
Debug.Assert(initializer.Parent?.Parent is RecordDeclarationSyntax recordDecl && recordDecl.ParameterList is object && recordDecl.BaseWithArguments == initializer);

BoundExpression initializerInvocation = GetBinder(initializer).BindConstructorInitializer(initializer.ArgumentList, (MethodSymbol)this.ContainingMember(), diagnostics);
var constructorInitializer = new BoundExpressionStatement(initializer, initializerInvocation);
Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected void FindExpressionVariables(
case SyntaxKind.GotoCaseStatement:
break;
case SyntaxKind.ArgumentList:
Debug.Assert(node.Parent is ConstructorInitializerSyntax);
Debug.Assert(node.Parent is ConstructorInitializerSyntax || node.Parent is PrimaryConstructorBaseTypeSyntax);
break;
case SyntaxKind.RecordDeclaration:
Debug.Assert(((RecordDeclarationSyntax)node).ParameterList is object);
Expand Down Expand Up @@ -397,7 +397,7 @@ public override void VisitRecordDeclaration(RecordDeclarationSyntax node)
{
Debug.Assert(node.ParameterList is object);

if (node.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments)
if (node.PrimaryConstructorBaseType is PrimaryConstructorBaseTypeSyntax baseWithArguments)
{
VisitNodeToBind(baseWithArguments);
}
Expand Down
25 changes: 15 additions & 10 deletions src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,14 @@ public override void VisitRecordDeclaration(RecordDeclarationSyntax node)

Binder enclosing = new ExpressionVariableBinder(node, _enclosing);
AddToMap(node, enclosing);
Visit(node.PrimaryConstructorBaseType, enclosing);
}

if (node.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments)
{
enclosing = enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer);
AddToMap(baseWithArguments, enclosing);
Visit(baseWithArguments.ArgumentList, enclosing);
}
public override void VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node)
{
Binder enclosing = _enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer);
AddToMap(node, enclosing);
VisitConstructorInitializerArgumentList(node, node.ArgumentList, enclosing);
}

public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node)
Expand Down Expand Up @@ -317,16 +318,20 @@ public override void VisitConstructorInitializer(ConstructorInitializerSyntax no
{
var binder = _enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer);
AddToMap(node, binder);
VisitConstructorInitializerArgumentList(node, node.ArgumentList, binder);
}

if (node.ArgumentList != null)
private void VisitConstructorInitializerArgumentList(CSharpSyntaxNode node, ArgumentListSyntax argumentList, Binder binder)
{
if (argumentList != null)
{
if (_root == node)
{
binder = new ExpressionVariableBinder(node.ArgumentList, binder);
AddToMap(node.ArgumentList, binder);
binder = new ExpressionVariableBinder(argumentList, binder);
AddToMap(argumentList, binder);
}

Visit(node.ArgumentList, binder);
Visit(argumentList, binder);
}
}

Expand Down
58 changes: 58 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,22 @@ public static Conversion ClassifyConversion(this Compilation? compilation, IType
}
}

/// <summary>
/// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program.
/// </summary>
public static SymbolInfo GetSymbolInfo(this SemanticModel? semanticModel, PrimaryConstructorBaseTypeSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken))
{
var csmodel = semanticModel as CSharpSemanticModel;
if (csmodel != null)
{
return csmodel.GetSymbolInfo(constructorInitializer, cancellationToken);
}
else
{
return SymbolInfo.None;
}
}

/// <summary>
/// Returns what symbol(s), if any, the given attribute syntax bound to in the program.
/// </summary>
Expand Down Expand Up @@ -643,6 +659,27 @@ public static SymbolInfo GetSpeculativeSymbolInfo(this SemanticModel? semanticMo
}
}

/// <summary>
/// Bind the constructor initializer in the context of the specified location and get semantic information
/// about symbols. This method is used to get semantic information about a constructor
/// initializer that did not actually appear in the source code.
///
/// NOTE: This will only work in locations where there is already a constructor initializer.
/// <see cref="PrimaryConstructorBaseTypeSyntax"/>.
/// </summary>
public static SymbolInfo GetSpeculativeSymbolInfo(this SemanticModel? semanticModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer)
{
var csmodel = semanticModel as CSharpSemanticModel;
if (csmodel != null)
{
return csmodel.GetSpeculativeSymbolInfo(position, constructorInitializer);
}
else
{
return SymbolInfo.None;
}
}

/// <summary>
/// Gets type information about a constructor initializer.
/// </summary>
Expand Down Expand Up @@ -1178,6 +1215,27 @@ public static bool TryGetSpeculativeSemanticModel([NotNullWhen(true)] this Seman
}
}

/// <summary>
/// Get a SemanticModel object that is associated with a constructor initializer that did not appear in
/// this source code. This can be used to get detailed semantic information about sub-parts
/// of a constructor initializer that did not appear in source code.
///
/// NOTE: This will only work in locations where there is already a constructor initializer.
/// </summary>
public static bool TryGetSpeculativeSemanticModel([NotNullWhen(true)] this SemanticModel? semanticModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, [NotNullWhen(true)] out SemanticModel? speculativeModel)
{
var csmodel = semanticModel as CSharpSemanticModel;
if (csmodel != null)
{
return csmodel.TryGetSpeculativeSemanticModel(position, constructorInitializer, out speculativeModel);
}
else
{
speculativeModel = null;
return false;
}
}

/// <summary>
/// Get a SemanticModel object that is associated with an attribute that did not appear in
/// this source code. This can be used to get detailed semantic information about sub-parts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode
return false;
}

internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel)
{
speculativeModel = null;
return false;
}

internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel)
{
speculativeModel = null;
Expand Down
Loading

0 comments on commit 09db48d

Please sign in to comment.