未验证 提交 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;
}
}
}
......@@ -8,9 +8,13 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Test.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
......@@ -5755,7 +5759,6 @@ static void Main()
}
}";
comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics();
var actualMembers = GetProperties(comp, "C").ToTestDisplayStrings();
AssertEx.Equal(new[] { "System.Type C.EqualityContract { get; }" }, actualMembers);
......@@ -14823,7 +14826,7 @@ public Base(int X, int Y)
public Base() {}
}
record C(int X, int Y) : Base(X, Y)
record C(int X, int Y = 123) : Base(X, Y)
{
int Z = 123;
public static void Main()
......@@ -14832,7 +14835,7 @@ public static void Main()
Console.WriteLine(c.Z);
}
C(int X, int Y, int Z) : this(X, Y) {}
C(int X, int Y, int Z = 124) : this(X, Y) {}
}";
var verifier = CompileAndVerify(src, expectedOutput: @"
1
......@@ -14872,7 +14875,7 @@ .maxstack 3
var symbol = model.GetSymbolInfo(x).Symbol;
Assert.Equal(SymbolKind.Parameter, symbol!.Kind);
Assert.Equal("System.Int32 X", symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", symbol.ContainingSymbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, [System.Int32 Y = 123])", symbol.ContainingSymbol.ToTestDisplayString());
Assert.Equal(Accessibility.Public, symbol.ContainingSymbol.DeclaredAccessibility);
Assert.Same(symbol.ContainingSymbol, model.GetEnclosingSymbol(x.SpanStart));
Assert.Contains(symbol, model.LookupSymbols(x.SpanStart, name: "X"));
......@@ -14902,17 +14905,153 @@ .maxstack 3
model = comp.GetSemanticModel(tree);
Assert.Empty(model.GetMemberGroup(baseWithargs));
model = comp.GetSemanticModel(tree);
#nullable disable
var operation = model.GetOperation(baseWithargs);
VerifyOperationTree(comp, operation,
@"
IInvocationOperation ( Base..ctor(System.Int32 X, System.Int32 Y)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Base(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Base, IsImplicit) (Syntax: 'Base(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
");
Assert.Null(model.GetOperation(baseWithargs.Type));
Assert.Null(model.GetOperation(baseWithargs.Parent));
Assert.Same(operation.Parent.Parent, model.GetOperation(baseWithargs.Parent.Parent));
Assert.Equal(SyntaxKind.RecordDeclaration, baseWithargs.Parent.Parent.Kind());
VerifyOperationTree(comp, operation.Parent.Parent,
@"
IConstructorBodyOperation (OperationKind.ConstructorBody, Type: null) (Syntax: 'record C(in ... }')
Initializer:
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'Base(X, Y)')
Expression:
IInvocationOperation ( Base..ctor(System.Int32 X, System.Int32 Y)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Base(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Base, IsImplicit) (Syntax: 'Base(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
BlockBody:
IBlockOperation (0 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'record C(in ... }')
ExpressionBody:
null
");
Assert.Null(operation.Parent.Parent.Parent);
ControlFlowGraphVerifier.VerifyGraph(comp,
@"
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'Base(X, Y)')
Expression:
IInvocationOperation ( Base..ctor(System.Int32 X, System.Int32 Y)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Base(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Base, IsImplicit) (Syntax: 'Base(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Block[B2] - Exit
Predecessors: [B1]
Statements (0)
", ControlFlowGraph.Create((IConstructorBodyOperation)operation.Parent.Parent));
var equalsValue = tree.GetRoot().DescendantNodes().OfType<EqualsValueClauseSyntax>().First();
Assert.Equal("= 123", equalsValue.ToString());
model.VerifyOperationTree(equalsValue,
@"
IParameterInitializerOperation (Parameter: [System.Int32 Y = 123]) (OperationKind.ParameterInitializer, Type: null) (Syntax: '= 123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
");
#nullable enable
}
{
var baseWithargs = tree.GetRoot().DescendantNodes().OfType<ConstructorInitializerSyntax>().Single();
Assert.Equal(": this(X, Y)", baseWithargs.ToString());
Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo((SyntaxNode)baseWithargs).Symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo(baseWithargs).Symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", CSharpExtensions.GetSymbolInfo(model, baseWithargs).Symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, [System.Int32 Y = 123])", model.GetSymbolInfo((SyntaxNode)baseWithargs).Symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, [System.Int32 Y = 123])", model.GetSymbolInfo(baseWithargs).Symbol.ToTestDisplayString());
Assert.Equal("C..ctor(System.Int32 X, [System.Int32 Y = 123])", CSharpExtensions.GetSymbolInfo(model, baseWithargs).Symbol.ToTestDisplayString());
Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs).Select(m => m.ToTestDisplayString()));
Assert.Empty(model.GetMemberGroup(baseWithargs).Select(m => m.ToTestDisplayString()));
Assert.Empty(CSharpExtensions.GetMemberGroup(model, baseWithargs).Select(m => m.ToTestDisplayString()));
model.VerifyOperationTree(baseWithargs,
@"
IInvocationOperation ( C..ctor(System.Int32 X, [System.Int32 Y = 123])) (OperationKind.Invocation, Type: System.Void) (Syntax: ': this(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: ': this(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
");
var equalsValue = tree.GetRoot().DescendantNodes().OfType<EqualsValueClauseSyntax>().Last();
Assert.Equal("= 124", equalsValue.ToString());
model.VerifyOperationTree(equalsValue,
@"
IParameterInitializerOperation (Parameter: [System.Int32 Z = 124]) (OperationKind.ParameterInitializer, Type: null) (Syntax: '= 124')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 124) (Syntax: '124')
");
model.VerifyOperationTree(baseWithargs.Parent,
@"
IConstructorBodyOperation (OperationKind.ConstructorBody, Type: null) (Syntax: 'C(int X, in ... is(X, Y) {}')
Initializer:
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: ': this(X, Y)')
Expression:
IInvocationOperation ( C..ctor(System.Int32 X, [System.Int32 Y = 123])) (OperationKind.Invocation, Type: System.Void) (Syntax: ': this(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: ': this(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
BlockBody:
IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{}')
ExpressionBody:
null
");
}
}
......@@ -15082,6 +15221,13 @@ public Base(int X, int Y)
Assert.Same("<global namespace>", model.GetEnclosingSymbol(x.SpanStart).ToTestDisplayString());
Assert.Empty(model.LookupSymbols(x.SpanStart, name: "X"));
Assert.DoesNotContain("X", model.LookupNames(x.SpanStart));
var recordDeclarations = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().Skip(1).ToArray();
Assert.Equal("C", recordDeclarations[0].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[0]));
Assert.Equal("C", recordDeclarations[1].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[1]));
}
[Fact]
......@@ -15136,6 +15282,14 @@ public Base(int X, int Y)
Assert.Empty(model.LookupSymbols(x.SpanStart, name: "X"));
Assert.DoesNotContain("X", model.LookupNames(x.SpanStart));
}
var recordDeclarations = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().Skip(1).ToArray();
Assert.Equal("C", recordDeclarations[0].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[0]));
Assert.Equal("C", recordDeclarations[1].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[1]));
}
[Fact]
......@@ -15196,6 +15350,35 @@ public Base(int X, int Y)
Assert.Same("<global namespace>", model.GetEnclosingSymbol(x.SpanStart).ToTestDisplayString());
Assert.Empty(model.LookupSymbols(x.SpanStart, name: "X"));
Assert.DoesNotContain("X", model.LookupNames(x.SpanStart));
var recordDeclarations = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().Skip(1).ToArray();
Assert.Equal("C", recordDeclarations[0].Identifier.ValueText);
model.VerifyOperationTree(recordDeclarations[0],
@"
IConstructorBodyOperation (OperationKind.ConstructorBody, Type: null) (Syntax: 'partial rec ... }')
Initializer:
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'Base(X, Y)')
Expression:
IInvocationOperation ( Base..ctor(System.Int32 X, System.Int32 Y)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Base(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Base, IsImplicit) (Syntax: 'Base(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
BlockBody:
IBlockOperation (0 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'partial rec ... }')
ExpressionBody:
null
");
Assert.Equal("C", recordDeclarations[1].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[1]));
}
[Fact]
......@@ -15256,6 +15439,36 @@ public Base(int X, int Y)
Assert.Same("<global namespace>", model.GetEnclosingSymbol(x.SpanStart).ToTestDisplayString());
Assert.Empty(model.LookupSymbols(x.SpanStart, name: "X"));
Assert.DoesNotContain("X", model.LookupNames(x.SpanStart));
var recordDeclarations = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().Skip(1).ToArray();
Assert.Equal("C", recordDeclarations[0].Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclarations[0]));
Assert.Equal("C", recordDeclarations[1].Identifier.ValueText);
model.VerifyOperationTree(recordDeclarations[1],
@"
IConstructorBodyOperation (OperationKind.ConstructorBody, Type: null) (Syntax: 'partial rec ... }')
Initializer:
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'Base(X, Y)')
Expression:
IInvocationOperation ( Base..ctor(System.Int32 X, System.Int32 Y)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Base(X, Y)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Base, IsImplicit) (Syntax: 'Base(X, Y)')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: 'X')
IParameterReferenceOperation: X (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'X')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: Y) (OperationKind.Argument, Type: null) (Syntax: 'Y')
IParameterReferenceOperation: Y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'Y')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
BlockBody:
IBlockOperation (0 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'partial rec ... }')
ExpressionBody:
null
");
}
[Fact]
......@@ -16395,6 +16608,13 @@ static void Main()
}
}";
var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe);
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var recordDeclaration = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().First();
Assert.Null(model.GetOperation(recordDeclaration));
var verifier = CompileAndVerify(comp, expectedOutput:
@"True
True
......@@ -16792,6 +17012,14 @@ static void Main()
}
}";
var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe);
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var recordDeclaration = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().ElementAt(1);
Assert.Equal("B", recordDeclaration.Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclaration));
var verifier = CompileAndVerify(comp, expectedOutput:
@"True
False
......@@ -17926,6 +18154,10 @@ public static void Main()
Assert.Equal("System.Int32 C.Z", model.GetEnclosingSymbol(x.SpanStart).ToTestDisplayString());
Assert.Contains(symbol, model.LookupSymbols(x.SpanStart, name: "X"));
Assert.Contains("X", model.LookupNames(x.SpanStart));
var recordDeclaration = tree.GetRoot().DescendantNodes().OfType<RecordDeclarationSyntax>().Single();
Assert.Equal("C", recordDeclaration.Identifier.ValueText);
Assert.Null(model.GetOperation(recordDeclaration));
}
[Fact]
......@@ -18887,5 +19119,1601 @@ public static void Main()
Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("R2<T>", "T", "int").WithLocation(10, 20)
);
}
[Fact]
public void AnalyzerActions_01()
{
var text1 = @"
record A([Attr1]int X = 0) : I1
{}
record B([Attr2]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3]int Z = 4) : base(5)
{}
}
interface I1 {}
class Attr1 : System.Attribute {}
class Attr2 : System.Attribute {}
class Attr3 : System.Attribute {}
";
var analyzer = new AnalyzerActions_01_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount0);
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(1, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
Assert.Equal(1, analyzer.FireCount10);
Assert.Equal(1, analyzer.FireCount11);
Assert.Equal(1, analyzer.FireCount12);
Assert.Equal(1, analyzer.FireCount13);
Assert.Equal(1, analyzer.FireCount14);
Assert.Equal(1, analyzer.FireCount15);
Assert.Equal(1, analyzer.FireCount16);
Assert.Equal(1, analyzer.FireCount17);
Assert.Equal(1, analyzer.FireCount18);
Assert.Equal(1, analyzer.FireCount19);
Assert.Equal(1, analyzer.FireCount20);
Assert.Equal(1, analyzer.FireCount21);
Assert.Equal(1, analyzer.FireCount22);
Assert.Equal(1, analyzer.FireCount23);
Assert.Equal(1, analyzer.FireCount24);
Assert.Equal(1, analyzer.FireCount25);
Assert.Equal(1, analyzer.FireCount26);
Assert.Equal(1, analyzer.FireCount27);
Assert.Equal(1, analyzer.FireCount28);
Assert.Equal(1, analyzer.FireCount29);
Assert.Equal(1, analyzer.FireCount30);
Assert.Equal(1, analyzer.FireCount31);
}
private class AnalyzerActions_01_Analyzer : DiagnosticAnalyzer
{
public int FireCount0;
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
public int FireCount5;
public int FireCount6;
public int FireCount7;
public int FireCount8;
public int FireCount9;
public int FireCount10;
public int FireCount11;
public int FireCount12;
public int FireCount13;
public int FireCount14;
public int FireCount15;
public int FireCount16;
public int FireCount17;
public int FireCount18;
public int FireCount19;
public int FireCount20;
public int FireCount21;
public int FireCount22;
public int FireCount23;
public int FireCount24;
public int FireCount25;
public int FireCount26;
public int FireCount27;
public int FireCount28;
public int FireCount29;
public int FireCount30;
public int FireCount31;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(Handle1, SyntaxKind.NumericLiteralExpression);
context.RegisterSyntaxNodeAction(Handle2, SyntaxKind.EqualsValueClause);
context.RegisterSyntaxNodeAction(Handle3, SyntaxKind.BaseConstructorInitializer);
context.RegisterSyntaxNodeAction(Handle4, SyntaxKind.ConstructorDeclaration);
context.RegisterSyntaxNodeAction(Handle5, SyntaxKind.PrimaryConstructorBaseType);
context.RegisterSyntaxNodeAction(Handle6, SyntaxKind.RecordDeclaration);
context.RegisterSyntaxNodeAction(Handle7, SyntaxKind.IdentifierName);
context.RegisterSyntaxNodeAction(Handle8, SyntaxKind.SimpleBaseType);
context.RegisterSyntaxNodeAction(Handle9, SyntaxKind.ParameterList);
context.RegisterSyntaxNodeAction(Handle10, SyntaxKind.ArgumentList);
}
protected void Handle1(SyntaxNodeAnalysisContext context)
{
var literal = (LiteralExpressionSyntax)context.Node;
switch (literal.ToString())
{
case "0":
Interlocked.Increment(ref FireCount0);
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
break;
case "1":
Interlocked.Increment(ref FireCount1);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "2":
Interlocked.Increment(ref FireCount2);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "3":
Interlocked.Increment(ref FireCount3);
Assert.Equal("System.Int32 B.M()", context.ContainingSymbol.ToTestDisplayString());
break;
case "4":
Interlocked.Increment(ref FireCount4);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
case "5":
Interlocked.Increment(ref FireCount5);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
default:
Assert.True(false);
break;
}
Assert.Same(literal.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
}
protected void Handle2(SyntaxNodeAnalysisContext context)
{
var equalsValue = (EqualsValueClauseSyntax)context.Node;
switch (equalsValue.ToString())
{
case "= 0":
Interlocked.Increment(ref FireCount15);
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
break;
case "= 1":
Interlocked.Increment(ref FireCount16);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "= 4":
Interlocked.Increment(ref FireCount6);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
default:
Assert.True(false);
break;
}
Assert.Same(equalsValue.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
}
protected void Handle3(SyntaxNodeAnalysisContext context)
{
var initializer = (ConstructorInitializerSyntax)context.Node;
switch (initializer.ToString())
{
case ": base(5)":
Interlocked.Increment(ref FireCount7);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
default:
Assert.True(false);
break;
}
Assert.Same(initializer.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
}
protected void Handle4(SyntaxNodeAnalysisContext context)
{
Interlocked.Increment(ref FireCount8);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
}
protected void Handle5(SyntaxNodeAnalysisContext context)
{
var baseType = (PrimaryConstructorBaseTypeSyntax)context.Node;
switch (baseType.ToString())
{
case "A(2)":
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount9);
break;
case "B":
Interlocked.Increment(ref FireCount17);
break;
default:
Assert.True(false);
break;
}
break;
default:
Assert.True(false);
break;
}
Assert.Same(baseType.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
}
protected void Handle6(SyntaxNodeAnalysisContext context)
{
var record = (RecordDeclarationSyntax)context.Node;
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount10);
break;
case "B":
Interlocked.Increment(ref FireCount11);
break;
case "A":
Interlocked.Increment(ref FireCount12);
break;
case "C":
Interlocked.Increment(ref FireCount13);
break;
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount14);
break;
default:
Assert.True(false);
break;
}
Assert.Same(record.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
}
protected void Handle7(SyntaxNodeAnalysisContext context)
{
var identifier = (IdentifierNameSyntax)context.Node;
switch (identifier.Identifier.ValueText)
{
case "A":
switch (identifier.Parent!.ToString())
{
case "A(2)":
Interlocked.Increment(ref FireCount18);
Assert.Equal("B", context.ContainingSymbol.ToTestDisplayString());
break;
case "A":
Interlocked.Increment(ref FireCount19);
Assert.Equal(SyntaxKind.SimpleBaseType, identifier.Parent.Kind());
Assert.Equal("C", context.ContainingSymbol.ToTestDisplayString());
break;
default:
Assert.True(false);
break;
}
break;
case "Attr1":
Interlocked.Increment(ref FireCount24);
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
break;
case "Attr2":
Interlocked.Increment(ref FireCount25);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "Attr3":
Interlocked.Increment(ref FireCount26);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
}
}
protected void Handle8(SyntaxNodeAnalysisContext context)
{
var baseType = (SimpleBaseTypeSyntax)context.Node;
switch (baseType.ToString())
{
case "I1":
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "A":
Interlocked.Increment(ref FireCount20);
break;
case "B":
Interlocked.Increment(ref FireCount21);
break;
case "C":
Interlocked.Increment(ref FireCount22);
break;
default:
Assert.True(false);
break;
}
break;
case "A":
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "C":
Interlocked.Increment(ref FireCount23);
break;
default:
Assert.True(false);
break;
}
break;
case "System.Attribute":
break;
default:
Assert.True(false);
break;
}
}
protected void Handle9(SyntaxNodeAnalysisContext context)
{
var parameterList = (ParameterListSyntax)context.Node;
switch (parameterList.ToString())
{
case "([Attr1]int X = 0)":
Interlocked.Increment(ref FireCount27);
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
break;
case "([Attr2]int Y = 1)":
Interlocked.Increment(ref FireCount28);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "([Attr3]int Z = 4)":
Interlocked.Increment(ref FireCount29);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
case "()":
break;
default:
Assert.True(false);
break;
}
}
protected void Handle10(SyntaxNodeAnalysisContext context)
{
var argumentList = (ArgumentListSyntax)context.Node;
switch (argumentList.ToString())
{
case "(2)":
Interlocked.Increment(ref FireCount30);
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
break;
case "(5)":
Interlocked.Increment(ref FireCount31);
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_02()
{
var text1 = @"
record A(int X = 0)
{}
record C
{
C(int Z = 4)
{}
}
";
var analyzer = new AnalyzerActions_02_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
}
private class AnalyzerActions_02_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
public int FireCount5;
public int FireCount6;
public int FireCount7;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(Handle, SymbolKind.Method);
context.RegisterSymbolAction(Handle, SymbolKind.Property);
context.RegisterSymbolAction(Handle, SymbolKind.Parameter);
context.RegisterSymbolAction(Handle, SymbolKind.NamedType);
}
private void Handle(SymbolAnalysisContext context)
{
switch (context.Symbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1);
break;
case "System.Int32 A.X { get; init; }":
Interlocked.Increment(ref FireCount2);
break;
case "[System.Int32 X = 0]":
Interlocked.Increment(ref FireCount3);
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount4);
break;
case "[System.Int32 Z = 4]":
Interlocked.Increment(ref FireCount5);
break;
case "A":
Interlocked.Increment(ref FireCount6);
break;
case "C":
Interlocked.Increment(ref FireCount7);
break;
case "System.Runtime.CompilerServices.IsExternalInit":
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_03()
{
var text1 = @"
record A(int X = 0)
{}
record C
{
C(int Z = 4)
{}
}
";
var analyzer = new AnalyzerActions_03_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(0, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(0, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(1, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
Assert.Equal(1, analyzer.FireCount10);
Assert.Equal(1, analyzer.FireCount11);
Assert.Equal(1, analyzer.FireCount12);
}
private class AnalyzerActions_03_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
public int FireCount5;
public int FireCount6;
public int FireCount7;
public int FireCount8;
public int FireCount9;
public int FireCount10;
public int FireCount11;
public int FireCount12;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolStartAction(Handle1, SymbolKind.Method);
context.RegisterSymbolStartAction(Handle1, SymbolKind.Property);
context.RegisterSymbolStartAction(Handle1, SymbolKind.Parameter);
context.RegisterSymbolStartAction(Handle1, SymbolKind.NamedType);
}
private void Handle1(SymbolStartAnalysisContext context)
{
switch (context.Symbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1);
context.RegisterSymbolEndAction(Handle2);
break;
case "System.Int32 A.X { get; init; }":
Interlocked.Increment(ref FireCount2);
context.RegisterSymbolEndAction(Handle3);
break;
case "[System.Int32 X = 0]":
Interlocked.Increment(ref FireCount3);
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount4);
context.RegisterSymbolEndAction(Handle4);
break;
case "[System.Int32 Z = 4]":
Interlocked.Increment(ref FireCount5);
break;
case "A":
Interlocked.Increment(ref FireCount9);
Assert.Equal(0, FireCount1);
Assert.Equal(0, FireCount2);
Assert.Equal(0, FireCount6);
Assert.Equal(0, FireCount7);
context.RegisterSymbolEndAction(Handle5);
break;
case "C":
Interlocked.Increment(ref FireCount10);
Assert.Equal(0, FireCount4);
Assert.Equal(0, FireCount8);
context.RegisterSymbolEndAction(Handle6);
break;
case "System.Runtime.CompilerServices.IsExternalInit":
break;
default:
Assert.True(false);
break;
}
}
private void Handle2(SymbolAnalysisContext context)
{
Assert.Equal("A..ctor([System.Int32 X = 0])", context.Symbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount6);
}
private void Handle3(SymbolAnalysisContext context)
{
Assert.Equal("System.Int32 A.X { get; init; }", context.Symbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount7);
}
private void Handle4(SymbolAnalysisContext context)
{
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.Symbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount8);
}
private void Handle5(SymbolAnalysisContext context)
{
Assert.Equal("A", context.Symbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount11);
Assert.Equal(1, FireCount1);
Assert.Equal(1, FireCount2);
Assert.Equal(1, FireCount6);
Assert.Equal(1, FireCount7);
}
private void Handle6(SymbolAnalysisContext context)
{
Assert.Equal("C", context.Symbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount12);
Assert.Equal(1, FireCount4);
Assert.Equal(1, FireCount8);
}
}
[Fact]
public void AnalyzerActions_04()
{
var text1 = @"
record A([Attr1(100)]int X = 0) : I1
{}
record B([Attr2(200)]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3(300)]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_04_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(0, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(1, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
Assert.Equal(1, analyzer.FireCount10);
Assert.Equal(1, analyzer.FireCount11);
Assert.Equal(1, analyzer.FireCount12);
Assert.Equal(1, analyzer.FireCount13);
Assert.Equal(1, analyzer.FireCount14);
Assert.Equal(1, analyzer.FireCount15);
Assert.Equal(1, analyzer.FireCount16);
Assert.Equal(1, analyzer.FireCount17);
}
private class AnalyzerActions_04_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
public int FireCount5;
public int FireCount6;
public int FireCount7;
public int FireCount8;
public int FireCount9;
public int FireCount10;
public int FireCount11;
public int FireCount12;
public int FireCount13;
public int FireCount14;
public int FireCount15;
public int FireCount16;
public int FireCount17;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationAction(Handle1, OperationKind.ConstructorBody);
context.RegisterOperationAction(Handle2, OperationKind.Invocation);
context.RegisterOperationAction(Handle3, OperationKind.Literal);
context.RegisterOperationAction(Handle4, OperationKind.ParameterInitializer);
context.RegisterOperationAction(Handle5, OperationKind.PropertyInitializer);
context.RegisterOperationAction(Handle5, OperationKind.FieldInitializer);
}
protected void Handle1(OperationAnalysisContext context)
{
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1);
Assert.Equal(SyntaxKind.RecordDeclaration, context.Operation.Syntax.Kind());
break;
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount2);
Assert.Equal(SyntaxKind.RecordDeclaration, context.Operation.Syntax.Kind());
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount3);
Assert.Equal(SyntaxKind.ConstructorDeclaration, context.Operation.Syntax.Kind());
break;
default:
Assert.True(false);
break;
}
}
protected void Handle2(OperationAnalysisContext context)
{
switch (context.ContainingSymbol.ToTestDisplayString())
{
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount4);
Assert.Equal(SyntaxKind.PrimaryConstructorBaseType, context.Operation.Syntax.Kind());
VerifyOperationTree((CSharpCompilation)context.Compilation, context.Operation,
@"
IInvocationOperation ( A..ctor([System.Int32 X = 0])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'A(2)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: 'A(2)')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: '2')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
");
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount5);
Assert.Equal(SyntaxKind.BaseConstructorInitializer, context.Operation.Syntax.Kind());
VerifyOperationTree((CSharpCompilation)context.Compilation, context.Operation,
@"
IInvocationOperation ( A..ctor([System.Int32 X = 0])) (OperationKind.Invocation, Type: System.Void) (Syntax: ': base(5)')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: A, IsImplicit) (Syntax: ': base(5)')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: X) (OperationKind.Argument, Type: null) (Syntax: '5')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
");
break;
default:
Assert.True(false);
break;
}
}
protected void Handle3(OperationAnalysisContext context)
{
switch (context.Operation.Syntax.ToString())
{
case "100":
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount6);
break;
case "0":
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount7);
break;
case "200":
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount8);
break;
case "1":
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount9);
break;
case "2":
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount10);
break;
case "300":
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount11);
break;
case "4":
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount12);
break;
case "5":
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount13);
break;
case "3":
Assert.Equal("System.Int32 B.M()", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount17);
break;
default:
Assert.True(false);
break;
}
}
protected void Handle4(OperationAnalysisContext context)
{
switch (context.Operation.Syntax.ToString())
{
case "= 0":
Assert.Equal("A..ctor([System.Int32 X = 0])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount14);
break;
case "= 1":
Assert.Equal("B..ctor([System.Int32 Y = 1])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount15);
break;
case "= 4":
Assert.Equal("C..ctor([System.Int32 Z = 4])", context.ContainingSymbol.ToTestDisplayString());
Interlocked.Increment(ref FireCount16);
break;
default:
Assert.True(false);
break;
}
}
protected void Handle5(OperationAnalysisContext context)
{
Assert.True(false);
}
}
[Fact]
public void AnalyzerActions_05()
{
var text1 = @"
record A([Attr1(100)]int X = 0) : I1
{}
record B([Attr2(200)]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3(300)]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_05_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
}
private class AnalyzerActions_05_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationBlockAction(Handle);
}
private void Handle(OperationBlockAnalysisContext context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1);
Assert.Equal(2, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString());
break;
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount2);
Assert.Equal(3, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind);
Assert.Equal("A(2)", context.OperationBlocks[2].Syntax.ToString());
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount3);
Assert.Equal(4, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind);
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[3].Kind);
Assert.Equal(": base(5)", context.OperationBlocks[3].Syntax.ToString());
break;
case "System.Int32 B.M()":
Interlocked.Increment(ref FireCount4);
Assert.Equal(1, context.OperationBlocks.Length);
Assert.Equal(OperationKind.Block, context.OperationBlocks[0].Kind);
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_06()
{
var text1 = @"
record A([Attr1(100)]int X = 0) : I1
{}
record B([Attr2(200)]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3(300)]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_06_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount100);
Assert.Equal(1, analyzer.FireCount200);
Assert.Equal(1, analyzer.FireCount300);
Assert.Equal(1, analyzer.FireCount400);
Assert.Equal(0, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(1, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
Assert.Equal(1, analyzer.FireCount10);
Assert.Equal(1, analyzer.FireCount11);
Assert.Equal(1, analyzer.FireCount12);
Assert.Equal(1, analyzer.FireCount13);
Assert.Equal(1, analyzer.FireCount14);
Assert.Equal(1, analyzer.FireCount15);
Assert.Equal(1, analyzer.FireCount16);
Assert.Equal(1, analyzer.FireCount17);
Assert.Equal(1, analyzer.FireCount1000);
Assert.Equal(1, analyzer.FireCount2000);
Assert.Equal(1, analyzer.FireCount3000);
Assert.Equal(1, analyzer.FireCount4000);
}
private class AnalyzerActions_06_Analyzer : AnalyzerActions_04_Analyzer
{
public int FireCount100;
public int FireCount200;
public int FireCount300;
public int FireCount400;
public int FireCount1000;
public int FireCount2000;
public int FireCount3000;
public int FireCount4000;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationBlockStartAction(Handle);
}
private void Handle(OperationBlockStartAnalysisContext context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount100);
Assert.Equal(2, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString());
RegisterOperationAction(context);
context.RegisterOperationBlockEndAction(Handle6);
break;
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount200);
Assert.Equal(3, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind);
Assert.Equal("A(2)", context.OperationBlocks[2].Syntax.ToString());
RegisterOperationAction(context);
context.RegisterOperationBlockEndAction(Handle6);
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount300);
Assert.Equal(4, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind);
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[3].Kind);
Assert.Equal(": base(5)", context.OperationBlocks[3].Syntax.ToString());
RegisterOperationAction(context);
context.RegisterOperationBlockEndAction(Handle6);
break;
case "System.Int32 B.M()":
Interlocked.Increment(ref FireCount400);
Assert.Equal(1, context.OperationBlocks.Length);
Assert.Equal(OperationKind.Block, context.OperationBlocks[0].Kind);
RegisterOperationAction(context);
context.RegisterOperationBlockEndAction(Handle6);
break;
default:
Assert.True(false);
break;
}
}
private void RegisterOperationAction(OperationBlockStartAnalysisContext context)
{
context.RegisterOperationAction(Handle1, OperationKind.ConstructorBody);
context.RegisterOperationAction(Handle2, OperationKind.Invocation);
context.RegisterOperationAction(Handle3, OperationKind.Literal);
context.RegisterOperationAction(Handle4, OperationKind.ParameterInitializer);
context.RegisterOperationAction(Handle5, OperationKind.PropertyInitializer);
context.RegisterOperationAction(Handle5, OperationKind.FieldInitializer);
}
private void Handle6(OperationBlockAnalysisContext context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1000);
Assert.Equal(2, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 0", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr1(100)", context.OperationBlocks[1].Syntax.ToString());
break;
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount2000);
Assert.Equal(3, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 1", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr2(200)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[2].Kind);
Assert.Equal("A(2)", context.OperationBlocks[2].Syntax.ToString());
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount3000);
Assert.Equal(4, context.OperationBlocks.Length);
Assert.Equal(OperationKind.ParameterInitializer, context.OperationBlocks[0].Kind);
Assert.Equal("= 4", context.OperationBlocks[0].Syntax.ToString());
Assert.Equal(OperationKind.None, context.OperationBlocks[1].Kind);
Assert.Equal("Attr3(300)", context.OperationBlocks[1].Syntax.ToString());
Assert.Equal(OperationKind.Block, context.OperationBlocks[2].Kind);
Assert.Equal(OperationKind.Invocation, context.OperationBlocks[3].Kind);
Assert.Equal(": base(5)", context.OperationBlocks[3].Syntax.ToString());
break;
case "System.Int32 B.M()":
Interlocked.Increment(ref FireCount4000);
Assert.Equal(1, context.OperationBlocks.Length);
Assert.Equal(OperationKind.Block, context.OperationBlocks[0].Kind);
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_07()
{
var text1 = @"
record A([Attr1(100)]int X = 0) : I1
{}
record B([Attr2(200)]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3(300)]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_07_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
}
private class AnalyzerActions_07_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterCodeBlockAction(Handle);
}
private void Handle(CodeBlockAnalysisContext context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "A" } }:
Interlocked.Increment(ref FireCount1);
break;
default:
Assert.True(false);
break;
}
break;
case "B..ctor([System.Int32 Y = 1])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "B" } }:
Interlocked.Increment(ref FireCount2);
break;
default:
Assert.True(false);
break;
}
break;
case "C..ctor([System.Int32 Z = 4])":
switch (context.CodeBlock)
{
case ConstructorDeclarationSyntax { Identifier: { ValueText: "C" } }:
Interlocked.Increment(ref FireCount3);
break;
default:
Assert.True(false);
break;
}
break;
case "System.Int32 B.M()":
switch (context.CodeBlock)
{
case MethodDeclarationSyntax { Identifier: { ValueText: "M" } }:
Interlocked.Increment(ref FireCount4);
break;
default:
Assert.True(false);
break;
}
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_08()
{
var text1 = @"
record A([Attr1]int X = 0) : I1
{}
record B([Attr2]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_08_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount100);
Assert.Equal(1, analyzer.FireCount200);
Assert.Equal(1, analyzer.FireCount300);
Assert.Equal(1, analyzer.FireCount400);
Assert.Equal(1, analyzer.FireCount0);
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(0, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
Assert.Equal(0, analyzer.FireCount10);
Assert.Equal(0, analyzer.FireCount11);
Assert.Equal(0, analyzer.FireCount12);
Assert.Equal(0, analyzer.FireCount13);
Assert.Equal(0, analyzer.FireCount14);
Assert.Equal(1, analyzer.FireCount15);
Assert.Equal(1, analyzer.FireCount16);
Assert.Equal(0, analyzer.FireCount17);
Assert.Equal(0, analyzer.FireCount18);
Assert.Equal(0, analyzer.FireCount19);
Assert.Equal(0, analyzer.FireCount20);
Assert.Equal(0, analyzer.FireCount21);
Assert.Equal(0, analyzer.FireCount22);
Assert.Equal(0, analyzer.FireCount23);
Assert.Equal(1, analyzer.FireCount24);
Assert.Equal(1, analyzer.FireCount25);
Assert.Equal(1, analyzer.FireCount26);
Assert.Equal(0, analyzer.FireCount27);
Assert.Equal(0, analyzer.FireCount28);
Assert.Equal(0, analyzer.FireCount29);
Assert.Equal(1, analyzer.FireCount30);
Assert.Equal(1, analyzer.FireCount31);
Assert.Equal(1, analyzer.FireCount1000);
Assert.Equal(1, analyzer.FireCount2000);
Assert.Equal(1, analyzer.FireCount3000);
Assert.Equal(1, analyzer.FireCount4000);
}
private class AnalyzerActions_08_Analyzer : AnalyzerActions_01_Analyzer
{
public int FireCount100;
public int FireCount200;
public int FireCount300;
public int FireCount400;
public int FireCount1000;
public int FireCount2000;
public int FireCount3000;
public int FireCount4000;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterCodeBlockStartAction<SyntaxKind>(Handle);
}
private void Handle(CodeBlockStartAnalysisContext<SyntaxKind> context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "A" } }:
Interlocked.Increment(ref FireCount100);
break;
default:
Assert.True(false);
break;
}
break;
case "B..ctor([System.Int32 Y = 1])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "B" } }:
Interlocked.Increment(ref FireCount200);
break;
default:
Assert.True(false);
break;
}
break;
case "C..ctor([System.Int32 Z = 4])":
switch (context.CodeBlock)
{
case ConstructorDeclarationSyntax { Identifier: { ValueText: "C" } }:
Interlocked.Increment(ref FireCount300);
break;
default:
Assert.True(false);
break;
}
break;
case "System.Int32 B.M()":
switch (context.CodeBlock)
{
case MethodDeclarationSyntax { Identifier: { ValueText: "M" } }:
Interlocked.Increment(ref FireCount400);
break;
default:
Assert.True(false);
break;
}
break;
default:
Assert.True(false);
break;
}
context.RegisterSyntaxNodeAction(Handle1, SyntaxKind.NumericLiteralExpression);
context.RegisterSyntaxNodeAction(Handle2, SyntaxKind.EqualsValueClause);
context.RegisterSyntaxNodeAction(Handle3, SyntaxKind.BaseConstructorInitializer);
context.RegisterSyntaxNodeAction(Handle4, SyntaxKind.ConstructorDeclaration);
context.RegisterSyntaxNodeAction(Handle5, SyntaxKind.PrimaryConstructorBaseType);
context.RegisterSyntaxNodeAction(Handle6, SyntaxKind.RecordDeclaration);
context.RegisterSyntaxNodeAction(Handle7, SyntaxKind.IdentifierName);
context.RegisterSyntaxNodeAction(Handle8, SyntaxKind.SimpleBaseType);
context.RegisterSyntaxNodeAction(Handle9, SyntaxKind.ParameterList);
context.RegisterSyntaxNodeAction(Handle10, SyntaxKind.ArgumentList);
context.RegisterCodeBlockEndAction(Handle11);
}
private void Handle11(CodeBlockAnalysisContext context)
{
switch (context.OwningSymbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "A" } }:
Interlocked.Increment(ref FireCount1000);
break;
default:
Assert.True(false);
break;
}
break;
case "B..ctor([System.Int32 Y = 1])":
switch (context.CodeBlock)
{
case RecordDeclarationSyntax { Identifier: { ValueText: "B" } }:
Interlocked.Increment(ref FireCount2000);
break;
default:
Assert.True(false);
break;
}
break;
case "C..ctor([System.Int32 Z = 4])":
switch (context.CodeBlock)
{
case ConstructorDeclarationSyntax { Identifier: { ValueText: "C" } }:
Interlocked.Increment(ref FireCount3000);
break;
default:
Assert.True(false);
break;
}
break;
case "System.Int32 B.M()":
switch (context.CodeBlock)
{
case MethodDeclarationSyntax { Identifier: { ValueText: "M" } }:
Interlocked.Increment(ref FireCount4000);
break;
default:
Assert.True(false);
break;
}
break;
default:
Assert.True(false);
break;
}
}
}
[Fact]
public void AnalyzerActions_09()
{
var text1 = @"
record A([Attr1(100)]int X = 0) : I1
{}
record B([Attr2(200)]int Y = 1) : A(2), I1
{
int M() => 3;
}
record C : A, I1
{
C([Attr3(300)]int Z = 4) : base(5)
{}
}
interface I1 {}
";
var analyzer = new AnalyzerActions_09_Analyzer();
var comp = CreateCompilation(text1);
comp.GetAnalyzerDiagnostics(new[] { analyzer }, null).Verify();
Assert.Equal(1, analyzer.FireCount1);
Assert.Equal(1, analyzer.FireCount2);
Assert.Equal(1, analyzer.FireCount3);
Assert.Equal(1, analyzer.FireCount4);
Assert.Equal(1, analyzer.FireCount5);
Assert.Equal(1, analyzer.FireCount6);
Assert.Equal(1, analyzer.FireCount7);
Assert.Equal(1, analyzer.FireCount8);
Assert.Equal(1, analyzer.FireCount9);
}
private class AnalyzerActions_09_Analyzer : DiagnosticAnalyzer
{
public int FireCount1;
public int FireCount2;
public int FireCount3;
public int FireCount4;
public int FireCount5;
public int FireCount6;
public int FireCount7;
public int FireCount8;
public int FireCount9;
private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(Handle1, SymbolKind.Method);
context.RegisterSymbolAction(Handle2, SymbolKind.Property);
context.RegisterSymbolAction(Handle3, SymbolKind.Parameter);
}
private void Handle1(SymbolAnalysisContext context)
{
switch (context.Symbol.ToTestDisplayString())
{
case "A..ctor([System.Int32 X = 0])":
Interlocked.Increment(ref FireCount1);
break;
case "B..ctor([System.Int32 Y = 1])":
Interlocked.Increment(ref FireCount2);
break;
case "C..ctor([System.Int32 Z = 4])":
Interlocked.Increment(ref FireCount3);
break;
case "System.Int32 B.M()":
Interlocked.Increment(ref FireCount4);
break;
default:
Assert.True(false);
break;
}
}
private void Handle2(SymbolAnalysisContext context)
{
switch (context.Symbol.ToTestDisplayString())
{
case "System.Int32 A.X { get; init; }":
Interlocked.Increment(ref FireCount5);
break;
case "System.Int32 B.Y { get; init; }":
Interlocked.Increment(ref FireCount6);
break;
default:
Assert.True(false);
break;
}
}
private void Handle3(SymbolAnalysisContext context)
{
switch (context.Symbol.ToTestDisplayString())
{
case "[System.Int32 X = 0]":
Interlocked.Increment(ref FireCount7);
break;
case "[System.Int32 Y = 1]":
Interlocked.Increment(ref FireCount8);
break;
case "[System.Int32 Z = 4]":
Interlocked.Increment(ref FireCount9);
break;
default:
Assert.True(false);
break;
}
}
}
}
}
......@@ -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.
先完成此消息的编辑!
想要评论请 注册