未验证 提交 5d1ba27f 编写于 作者: A AlekseyTs 提交者: GitHub

Adjust records behavior around IOperations and analyzer actions. (#46308)

上级 1386d0fd
......@@ -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;
......@@ -95,10 +96,29 @@ private static bool InvalidLevel(int? level)
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)
......
......@@ -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)
{
......
......@@ -2323,6 +2323,11 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol)
}
}
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,
......
......@@ -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;
......@@ -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;
}
......@@ -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;
......@@ -2006,7 +2020,14 @@ internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclaratio
MethodSymbol method;
if (memberDecl is RecordDeclarationSyntax recordDecl && recordDecl.ParameterList == paramList)
{
method = TryGetSynthesizedRecordConstructor(recordDecl);
}
else
{
method = (GetDeclaredSymbol(memberDecl, cancellationToken) as IMethodSymbol).GetSymbol();
}
if ((object)method == null)
{
......@@ -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;
}
}
}
......@@ -18,6 +18,11 @@ internal class DeclarationComputer
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, IEnumerable<SyntaxNode>? executableCodeBlocks, CancellationToken cancellationToken)
{
var declaredSymbol = GetDeclaredSymbol(model, node, getSymbol, cancellationToken);
return GetDeclarationInfo(node, declaredSymbol, executableCodeBlocks);
}
internal static DeclarationInfo GetDeclarationInfo(SyntaxNode node, ISymbol? declaredSymbol, IEnumerable<SyntaxNode>? executableCodeBlocks)
{
var codeBlocks = executableCodeBlocks?.Where(c => c != null).AsImmutableOrEmpty() ?? ImmutableArray<SyntaxNode>.Empty;
return new DeclarationInfo(node, codeBlocks, declaredSymbol);
}
......
......@@ -2767,6 +2767,7 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb
{
case OperationKind.MethodBody:
case OperationKind.ConstructorBody:
Debug.Assert(!operationBlock.Parent.IsImplicit);
operationsToAnalyze.Add(operationBlock.Parent);
break;
......@@ -2776,6 +2777,10 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb
Debug.Assert(operationBlock.Parent.IsImplicit);
Debug.Assert(operationBlock.Parent.Parent is IConstructorBodyOperation ctorBody &&
ctorBody.Initializer == operationBlock.Parent);
Debug.Assert(!operationBlock.Parent.Parent.IsImplicit);
operationsToAnalyze.Add(operationBlock.Parent.Parent);
break;
default:
......
......@@ -943,7 +943,20 @@ public void MarkSymbolEndAnalysisComplete(ISymbol symbol, DiagnosticAnalyzer ana
{
ExecuteBlockActionsCore<CodeBlockStartAnalyzerAction<TLanguageKindEnum>, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction<TLanguageKindEnum>, SyntaxNodeAnalyzerStateData, SyntaxNode, TLanguageKindEnum>(
codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer,
declaredNode, declaredSymbol, executableCodeBlocks, (codeBlocks) => codeBlocks.SelectMany(cb => cb.DescendantNodesAndSelf()),
declaredNode, declaredSymbol, executableCodeBlocks, (codeBlocks) => codeBlocks.SelectMany(
cb =>
{
var filter = semanticModel.GetSyntaxNodesToAnalyzeFilter(cb, declaredSymbol);
if (filter is object)
{
return cb.DescendantNodesAndSelf(descendIntoChildren: filter).Where(filter);
}
else
{
return cb.DescendantNodesAndSelf();
}
}),
semanticModel, getKind, analyzerStateOpt?.CodeBlockAnalysisState, isGeneratedCode);
return true;
}
......
......@@ -1769,9 +1769,13 @@ protected static IOperation VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompil
protected static void VerifyOperationTreeForNode(CSharpCompilation compilation, SemanticModel model, SyntaxNode syntaxNode, string expectedOperationTree)
{
var actualOperation = model.GetOperation(syntaxNode);
Assert.NotNull(actualOperation);
var actualOperationTree = GetOperationTreeForTest(compilation, actualOperation);
VerifyOperationTree(compilation, model.GetOperation(syntaxNode), expectedOperationTree);
}
protected static void VerifyOperationTree(CSharpCompilation compilation, IOperation operation, string expectedOperationTree)
{
Assert.NotNull(operation);
var actualOperationTree = GetOperationTreeForTest(compilation, operation);
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册