Skip to content

Commit

Permalink
Adjust records behavior around IOperations and analyzer actions. (dot…
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs authored Jul 28, 2020
1 parent 1386d0f commit 5d1ba27
Show file tree
Hide file tree
Showing 9 changed files with 1,999 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -95,10 +96,29 @@ private static void ComputeDeclarations(
return;
}

case SyntaxKind.RecordDeclaration:
{
if (associatedSymbol is IMethodSymbol ctor)
{
var recordDeclaration = (RecordDeclarationSyntax)node;
Debug.Assert(ctor.MethodKind == MethodKind.Constructor && recordDeclaration.ParameterList is object);

var codeBlocks = GetParameterListInitializersAndAttributes(recordDeclaration.ParameterList);

if (recordDeclaration.BaseList?.Types.FirstOrDefault() is PrimaryConstructorBaseTypeSyntax initializer)
{
codeBlocks = codeBlocks.Concat(initializer);
}

builder.Add(GetDeclarationInfo(node, associatedSymbol, codeBlocks));
return;
}

goto case SyntaxKind.ClassDeclaration;
}
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.RecordDeclaration:
{
var t = (TypeDeclarationSyntax)node;
foreach (var decl in t.Members)
Expand Down
29 changes: 1 addition & 28 deletions src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5106,34 +5106,7 @@ internal override void ComputeDeclarationsInNode(SyntaxNode node, ISymbol associ
CSharpDeclarationComputer.ComputeDeclarationsInNode(this, associatedSymbol, node, getSymbol, builder, cancellationToken, levelsToCompute);
}

internal override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol)
{
if (declaredNode is CompilationUnitSyntax unit && SimpleProgramNamedTypeSymbol.GetSimpleProgramEntryPoint(Compilation, unit, fallbackToMainEntryPoint: false) is SynthesizedSimpleProgramEntryPointSymbol entryPoint)
{
switch (declaredSymbol.Kind)
{
case SymbolKind.Namespace:
Debug.Assert(((INamespaceSymbol)declaredSymbol).IsGlobalNamespace);
// Do not include top level global statements into a global namespace
return (node) => node.Kind() != SyntaxKind.GlobalStatement || node.Parent != unit;

case SymbolKind.Method:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)entryPoint);
// Include only global statements at the top level
return (node) => node.Parent != unit || node.Kind() == SyntaxKind.GlobalStatement;

case SymbolKind.NamedType:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)entryPoint.ContainingSymbol);
return (node) => false;

default:
ExceptionUtilities.UnexpectedValue(declaredSymbol.Kind);
break;
}
}

return base.GetSyntaxNodesToAnalyzeFilter(declaredNode, declaredSymbol);
}
internal abstract override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol);

protected internal override SyntaxNode GetTopmostNodeForDiagnosticAnalysis(ISymbol symbol, SyntaxNode declaringSyntax)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,11 @@ symbol is ParameterSymbol ||
}
}

internal sealed override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol)
{
throw ExceptionUtilities.Unreachable;
}

/// <summary>
/// The incremental binder is used when binding statements. Whenever a statement
/// is bound, it checks the bound node cache to see if that statement was bound,
Expand Down
111 changes: 106 additions & 5 deletions src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat
case AccessorDeclarationSyntax accessor:
model = (accessor.Body != null || accessor.ExpressionBody != null) ? GetOrAddModel(node) : null;
break;

case RecordDeclarationSyntax { ParameterList: { }, PrimaryConstructorBaseType: { } } recordDeclaration when TryGetSynthesizedRecordConstructor(recordDeclaration) is SynthesizedRecordConstructor ctor:
model = GetOrAddModel(recordDeclaration);
break;
default:
model = this.GetMemberModel(node);
break;
Expand Down Expand Up @@ -1087,10 +1089,9 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)

case SyntaxKind.RecordDeclaration:
{
var recordType = GetDeclaredSymbol((TypeDeclarationSyntax)node).GetSymbol<NamedTypeSymbol>();
var symbol = recordType.GetMembersUnordered().OfType<SynthesizedRecordConstructor>().SingleOrDefault();
SynthesizedRecordConstructor symbol = TryGetSynthesizedRecordConstructor((RecordDeclarationSyntax)node);

if (symbol?.GetSyntax() != node)
if (symbol is null)
{
return null;
}
Expand Down Expand Up @@ -1250,6 +1251,19 @@ MemberSemanticModel createMethodBodySemanticModel(CSharpSyntaxNode memberDecl, S
}
}

