Skip to content

Commit 362b715

Browse files
author
Julien Couvreur
authored
Extensions: analyzer actions (#78319)
1 parent bdf23fd commit 362b715

File tree

3 files changed

+246
-3
lines changed

3 files changed

+246
-3
lines changed

src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ private static void ComputeDeclarations(
102102
case SyntaxKind.StructDeclaration:
103103
case SyntaxKind.RecordDeclaration:
104104
case SyntaxKind.RecordStructDeclaration:
105-
// Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for analyzers
106105
{
107106
if (associatedSymbol is IMethodSymbol ctor)
108107
{
@@ -123,6 +122,7 @@ private static void ComputeDeclarations(
123122
goto case SyntaxKind.InterfaceDeclaration;
124123
}
125124
case SyntaxKind.InterfaceDeclaration:
125+
case SyntaxKind.ExtensionDeclaration:
126126
{
127127
var t = (TypeDeclarationSyntax)node;
128128
foreach (var decl in t.Members)

src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55

66
using System;
77
using System.Collections;
8+
using System.Collections.Concurrent;
89
using System.Collections.Generic;
10+
using System.Collections.Immutable;
911
using System.Linq;
1012
using Microsoft.CodeAnalysis.CSharp.Symbols;
1113
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
1214
using Microsoft.CodeAnalysis.CSharp.Syntax;
1315
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
16+
using Microsoft.CodeAnalysis.Diagnostics;
1417
using Microsoft.CodeAnalysis.Emit;
1518
using Microsoft.CodeAnalysis.Test.Utilities;
1619
using Microsoft.CodeAnalysis.VisualBasic;
@@ -37287,4 +37290,237 @@ static class E
3728737290
var model = comp.GetSemanticModel(tree);
3728837291
Assert.Equal(["(T, null)", "(T, T)"], PrintXmlNameSymbols(tree, model));
3728937292
}
37293+
37294+
[Fact]
37295+
public void AnalyzerActions_01()
37296+
{
37297+
var src = """
37298+
static class E
37299+
{
37300+
extension<T>([Attr] T t)
37301+
{
37302+
[Attr2]
37303+
public void M() { }
37304+
37305+
[Attr3]
37306+
public int P => 0;
37307+
}
37308+
}
37309+
""";
37310+
37311+
var analyzer = new AnalyzerActions_01_Analyzer();
37312+
var comp = CreateCompilation(src);
37313+
comp.GetAnalyzerDiagnostics([analyzer], null).Verify();
37314+
37315+
AssertEx.SetEqual([
37316+
"Attr2 -> void E.<>E__0<T>.M()",
37317+
"M -> void E.<>E__0<T>.M()",
37318+
"Attr3 -> System.Int32 E.<>E__0<T>.P { get; }",
37319+
"P -> System.Int32 E.<>E__0<T>.P { get; }",
37320+
"T -> E.<>E__0<T>",
37321+
"Attr -> E.<>E__0<T>",
37322+
"extension -> E.<>E__0<T>"],
37323+
analyzer._results.ToArray());
37324+
}
37325+
37326+
private class AnalyzerActions_01_Analyzer : DiagnosticAnalyzer
37327+
{
37328+
public ConcurrentQueue<string> _results = new ConcurrentQueue<string>();
37329+
37330+
private static readonly DiagnosticDescriptor Descriptor =
37331+
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
37332+
37333+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
37334+
37335+
public override void Initialize(AnalysisContext context)
37336+
{
37337+
context.RegisterSyntaxNodeAction(handle, SyntaxKind.ExtensionDeclaration);
37338+
context.RegisterSyntaxNodeAction(handle, SyntaxKind.IdentifierName);
37339+
context.RegisterSyntaxNodeAction(handle, SyntaxKind.MethodDeclaration);
37340+
context.RegisterSyntaxNodeAction(handle, SyntaxKind.PropertyDeclaration);
37341+
37342+
void handle(SyntaxNodeAnalysisContext context)
37343+
{
37344+
_results.Enqueue(print(context));
37345+
Assert.Same(context.Node.SyntaxTree, context.ContainingSymbol!.DeclaringSyntaxReferences.Single().SyntaxTree);
37346+
}
37347+
37348+
static string print(SyntaxNodeAnalysisContext context)
37349+
{
37350+
var syntaxString = context.Node switch
37351+
{
37352+
ExtensionDeclarationSyntax => "extension",
37353+
MethodDeclarationSyntax method => method.Identifier.ValueText,
37354+
PropertyDeclarationSyntax property => property.Identifier.ValueText,
37355+
_ => context.Node.ToString()
37356+
};
37357+
37358+
return $"{syntaxString} -> {context.ContainingSymbol.ToTestDisplayString()}";
37359+
}
37360+
}
37361+
}
37362+
37363+
[Fact]
37364+
public void AnalyzerActions_02()
37365+
{
37366+
var src = """
37367+
static class E
37368+
{
37369+
extension<T>(T t)
37370+
{
37371+
public void M(int i) { }
37372+
public int P => 0;
37373+
}
37374+
extension(__arglist) { }
37375+
extension(object o1, object o2) { }
37376+
}
37377+
""";
37378+
37379+
var analyzer = new AnalyzerActions_02_Analyzer();
37380+
var comp = CreateCompilation(src);
37381+
comp.GetAnalyzerDiagnostics([analyzer], null).Verify();
37382+
37383+
AssertEx.SetEqual([
37384+
"E",
37385+
"E.<>E__0<T>",
37386+
"System.Int32 E.<>E__0<T>.P { get; }",
37387+
"T t",
37388+
"E.<>E__1",
37389+
"E.<>E__2",
37390+
"System.Object o1",
37391+
"void E.<>E__0<T>.M(System.Int32 i)",
37392+
"System.Int32 i",
37393+
"System.Int32 E.<>E__0<T>.P.get"],
37394+
analyzer._results.ToArray());
37395+
}
37396+
37397+
private class AnalyzerActions_02_Analyzer : DiagnosticAnalyzer
37398+
{
37399+
public ConcurrentQueue<string> _results = new ConcurrentQueue<string>();
37400+
37401+
private static readonly DiagnosticDescriptor Descriptor =
37402+
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
37403+
37404+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
37405+
37406+
public override void Initialize(AnalysisContext context)
37407+
{
37408+
context.RegisterSymbolAction(handle, SymbolKind.NamedType);
37409+
context.RegisterSymbolAction(handle, SymbolKind.Parameter);
37410+
context.RegisterSymbolAction(handle, SymbolKind.TypeParameter);
37411+
context.RegisterSymbolAction(handle, SymbolKind.Method);
37412+
context.RegisterSymbolAction(handle, SymbolKind.Property);
37413+
37414+
void handle(SymbolAnalysisContext context)
37415+
{
37416+
_results.Enqueue(context.Symbol.ToTestDisplayString());
37417+
}
37418+
}
37419+
}
37420+
37421+
[Fact]
37422+
public void AnalyzerActions_03()
37423+
{
37424+
var src = """
37425+
static class E
37426+
{
37427+
extension<T>(T t)
37428+
{
37429+
public void M() { }
37430+
public int P { get { return 0; } }
37431+
}
37432+
}
37433+
""";
37434+
37435+
var analyzer = new AnalyzerActions_03_Analyzer();
37436+
var comp = CreateCompilation(src);
37437+
comp.GetAnalyzerDiagnostics([analyzer], null).Verify();
37438+
37439+
AssertEx.SetEqual([
37440+
"public void M() { } -> void E.<>E__0<T>.M()",
37441+
"get { return 0; } -> System.Int32 E.<>E__0<T>.P.get"],
37442+
analyzer._results.ToArray());
37443+
}
37444+
37445+
private class AnalyzerActions_03_Analyzer : DiagnosticAnalyzer
37446+
{
37447+
public ConcurrentQueue<string> _results = new ConcurrentQueue<string>();
37448+
37449+
private static readonly DiagnosticDescriptor Descriptor =
37450+
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
37451+
37452+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
37453+
37454+
public override void Initialize(AnalysisContext context)
37455+
{
37456+
context.RegisterOperationAction(handle, OperationKind.MethodBody);
37457+
37458+
void handle(OperationAnalysisContext context)
37459+
{
37460+
_results.Enqueue($"{context.Operation.Syntax.ToString()} -> {context.ContainingSymbol.ToTestDisplayString()}");
37461+
}
37462+
}
37463+
}
37464+
37465+
[Fact]
37466+
public void AnalyzerActions_04()
37467+
{
37468+
var src = """
37469+
static class E
37470+
{
37471+
extension<T>(T t)
37472+
{
37473+
public void M(int i) { }
37474+
public int P { get { return 0; } }
37475+
}
37476+
}
37477+
""";
37478+
37479+
var analyzer = new AnalyzerActions_04_Analyzer();
37480+
var comp = CreateCompilation(src);
37481+
comp.GetAnalyzerDiagnostics([analyzer], null).Verify();
37482+
37483+
AssertEx.SetEqual([
37484+
"Start: E",
37485+
"Start: E.<>E__0<T>",
37486+
"Start: void E.<>E__0<T>.M(System.Int32 i)",
37487+
"Start: System.Int32 E.<>E__0<T>.P { get; }",
37488+
"Start: System.Int32 E.<>E__0<T>.P.get",
37489+
"End: System.Int32 E.<>E__0<T>.P { get; }",
37490+
"End: System.Int32 E.<>E__0<T>.P.get",
37491+
"End: void E.<>E__0<T>.M(System.Int32 i)",
37492+
"End: E.<>E__0<T>",
37493+
"End: E"],
37494+
analyzer._results.ToArray());
37495+
}
37496+
37497+
private class AnalyzerActions_04_Analyzer : DiagnosticAnalyzer
37498+
{
37499+
public ConcurrentQueue<string> _results = new ConcurrentQueue<string>();
37500+
37501+
private static readonly DiagnosticDescriptor Descriptor =
37502+
new DiagnosticDescriptor("XY0000", "Test", "Test", "Test", DiagnosticSeverity.Warning, true, "Test", "Test");
37503+
37504+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
37505+
37506+
public override void Initialize(AnalysisContext context)
37507+
{
37508+
context.RegisterSymbolStartAction(handleStart, SymbolKind.NamedType);
37509+
context.RegisterSymbolStartAction(handleStart, SymbolKind.Method);
37510+
context.RegisterSymbolStartAction(handleStart, SymbolKind.Property);
37511+
context.RegisterSymbolStartAction(handleStart, SymbolKind.Parameter);
37512+
context.RegisterSymbolStartAction(handleStart, SymbolKind.TypeParameter);
37513+
37514+
void handleStart(SymbolStartAnalysisContext context)
37515+
{
37516+
_results.Enqueue($"Start: {context.Symbol.ToTestDisplayString()}");
37517+
context.RegisterSymbolEndAction(handleEnd);
37518+
}
37519+
37520+
void handleEnd(SymbolAnalysisContext context)
37521+
{
37522+
_results.Enqueue($"End: {context.Symbol.ToTestDisplayString()}");
37523+
}
37524+
}
37525+
}
3729037526
}

src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,15 @@ public void RegisterSymbolAction(Action<SymbolAnalysisContext> action, Immutable
549549
break;
550550
case SymbolKind.NamedType:
551551
var namedType = (INamedTypeSymbol)context.Symbol;
552-
var delegateInvokeMethod = namedType.DelegateInvokeMethod;
553-
parameters = delegateInvokeMethod?.Parameters ?? ImmutableArray.Create<IParameterSymbol>();
552+
if (namedType.IsExtension)
553+
{
554+
parameters = namedType.ExtensionParameter is { } extensionParameter ? [extensionParameter] : [];
555+
}
556+
else
557+
{
558+
var delegateInvokeMethod = namedType.DelegateInvokeMethod;
559+
parameters = delegateInvokeMethod?.Parameters ?? ImmutableArray.Create<IParameterSymbol>();
560+
}
554561
break;
555562
default:
556563
throw new ArgumentException($"{context.Symbol.Kind} is not supported.", nameof(context));

0 commit comments

Comments
 (0)