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

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

上级 1386d0fd
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
...@@ -95,10 +96,29 @@ private static bool InvalidLevel(int? level) ...@@ -95,10 +96,29 @@ private static bool InvalidLevel(int? level)
return; 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.ClassDeclaration:
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration: case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.RecordDeclaration:
{ {
var t = (TypeDeclarationSyntax)node; var t = (TypeDeclarationSyntax)node;
foreach (var decl in t.Members) foreach (var decl in t.Members)
......
...@@ -5106,34 +5106,7 @@ internal override void ComputeDeclarationsInNode(SyntaxNode node, ISymbol associ ...@@ -5106,34 +5106,7 @@ internal override void ComputeDeclarationsInNode(SyntaxNode node, ISymbol associ
CSharpDeclarationComputer.ComputeDeclarationsInNode(this, associatedSymbol, node, getSymbol, builder, cancellationToken, levelsToCompute); CSharpDeclarationComputer.ComputeDeclarationsInNode(this, associatedSymbol, node, getSymbol, builder, cancellationToken, levelsToCompute);
} }
internal override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol) internal abstract 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);
}
protected internal override SyntaxNode GetTopmostNodeForDiagnosticAnalysis(ISymbol symbol, SyntaxNode declaringSyntax) protected internal override SyntaxNode GetTopmostNodeForDiagnosticAnalysis(ISymbol symbol, SyntaxNode declaringSyntax)
{ {
......
...@@ -2323,6 +2323,11 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol) ...@@ -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> /// <summary>
/// The incremental binder is used when binding statements. Whenever a statement /// 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, /// 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 ...@@ -178,7 +178,9 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat
case AccessorDeclarationSyntax accessor: case AccessorDeclarationSyntax accessor:
model = (accessor.Body != null || accessor.ExpressionBody != null) ? GetOrAddModel(node) : null; model = (accessor.Body != null || accessor.ExpressionBody != null) ? GetOrAddModel(node) : null;
break; break;
case RecordDeclarationSyntax { ParameterList: { }, PrimaryConstructorBaseType: { } } recordDeclaration when TryGetSynthesizedRecordConstructor(recordDeclaration) is SynthesizedRecordConstructor ctor:
model = GetOrAddModel(recordDeclaration);
break;
default: default:
model = this.GetMemberModel(node); model = this.GetMemberModel(node);
break; break;
...@@ -1087,10 +1089,9 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node) ...@@ -1087,10 +1089,9 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node)
case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordDeclaration:
{ {
var recordType = GetDeclaredSymbol((TypeDeclarationSyntax)node).GetSymbol<NamedTypeSymbol>(); SynthesizedRecordConstructor symbol = TryGetSynthesizedRecordConstructor((RecordDeclarationSyntax)node);
var symbol = recordType.GetMembersUnordered().OfType<SynthesizedRecordConstructor>().SingleOrDefault();
if (symbol?.GetSyntax() != node) if (symbol is null)
{ {
return null; return null;
} }
...@@ -1250,6 +1251,19 @@ MemberSemanticModel createMethodBodySemanticModel(CSharpSyntaxNode memberDecl, S ...@@ -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) private AttributeSemanticModel CreateModelForAttribute(Binder enclosingBinder, AttributeSyntax attribute, MemberSemanticModel containingModel)
{ {
AliasSymbol aliasOpt; AliasSymbol aliasOpt;
...@@ -2006,7 +2020,14 @@ internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclaratio ...@@ -2006,7 +2020,14 @@ internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclaratio
MethodSymbol method; 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) if ((object)method == null)
{ {
...@@ -2334,5 +2355,85 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol) ...@@ -2334,5 +2355,85 @@ internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol)
var memberModel = GetMemberModel(position); var memberModel = GetMemberModel(position);
return memberModel?.RemapSymbolIfNecessaryCore(symbol) ?? symbol; 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 ...@@ -18,6 +18,11 @@ internal class DeclarationComputer
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, IEnumerable<SyntaxNode>? executableCodeBlocks, CancellationToken cancellationToken) internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, IEnumerable<SyntaxNode>? executableCodeBlocks, CancellationToken cancellationToken)
{ {
var declaredSymbol = GetDeclaredSymbol(model, node, getSymbol, 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; var codeBlocks = executableCodeBlocks?.Where(c => c != null).AsImmutableOrEmpty() ?? ImmutableArray<SyntaxNode>.Empty;
return new DeclarationInfo(node, codeBlocks, declaredSymbol); return new DeclarationInfo(node, codeBlocks, declaredSymbol);
} }
......
...@@ -2767,6 +2767,7 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb ...@@ -2767,6 +2767,7 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb
{ {
case OperationKind.MethodBody: case OperationKind.MethodBody:
case OperationKind.ConstructorBody: case OperationKind.ConstructorBody:
Debug.Assert(!operationBlock.Parent.IsImplicit);
operationsToAnalyze.Add(operationBlock.Parent); operationsToAnalyze.Add(operationBlock.Parent);
break; break;
...@@ -2776,6 +2777,10 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb ...@@ -2776,6 +2777,10 @@ private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol otherSymb
Debug.Assert(operationBlock.Parent.IsImplicit); Debug.Assert(operationBlock.Parent.IsImplicit);
Debug.Assert(operationBlock.Parent.Parent is IConstructorBodyOperation ctorBody && Debug.Assert(operationBlock.Parent.Parent is IConstructorBodyOperation ctorBody &&
ctorBody.Initializer == operationBlock.Parent); ctorBody.Initializer == operationBlock.Parent);
Debug.Assert(!operationBlock.Parent.Parent.IsImplicit);
operationsToAnalyze.Add(operationBlock.Parent.Parent);
break; break;
default: default:
......
...@@ -943,7 +943,20 @@ public void MarkSymbolEndAnalysisComplete(ISymbol symbol, DiagnosticAnalyzer ana ...@@ -943,7 +943,20 @@ public void MarkSymbolEndAnalysisComplete(ISymbol symbol, DiagnosticAnalyzer ana
{ {
ExecuteBlockActionsCore<CodeBlockStartAnalyzerAction<TLanguageKindEnum>, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction<TLanguageKindEnum>, SyntaxNodeAnalyzerStateData, SyntaxNode, TLanguageKindEnum>( ExecuteBlockActionsCore<CodeBlockStartAnalyzerAction<TLanguageKindEnum>, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction<TLanguageKindEnum>, SyntaxNodeAnalyzerStateData, SyntaxNode, TLanguageKindEnum>(
codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer, 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); semanticModel, getKind, analyzerStateOpt?.CodeBlockAnalysisState, isGeneratedCode);
return true; return true;
} }
......
...@@ -1769,9 +1769,13 @@ protected static IOperation VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompil ...@@ -1769,9 +1769,13 @@ protected static IOperation VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompil
protected static void VerifyOperationTreeForNode(CSharpCompilation compilation, SemanticModel model, SyntaxNode syntaxNode, string expectedOperationTree) protected static void VerifyOperationTreeForNode(CSharpCompilation compilation, SemanticModel model, SyntaxNode syntaxNode, string expectedOperationTree)
{ {
var actualOperation = model.GetOperation(syntaxNode); VerifyOperationTree(compilation, model.GetOperation(syntaxNode), expectedOperationTree);
Assert.NotNull(actualOperation); }
var actualOperationTree = GetOperationTreeForTest(compilation, actualOperation);
protected static void VerifyOperationTree(CSharpCompilation compilation, IOperation operation, string expectedOperationTree)
{
Assert.NotNull(operation);
var actualOperationTree = GetOperationTreeForTest(compilation, operation);
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册