private SynthesizedRecordConstructor TryGetSynthesizedRecordConstructor(RecordDeclarationSyntax node)
{
NamedTypeSymbol recordType = GetDeclaredType(node);
var symbol = recordType.GetMembersUnordered().OfType<SynthesizedRecordConstructor>().SingleOrDefault();

if (symbol?.GetSyntax() != node)
{
return null;
}

return symbol;
}

private AttributeSemanticModel CreateModelForAttribute(Binder enclosingBinder, AttributeSyntax attribute, MemberSemanticModel containingModel)
{
AliasSymbol aliasOpt;
Expand Down Expand Up @@ -2006,7 +2020,14 @@ private ParameterSymbol GetMethodParameterSymbol(

MethodSymbol method;

method = (GetDeclaredSymbol(memberDecl, cancellationToken) as IMethodSymbol).GetSymbol();
if (memberDecl is RecordDeclarationSyntax recordDecl && recordDecl.ParameterList == paramList)
{
method = TryGetSynthesizedRecordConstructor(recordDecl);
}
else
{
method = (GetDeclaredSymbol(memberDecl, cancellationToken) as IMethodSymbol).GetSymbol();
}

if ((object)method == null)
{
Expand Down Expand Up @@ -2334,5 +2355,85 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol)
var memberModel = GetMemberModel(position);
return memberModel?.RemapSymbolIfNecessaryCore(symbol) ?? symbol;
}

internal override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol)
{
switch (declaredNode)
{
case CompilationUnitSyntax unit when SimpleProgramNamedTypeSymbol.GetSimpleProgramEntryPoint(Compilation, unit, fallbackToMainEntryPoint: false) is SynthesizedSimpleProgramEntryPointSymbol entryPoint:
switch (declaredSymbol.Kind)
{
case SymbolKind.Namespace:
Debug.Assert(((INamespaceSymbol)declaredSymbol).IsGlobalNamespace);
// Do not include top level global statements into a global namespace
return (node) => node.Kind() != SyntaxKind.GlobalStatement || node.Parent != unit;

case SymbolKind.Method:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)entryPoint);
// Include only global statements at the top level
return (node) => node.Parent != unit || node.Kind() == SyntaxKind.GlobalStatement;

case SymbolKind.NamedType:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)entryPoint.ContainingSymbol);
return (node) => false;

default:
ExceptionUtilities.UnexpectedValue(declaredSymbol.Kind);
break;
}
break;

case RecordDeclarationSyntax recordDeclaration when TryGetSynthesizedRecordConstructor(recordDeclaration) is SynthesizedRecordConstructor ctor:
switch (declaredSymbol.Kind)
{
case SymbolKind.Method:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)ctor);
return (node) =>
{
// Accept only nodes that either match, or above/below of a 'parameter list'/'base arguments list'.
if (node.Parent == recordDeclaration)
{
return node == recordDeclaration.ParameterList || node == recordDeclaration.BaseList;
}
else if (node.Parent is BaseListSyntax baseList)
{
return node == recordDeclaration.PrimaryConstructorBaseType;
}
else if (node.Parent is PrimaryConstructorBaseTypeSyntax baseType && baseType == recordDeclaration.PrimaryConstructorBaseType)
{
return node == baseType.ArgumentList;
}
return true;
};

case SymbolKind.NamedType:
Debug.Assert((object)declaredSymbol.GetSymbol() == (object)ctor.ContainingSymbol);
// Accept nodes that do not match a 'parameter list'/'base arguments list'.
return (node) => node != recordDeclaration.ParameterList &&
!(node.Kind() == SyntaxKind.ArgumentList && node == recordDeclaration.PrimaryConstructorBaseType?.ArgumentList);

default:
ExceptionUtilities.UnexpectedValue(declaredSymbol.Kind);
break;
}
break;

case PrimaryConstructorBaseTypeSyntax { Parent: BaseListSyntax { Parent: RecordDeclarationSyntax recordDeclaration } } baseType
when recordDeclaration.PrimaryConstructorBaseType == declaredNode && TryGetSynthesizedRecordConstructor(recordDeclaration) is SynthesizedRecordConstructor ctor:
if ((object)declaredSymbol.GetSymbol() == (object)ctor)
{
// Only 'base arguments list' or nodes below it
return (node) => node != baseType.Type;
}
break;

case ParameterSyntax param when declaredSymbol.Kind == SymbolKind.Property && param.Parent?.Parent is RecordDeclarationSyntax recordDeclaration && recordDeclaration.ParameterList == param.Parent:
Debug.Assert(declaredSymbol.GetSymbol() is SynthesizedRecordPropertySymbol);
return (node) => false;
}

return null;
}
}
}
Loading

0 comments on commit 5d1ba27

Please sign in to comment.