diff --git a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs index adbc4e53f37f9..beaf0053be4b9 100644 --- a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs +++ b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs @@ -4,10 +4,11 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; -using System.Linq; -using System.ComponentModel; +using Microsoft.CodeAnalysis.SourceGeneration; namespace Microsoft.CodeAnalysis.CSharp { @@ -74,5 +75,7 @@ internal override SyntaxTree ParseGeneratedSourceText(GeneratedSourceText input, internal override CommonMessageProvider MessageProvider => CSharp.MessageProvider.Instance; internal override string SourceExtension => ".cs"; + + internal override ISyntaxHelper SyntaxHelper => CSharpSyntaxHelper.Instance; } } diff --git a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs new file mode 100644 index 0000000000000..ea12b763d81a2 --- /dev/null +++ b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpSyntaxHelper.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.SourceGeneration; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal sealed class CSharpSyntaxHelper : AbstractSyntaxHelper + { + public static readonly ISyntaxHelper Instance = new CSharpSyntaxHelper(); + + private CSharpSyntaxHelper() + { + } + + public override bool IsCaseSensitive + => true; + + public override bool IsValidIdentifier(string name) + => SyntaxFacts.IsValidIdentifier(name); + + public override bool IsAnyNamespaceBlock(SyntaxNode node) + => node is BaseNamespaceDeclarationSyntax; + + public override bool IsAttribute(SyntaxNode node) + => node is AttributeSyntax; + + public override SyntaxNode GetNameOfAttribute(SyntaxNode node) + => ((AttributeSyntax)node).Name; + + public override bool IsAttributeList(SyntaxNode node) + => node is AttributeListSyntax; + + public override void AddAttributeTargets(SyntaxNode node, ArrayBuilder targets) + { + var attributeList = (AttributeListSyntax)node; + var container = attributeList.Parent; + RoslynDebug.AssertNotNull(container); + + // For fields/events, the attribute applies to all the variables declared. + if (container is FieldDeclarationSyntax field) + targets.AddRange(field.Declaration.Variables); + else if (container is EventFieldDeclarationSyntax ev) + targets.AddRange(ev.Declaration.Variables); + else + targets.Add(container); + } + + public override SeparatedSyntaxList GetAttributesOfAttributeList(SyntaxNode node) + => ((AttributeListSyntax)node).Attributes; + + public override bool IsLambdaExpression(SyntaxNode node) + => node is LambdaExpressionSyntax; + + public override SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node) + => ((NameSyntax)node).GetUnqualifiedName().Identifier; + + public override void AddAliases(SyntaxNode node, ArrayBuilder<(string aliasName, string symbolName)> aliases, bool global) + { + if (node is CompilationUnitSyntax compilationUnit) + { + AddAliases(compilationUnit.Usings, aliases, global); + } + else if (node is BaseNamespaceDeclarationSyntax namespaceDeclaration) + { + AddAliases(namespaceDeclaration.Usings, aliases, global); + } + else + { + throw ExceptionUtilities.UnexpectedValue(node.Kind()); + } + } + + private static void AddAliases(SyntaxList usings, ArrayBuilder<(string aliasName, string symbolName)> aliases, bool global) + { + foreach (var usingDirective in usings) + { + if (usingDirective.Alias is null) + continue; + + if (global != usingDirective.GlobalKeyword.Kind() is SyntaxKind.GlobalKeyword) + continue; + + var aliasName = usingDirective.Alias.Name.Identifier.ValueText; + var symbolName = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText; + aliases.Add((aliasName, symbolName)); + } + } + + public override void AddAliases(CompilationOptions compilation, ArrayBuilder<(string aliasName, string symbolName)> aliases) + { + // C# doesn't have global aliases at the compilation level. + return; + } + } +} diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index c20d3e5b5d4b9..aeecef424f270 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -274,7 +274,15 @@ private void VisitNamedTypeWithoutNullability(INamedTypeSymbol symbol) if (IncludeNamedType(symbol.ContainingType)) { symbol.ContainingType.Accept(this.NotFirstVisitor); - AddPunctuation(SyntaxKind.DotToken); + + if (format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes)) + { + AddPunctuation(SyntaxKind.PlusToken); + } + else + { + AddPunctuation(SyntaxKind.DotToken); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.cs new file mode 100644 index 0000000000000..51727b5140aab --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.cs @@ -0,0 +1,1666 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities.TestGenerators; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.SourceGeneration; + +internal static class IncrementalGeneratorInitializationContextExtensions +{ + public static IncrementalValuesProvider ForAttributeWithSimpleName( + this IncrementalGeneratorInitializationContext context, string simpleName) + where T : SyntaxNode + { + return context.SyntaxProvider.ForAttributeWithSimpleName( + simpleName, + (node, _) => node is T).Select((node, _) => (T)node); + } + + public static IncrementalValuesProvider ForAttributeWithMetadataName( + this IncrementalGeneratorInitializationContext context, string fullyQualifiedMetadataName) + where T : SyntaxNode + { + return context.SyntaxProvider.ForAttributeWithMetadataName( + fullyQualifiedMetadataName, + (node, _) => node is T, + (context, cancellationToken) => (T)context.TargetNode); + } +} + +public class GeneratorDriverTests_Attributes_FullyQualifiedName : CSharpTestBase +{ + #region Non-Incremental tests + + // These tests just validate basic correctness of results in different scenarios, without actually validating + // that the incremental nature of this provider works properly. + + [Fact] + public void FindCorrectAttributeOnTopLevelClass_WhenSearchingForClassDeclaration1() + { + var source = @" +[N1.X] +class C1 { } +[N2.X] +class C2 { } + +namespace N1 +{ + class XAttribute : System.Attribute { } +} + +namespace N2 +{ + class XAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("N1.XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C1" })); + } + + [Theory] + [InlineData("XAttribute")] + [InlineData("X")] + [InlineData("N1.xAttribute")] + [InlineData("N1.x")] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingSimpleName1(string name) + { + var source = @" +[N1.X] +class C1 { } +[N2.X] +class C2 { } + +namespace N1 +{ + class XAttribute : System.Attribute { } +} + +namespace N2 +{ + class XAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName(name); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + } + + [Fact] + public void FindCorrectAttributeOnTopLevelClass_WhenSearchingForClassDeclaration2() + { + var source = @" +[N1.X] +class C1 { } +[N2.X] +class C2 { } + +namespace N1 +{ + class XAttribute : System.Attribute { } +} + +namespace N2 +{ + class XAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("N2.XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C2" })); + } + + [Theory] + [InlineData("CLSCompliant(true)")] + [InlineData("CLSCompliantAttribute(true)")] + [InlineData("System.CLSCompliant(true)")] + [InlineData("System.CLSCompliantAttribute(true)")] + public void FindAssemblyAttribute1(string attribute) + { + var source = @$" +using System; +[assembly: {attribute}] +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.Single())); + } + + [Theory] + [InlineData("CLSCompliant(true)")] + [InlineData("CLSCompliantAttribute(true)")] + [InlineData("System.CLSCompliant(true)")] + [InlineData("System.CLSCompliantAttribute(true)")] + public void FindModuleAttribute1(string attribute) + { + var source = @$" +using System; +[module: {attribute}] +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.Single())); + } + + [Theory] + [InlineData("")] + [InlineData("class WithoutAttributes { }")] + public void FindAssemblyAttribute2(string source2) + { + var source1 = @" +using System; +[assembly: CLSCompliant(true)] +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.First())); + } + + [Theory] + [InlineData("")] + [InlineData("class WithoutAttributes { }")] + public void FindAssemblyAttribute3(string source1) + { + var source2 = @" +using System; +[assembly: CLSCompliant(true)] +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.Last())); + } + + [Fact] + public void FindAssemblyAttribute4() + { + var source1 = @" +using System; +[assembly: CLSCompliant(true)] +"; + var source2 = @" +using System; +[assembly: CLSCompliant(false)] +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.First()), + step => Assert.True(step.Outputs.Single().Value is CompilationUnitSyntax c && c.SyntaxTree == compilation.SyntaxTrees.Last())); + } + + [Fact] + public void FindTopLocalFunctionAttribute1() + { + var source = @" +using System; + +[CLSCompliant(true)] +void LocalFunc() +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is LocalFunctionStatementSyntax { Identifier.ValueText: "LocalFunc" })); + } + + [Fact] + public void FindNestedLocalFunctionAttribute1() + { + var source = @" +using System; + +class C +{ + void M() + { + [CLSCompliant(true)] + void LocalFunc() + { + } + } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is LocalFunctionStatementSyntax { Identifier.ValueText: "LocalFunc" })); + } + + [Fact] + public void FindNestedLocalFunctionAttribute2() + { + var source = @" +using System; + +class C +{ + void M() + { + var v = () => + { + [CLSCompliant(true)] + void LocalFunc() + { + } + }; + } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is LocalFunctionStatementSyntax { Identifier.ValueText: "LocalFunc" })); + } + + [Fact] + public void FindTypeParameterFunctionAttribute1() + { + var source = @" +using System; + +class C<[CLSCompliant(true)] T> +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is TypeParameterSyntax { Identifier.ValueText: "T" })); + } + + [Fact] + public void FindMethodAttribute1() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + void M() + { + } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is MethodDeclarationSyntax { Identifier.ValueText: "M" })); + } + + [Fact] + public void FindMethodReturnAttribute1() + { + var source = @" +using System; + +class C +{ + [return: CLSCompliant(true)] + void M() + { + } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is MethodDeclarationSyntax { Identifier.ValueText: "M" })); + } + + [Fact] + public void FindPartialMethodAttribute1() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + internal partial void M(); + internal partial void M() { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is MethodDeclarationSyntax { Identifier.ValueText: "M", Body: null, ExpressionBody: null })); + } + + [Fact] + public void FindPartialMethodAttribute2() + { + var source = @" +using System; + +class C +{ + internal partial void M(); + [CLSCompliant(true)] + internal partial void M() { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is MethodDeclarationSyntax { Identifier.ValueText: "M", Body: not null })); + } + + [Fact] + public void FindFieldAttribute1() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + int m; +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is VariableDeclaratorSyntax { Identifier.ValueText: "m" })); + } + + [Fact] + public void FindFieldAttribute2() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + int m, n; +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.Collection(step.Outputs, + v => Assert.True(v.Value is VariableDeclaratorSyntax { Identifier.ValueText: "m" }), + v => Assert.True(v.Value is VariableDeclaratorSyntax { Identifier.ValueText: "n" }))); + } + + [Fact] + public void FindEventFieldAttribute1() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + event Action m; +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is VariableDeclaratorSyntax { Identifier.ValueText: "m" })); + } + + [Fact] + public void FindEventFieldAttribute2() + { + var source = @" +using System; + +class C +{ + [CLSCompliant(true)] + event Action m, n; +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.Collection(step.Outputs, + v => Assert.True(v.Value is VariableDeclaratorSyntax { Identifier.ValueText: "m" }), + v => Assert.True(v.Value is VariableDeclaratorSyntax { Identifier.ValueText: "n" }))); + } + + [Fact] + public void FindParenthesizedLambdaAttribute1() + { + var source = @" +using System; + +Func v = [CLSCompliant(true)] (int i) => i; +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is LambdaExpressionSyntax)); + } + + [Fact] + public void FindAccessorAttribute1() + { + var source = @" +using System; + +class C +{ + int Prop + { + [CLSCompliant(true)] + get => 0; + } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is AccessorDeclarationSyntax { RawKind: (int)SyntaxKind.GetAccessorDeclaration })); + } + + [Fact] + public void FindTypeParameterAttribute1() + { + var source = @" +using System; + +class C<[CLSCompliant(true)]T> +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("System.CLSCompliantAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is TypeParameterSyntax { Identifier.ValueText: "T" })); + } + + [Fact] + public void FindNestedAttribute1() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute { } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer1+InnerAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C1" })); + } + + [Fact] + public void FindNestedAttribute2() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute { } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer2+InnerAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C2" })); + } + + [Fact] + public void FindNestedGenericAttribute1() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute{ } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer1+InnerAttribute`1"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C1" })); + } + + [Fact] + public void FindNestedGenericAttribute2() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute{ } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer2+InnerAttribute`2"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C2" })); + } + + [Fact] + public void DoNotFindNestedGenericAttribute1() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute{ } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer1+InnerAttribute`2"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + } + + [Fact] + public void DoNotFindNestedGenericAttribute2() + { + var source = @" +[Outer1.Inner] +class C1 { } +[Outer2.Inner] +class C2 { } + +class Outer1 +{ + public class InnerAttribute : System.Attribute{ } +} +class Outer2 +{ + public class InnerAttribute : System.Attribute { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("Outer2+InnerAttribute`1"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists1() + { + var source = @" +[X][X] +class C { } + +class XAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 2); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists1B() + { + var source = @" +[X, X] +class C { } + +class XAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 2); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists2() + { + var source = @" +[X][Y] +class C { } + +class XAttribute : System.Attribute { } +class YAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 1); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists2B() + { + var source = @" +[X, Y] +class C { } + +class XAttribute : System.Attribute { } +class YAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 1); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists3() + { + var source = @" +[Y][X] +class C { } + +class XAttribute : System.Attribute { } +class YAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 1); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributeLists3B() + { + var source = @" +[Y, X] +class C { } + +class XAttribute : System.Attribute { } +class YAttribute : System.Attribute { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var counter = 0; + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.SyntaxProvider.ForAttributeWithMetadataName( + "XAttribute", + (_, _) => true, + (ctx, _) => + { + Assert.True(ctx.Attributes.Length == 1); + return (ClassDeclarationSyntax)ctx.TargetNode; + }); + ctx.RegisterSourceOutput(input, (spc, node) => { counter++; }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.Equal(1, counter); + } + + #endregion + + #region Incremental tests + + // These tests validate minimal recomputation performed after changes are made to the compilation. + + [Fact] + public void RerunOnSameCompilationCachesResultFully() + { + var source = @" +[X] +class C { } + +class XAttribute : System.Attribute +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + // re-run without changes + driver = driver.RunGenerators(compilation); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunWithReferencesChange() + { + var source = @" +[X] +class C { } + +class XAttribute : System.Attribute +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + // re-run without changes + driver = driver.RunGenerators(compilation.RemoveAllReferences()); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunWithAddedFile1() + { + var source = @" +[X] +class C { } + +class XAttribute : System.Attribute +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From("")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunWithAddedFile2() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +class XAttribute : System.Attribute +{ +}")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunWithAddedFile_MultipleResults_SameFile1() + { + var source = @" +[X] +class C1 { } +[X] +class C2 { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +class XAttribute : System.Attribute +{ +}")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.Collection(step.Outputs, + t => Assert.True(t.Value is ClassDeclarationSyntax { Identifier.ValueText: "C1" }), + t => Assert.True(t.Value is ClassDeclarationSyntax { Identifier.ValueText: "C2" }))); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"].Single().Outputs, + t => Assert.Equal(IncrementalStepRunReason.Cached, t.Reason), + t => Assert.Equal(IncrementalStepRunReason.Cached, t.Reason)); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs, + t => Assert.Equal(IncrementalStepRunReason.Modified, t.Reason), + t => Assert.Equal(IncrementalStepRunReason.Modified, t.Reason)); + } + + [Fact] + public void RerunWithAddedFile_MultipleResults_MultipleFile1() + { + var source1 = @" +[X] +class C1 { } +"; + var source2 = @" +[X] +class C2 { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +class XAttribute : System.Attribute +{ +}")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.Collection(step.Outputs, t => Assert.True(t.Value is ClassDeclarationSyntax { Identifier.ValueText: "C1" })), + step => Assert.Collection(step.Outputs, t => Assert.True(t.Value is ClassDeclarationSyntax { Identifier.ValueText: "C2" }))); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Collection(runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason)); + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Collection(runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"], + s => Assert.Collection(s.Outputs, + t => Assert.Equal(IncrementalStepRunReason.Cached, t.Reason), + t => Assert.Equal(IncrementalStepRunReason.Cached, t.Reason))); + Assert.Collection(runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"], + s => Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason)); + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + s => Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason)); + } + + [Fact] + public void RerunWithChangedFileThatNowReferencesAttribute1() + { + var source = @" +class C { } + +class XAttribute : System.Attribute +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.First(), + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +[X] +class C { } + +class XAttribute : System.Attribute +{ +} +")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunWithChangedFileThatNowReferencesAttribute2() + { + var source1 = @" +class C { } +"; + var source2 = @" +class XAttribute : System.Attribute +{ +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithMetadataName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.First(), + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +[X] +class C { } +")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttributeWithMetadataName"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["groupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["result_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason); + } + + #endregion +} diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs new file mode 100644 index 0000000000000..593a416a2faf7 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs @@ -0,0 +1,1558 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities.TestGenerators; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.SourceGeneration; + +public class GeneratorDriverTests_Attributes_SimpleName : CSharpTestBase +{ + #region Non-Incremental tests + + // These tests just validate basic correctness of results in different scenarios, without actually validating + // that the incremental nature of this provider works properly. + + [Theory] + [InlineData("[X]")] + [InlineData("[X, Y]")] + [InlineData("[Y, X]")] + [InlineData("[X, X]")] + [InlineData("[X][Y]")] + [InlineData("[Y][X]")] + [InlineData("[X][X]")] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration1(string attribute) + { + var source = @$" +{attribute} +class C {{ }} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Theory] + [InlineData("XAttribute")] + [InlineData("A.X")] + [InlineData("A.XAttribute")] + [InlineData("A.X")] + [InlineData("global::X")] + [InlineData("global::A.X")] + public void FindFullAttributeNameOnTopLevelClass(string attribute) + { + var source = @$" +[{attribute}] +class C {{ }} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForDelegateDeclaration1() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForDifferentName() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("YAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForSyntaxNode1() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration1() + { + var source = @" +[X] +class C { } +[X] +class D { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => + { + Assert.True(step.Outputs.First().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" }); + Assert.True(step.Outputs.Last().Value is ClassDeclarationSyntax { Identifier.ValueText: "D" }); + }); + } + + [Fact] + public void FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration2() + { + var source = @" +[X] +class C { } +[Y] +class D { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => + { + Assert.True(step.Outputs.First().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" }); + Assert.False(step.Outputs.Any(o => o.Value is ClassDeclarationSyntax { Identifier.ValueText: "D" })); + }); + } + + [Fact] + public void FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration3() + { + var source = @" +[Y] +class C { } +[X] +class D { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => + { + Assert.False(step.Outputs.Any(o => o.Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + Assert.True(step.Outputs.Last().Value is ClassDeclarationSyntax { Identifier.ValueText: "D" }); + }); + } + + [Fact] + public void FindAttributeOnNestedClasses_WhenSearchingForClassDeclaration1() + { + var source = @" +[X] +class C +{ + [X] + class D { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => + { + Assert.True(step.Outputs.First().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" }); + Assert.True(step.Outputs.Last().Value is ClassDeclarationSyntax { Identifier.ValueText: "D" }); + }); + } + + [Fact] + public void FindAttributeOnClassInNamespace_WhenSearchingForClassDeclaration1() + { + var source = @" +namespace N +{ + [X] + class C { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.First().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_FullAttributeName1() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ShortAttributeName1() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("X"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindFullAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_FullAttributeName1() + { + var source = @" +[XAttribute] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Theory] + [InlineData("A = XAttribute")] + [InlineData("AAttribute = XAttribute")] + [InlineData("A = M.XAttribute")] + [InlineData("A = M.XAttribute")] + [InlineData("A = global::M.XAttribute")] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias1(string alias) + { + var source = @$" +using {alias}; + +[A] +class C {{ }} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias3() + { + var source = @" +using AAttribute = XAttribute; + +[AAttribute] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias1() + { + var source = @" +using AAttribute : X; + +[AAttribute] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias2() + { + var source = @" +using AAttribute : XAttribute; + +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases1() + { + var source = @" +using B = XAttribute; +namespace N +{ + using A = B; + + [A] + class C { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_OuterAliasReferencesInnerAlias() + { + // note: this is not legal. it's ok if this ever stops working in the futuer. + var source = @" +using BAttribute = AAttribute; +namespace N +{ + using AAttribute = XAttribute; + + [B] + class C { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases2() + { + var source = @" +using B = XAttribute; +namespace N +{ + using AAttribute = B; + + [A] + class C { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases2() + { + var source = @" +using BAttribute = XAttribute; +namespace N +{ + using AAttribute = B; + + [A] + class C { } +} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias1() + { + var source = @" +using AAttribute = BAttribute; +using BAttribute = AAttribute; + +[A] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias2() + { + var source = @" +using A = BAttribute; +using B = AAttribute; + +[A] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias3() + { + var source = @" +using A = B; +using B = A; + +[A] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_LocalAliasInDifferentFile1() + { + var source1 = @" +[A] +class C { } +"; + var source2 = @" +using A = XAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_LocalAliasInDifferentFile2() + { + var source1 = @" +[A] +class C { } +"; + var source2 = @" +using AAttribute = XAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Theory] + [InlineData("A = XAttribute")] + [InlineData("AAttribute = XAttribute")] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasInSameFile1(string alias) + { + var source = @$" +global using {alias}; + +[A] +class C {{ }} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Theory] + [InlineData("B = AAttribute")] + [InlineData("BAttribute = AAttribute")] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasInSameFile1(string alias) + { + var source = @$" +global using AAttribute = XAttribute; +using {alias}; + +[B] +class C {{ }} +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Theory] + [InlineData("A = XAttribute")] + [InlineData("AAttribute = XAttribute")] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasDifferentFile1(string alias) + { + var source1 = @" +[A] +class C { } +"; + var source2 = @$" +global using {alias}; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_BothGlobalAndLocalAliasDifferentFile1() + { + var source1 = @" +[B] +class C { } +"; + var source2 = @" +global using AAttribute = XAttribute; +using B = AAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasLoop1() + { + var source1 = @" +[A] +class C { } +"; + var source2 = @" +global using AAttribute = BAttribute; +global using BAttribute = AAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasDifferentFile1() + { + var source1 = @" +using B = AAttribute; +[B] +class C { } +"; + var source2 = @" +global using AAttribute = XAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasDifferentFile2() + { + var source1 = @" +using BAttribute = AAttribute; +[B] +class C { } +"; + var source2 = @" +global using AAttribute = XAttribute; +"; + + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + #endregion + + #region Incremental tests + + // These tests validate minimal recomputation performed after changes are made to the compilation. + + [Fact] + public void RerunOnSameCompilationCachesResultFully() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + // re-run without changes + driver = driver.RunGenerators(compilation); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void RerunOnCompilationWithReferencesChangeCachesResultFully() + { + var source = @" +[X] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: parseOptions); + + Assert.Single(compilation.SyntaxTrees); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + // re-run with just changes to references. this helper is entirely syntactic, so nothing should change. + driver = driver.RunGenerators(compilation.RemoveAllReferences()); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestSourceFileRemoved1() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + // re-run with the file with the class removed. this will remove the actual output. + driver = driver.RunGenerators(compilation.RemoveSyntaxTrees(compilation.SyntaxTrees.Last())); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Removed, s.Outputs.Single().Reason)); + + // the per-file global aliases get changed (because the last file is removed). + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + // however, the collected global aliases stays the same. + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestSourceFileChanged_AttributeRemoved1() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(@" +class C { } +")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestSourceFileChanged_AttributeAdded1() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(@" +[B] +class C { } +")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void TestSourceFileChanged_NonVisibleChangeToGlobalAttributeFile() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.First(), + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +global using AAttribute = XAttribute; +class Dummy {} +")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestRemoveGlobalAttributeFile1() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.RemoveSyntaxTrees( + compilation.SyntaxTrees.First())); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Removed, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestAddGlobalAttributeFile1() + { + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +global using AAttribute = XAttribute;")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void TestAddGlobalAttributeFile2() + { + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +global using BAttribute = XAttribute;")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + } + + [Fact] + public void TestAddSourceFileWithoutAttribute() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +class D { }")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + } + + [Fact] + public void TestAddSourceFileWithAttribute() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(@" +[A] +class D { }")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Collection(runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)); + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" }), + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "D" })); + } + + [Fact] + public void TestReplaceSourceFileWithDifferentAttribute() + { + var source1 = @" +global using AAttribute = XAttribute;"; + + var source2 = @" +global using BAttribute = AAttribute;"; + + var source3 = @" +[B] +class C { } +"; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(new[] { source1, source2, source3 }, options: TestOptions.DebugDll, parseOptions: parseOptions); + + var generator = new IncrementalGeneratorWrapper(new PipelineCallbackGenerator(ctx => + { + var input = ctx.ForAttributeWithSimpleName("XAttribute"); + ctx.RegisterSourceOutput(input, (spc, node) => { }); + })); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ISourceGenerator[] { generator }, parseOptions: parseOptions, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true)); + driver = driver.RunGenerators(compilation); + var runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" })); + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(@" +[A] +class D { }")))); + runResult = driver.GetRunResult().Results[0]; + + Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"], + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason); + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason); + + Assert.Collection(runResult.TrackedSteps["result_ForAttribute"], + step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "D" })); + } + + #endregion +} diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index e509163614250..d6b7dce8305f8 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -1374,6 +1374,118 @@ class C { } InternalSymbolDisplayPartKind.Arity); } + [Fact] + public void TestNestedType1() + { + var text = @" +class C +{ + class D + { + } +} +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeType, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes | SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes); + + TestSymbolDescription( + text, + global => global.GetTypeMembers("C").Single().GetTypeMember("D"), + format, + "C+D", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void TestNestedType2() + { + var text = @" +class C +{ + class D + { + } +} +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeType, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes | SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes); + + TestSymbolDescription( + text, + global => global.GetTypeMembers("C").Single().GetTypeMember("D"), + format, + "C+D`1", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName, + InternalSymbolDisplayPartKind.Arity); + } + + [Fact] + public void TestNestedType3() + { + var text = @" +class C +{ + class D + { + } +} +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeType, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes | SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes); + + TestSymbolDescription( + text, + global => global.GetTypeMembers("C").Single().GetTypeMember("D"), + format, + "C`1+D", + SymbolDisplayPartKind.ClassName, + InternalSymbolDisplayPartKind.Arity, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void TestNestedType4() + { + var text = @" +class C +{ + class D + { + } +} +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeType, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes | SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes); + + TestSymbolDescription( + text, + global => global.GetTypeMembers("C").Single().GetTypeMember("D"), + format, + "C`1+D`2", + SymbolDisplayPartKind.ClassName, + InternalSymbolDisplayPartKind.Arity, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName, + InternalSymbolDisplayPartKind.Arity); + } + [Fact] public void TestGenericTypeParameters() { diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 41953ef8b7190..8de479f594895 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,12 @@ *REMOVED*override abstract Microsoft.CodeAnalysis.Diagnostic.Equals(object? obj) -> bool abstract Microsoft.CodeAnalysis.SymbolVisitor.DefaultResult.get -> TResult Microsoft.CodeAnalysis.Compilation.GetTypesByMetadataName(string! fullyQualifiedMetadataName) -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext.Attributes.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext.GeneratorAttributeSyntaxContext() -> void +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext.SemanticModel.get -> Microsoft.CodeAnalysis.SemanticModel! +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext.TargetNode.get -> Microsoft.CodeAnalysis.SyntaxNode! +Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext.TargetSymbol.get -> Microsoft.CodeAnalysis.ISymbol! Microsoft.CodeAnalysis.IFieldSymbol.IsRequired.get -> bool Microsoft.CodeAnalysis.IImportScope Microsoft.CodeAnalysis.IImportScope.Aliases.get -> System.Collections.Immutable.ImmutableArray @@ -54,6 +60,7 @@ Microsoft.CodeAnalysis.SemanticModel.GetImportScopes(int position, System.Thread Microsoft.CodeAnalysis.ISymbol.Accept(Microsoft.CodeAnalysis.SymbolVisitor! visitor, TArgument argument) -> TResult Microsoft.CodeAnalysis.SymbolVisitor Microsoft.CodeAnalysis.SymbolVisitor.SymbolVisitor() -> void +Microsoft.CodeAnalysis.SyntaxValueProvider.ForAttributeWithMetadataName(string! fullyQualifiedMetadataName, System.Func! predicate, System.Func! transform) -> Microsoft.CodeAnalysis.IncrementalValuesProvider override sealed Microsoft.CodeAnalysis.Diagnostic.Equals(object? obj) -> bool *REMOVED*static Microsoft.CodeAnalysis.SyntaxNodeExtensions.ReplaceSyntax(this TRoot! root, System.Collections.Generic.IEnumerable! nodes, System.Func! computeReplacementNode, System.Collections.Generic.IEnumerable! tokens, System.Func! computeReplacementToken, System.Collections.Generic.IEnumerable! trivia, System.Func! computeReplacementTrivia) -> TRoot! static Microsoft.CodeAnalysis.Emit.EditAndContinueMethodDebugInformation.Create(System.Collections.Immutable.ImmutableArray compressedSlotMap, System.Collections.Immutable.ImmutableArray compressedLambdaMap, System.Collections.Immutable.ImmutableArray compressedStateMachineStateMap) -> Microsoft.CodeAnalysis.Emit.EditAndContinueMethodDebugInformation diff --git a/src/Compilers/Core/Portable/SourceGeneration/GeneratorContexts.cs b/src/Compilers/Core/Portable/SourceGeneration/GeneratorContexts.cs index e6f2f4655ef68..5df3d0219061b 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/GeneratorContexts.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/GeneratorContexts.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis { @@ -215,12 +216,14 @@ internal sealed class CallbackHolder /// public readonly struct GeneratorSyntaxContext { + internal readonly ISyntaxHelper SyntaxHelper; private readonly Lazy? _semanticModel; - internal GeneratorSyntaxContext(SyntaxNode node, Lazy? semanticModel) + internal GeneratorSyntaxContext(SyntaxNode node, Lazy? semanticModel, ISyntaxHelper syntaxHelper) { Node = node; _semanticModel = semanticModel; + SyntaxHelper = syntaxHelper; } /// diff --git a/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs b/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs index eabe61bc42efd..78e9a34adac57 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @@ -11,6 +11,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -191,7 +192,8 @@ internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, Diagnos var outputBuilder = ArrayBuilder.GetInstance(); var inputBuilder = ArrayBuilder.GetInstance(); var postInitSources = ImmutableArray.Empty; - var pipelineContext = new IncrementalGeneratorInitializationContext(inputBuilder, outputBuilder, SourceExtension); + var pipelineContext = new IncrementalGeneratorInitializationContext( + inputBuilder, outputBuilder, this.SyntaxHelper, this.SourceExtension); Exception? ex = null; try @@ -371,5 +373,7 @@ private static (ImmutableArray, ImmutableArray? _semanticModel; - internal GeneratorSyntaxWalker(ISyntaxContextReceiver syntaxReceiver) + internal GeneratorSyntaxWalker( + ISyntaxContextReceiver syntaxReceiver, + ISyntaxHelper syntaxHelper) { _syntaxReceiver = syntaxReceiver; + _syntaxHelper = syntaxHelper; } public void VisitWithModel(Lazy? model, SyntaxNode node) @@ -32,7 +36,7 @@ public void VisitWithModel(Lazy? model, SyntaxNode node) public override void Visit(SyntaxNode node) { Debug.Assert(_semanticModel is object && _semanticModel.Value.SyntaxTree == node.SyntaxTree); - _syntaxReceiver.OnVisitSyntaxNode(new GeneratorSyntaxContext(node, _semanticModel)); + _syntaxReceiver.OnVisitSyntaxNode(new GeneratorSyntaxContext(node, _semanticModel, _syntaxHelper)); base.Visit(node); } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/GlobalAliases.cs b/src/Compilers/Core/Portable/SourceGeneration/GlobalAliases.cs new file mode 100644 index 0000000000000..d4477c455faff --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/GlobalAliases.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SourceGeneration; + +/// +/// Simple wrapper class around an immutable array so we can have the value-semantics needed for the incremental +/// generator to know when a change actually happened and it should run later transform stages. +/// +internal sealed class GlobalAliases : IEquatable +{ + public static readonly GlobalAliases Empty = new(ImmutableArray<(string aliasName, string symbolName)>.Empty); + + public readonly ImmutableArray<(string aliasName, string symbolName)> AliasAndSymbolNames; + + private int _hashCode; + + private GlobalAliases(ImmutableArray<(string aliasName, string symbolName)> aliasAndSymbolNames) + { + AliasAndSymbolNames = aliasAndSymbolNames; + } + + public static GlobalAliases Create(ImmutableArray<(string aliasName, string symbolName)> aliasAndSymbolNames) + { + return aliasAndSymbolNames.IsEmpty ? Empty : new GlobalAliases(aliasAndSymbolNames); + } + + public static GlobalAliases Concat(GlobalAliases ga1, GlobalAliases ga2) + { + if (ga1.AliasAndSymbolNames.Length == 0) + return ga2; + + if (ga2.AliasAndSymbolNames.Length == 0) + return ga1; + + return new(ga1.AliasAndSymbolNames.Concat(ga2.AliasAndSymbolNames)); + } + + public override int GetHashCode() + { + if (_hashCode == 0) + { + var hashCode = 0; + foreach (var tuple in this.AliasAndSymbolNames) + hashCode = Hash.Combine(tuple.GetHashCode(), hashCode); + + _hashCode = hashCode == 0 ? 1 : hashCode; + } + + return _hashCode; + } + + public override bool Equals(object? obj) + => this.Equals(obj as GlobalAliases); + + public bool Equals(GlobalAliases? aliases) + { + if (aliases is null) + return false; + + if (ReferenceEquals(this, aliases)) + return true; + + if (this.AliasAndSymbolNames == aliases.AliasAndSymbolNames) + return true; + + return this.AliasAndSymbolNames.AsSpan().SequenceEqual(aliases.AliasAndSymbolNames.AsSpan()); + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs b/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs new file mode 100644 index 0000000000000..e505d313ada91 --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/ISyntaxHelper.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.SourceGeneration +{ + internal interface ISyntaxHelper + { + bool IsCaseSensitive { get; } + + bool IsValidIdentifier(string name); + + bool IsAnyNamespaceBlock(SyntaxNode node); + + bool IsAttributeList(SyntaxNode node); + SeparatedSyntaxList GetAttributesOfAttributeList(SyntaxNode node); + + void AddAttributeTargets(SyntaxNode node, ArrayBuilder targets); + + bool IsAttribute(SyntaxNode node); + SyntaxNode GetNameOfAttribute(SyntaxNode node); + + bool IsLambdaExpression(SyntaxNode node); + + SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node); + + /// + /// must be a compilation unit or namespace block. + /// + void AddAliases(SyntaxNode node, ArrayBuilder<(string aliasName, string symbolName)> aliases, bool global); + void AddAliases(CompilationOptions options, ArrayBuilder<(string aliasName, string symbolName)> aliases); + } + + internal abstract class AbstractSyntaxHelper : ISyntaxHelper + { + public abstract bool IsCaseSensitive { get; } + + public abstract bool IsValidIdentifier(string name); + + public abstract SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode name); + + public abstract bool IsAnyNamespaceBlock(SyntaxNode node); + + public abstract bool IsAttribute(SyntaxNode node); + public abstract SyntaxNode GetNameOfAttribute(SyntaxNode node); + + public abstract bool IsAttributeList(SyntaxNode node); + public abstract SeparatedSyntaxList GetAttributesOfAttributeList(SyntaxNode node); + public abstract void AddAttributeTargets(SyntaxNode node, ArrayBuilder targets); + + public abstract bool IsLambdaExpression(SyntaxNode node); + + public abstract void AddAliases(SyntaxNode node, ArrayBuilder<(string aliasName, string symbolName)> aliases, bool global); + public abstract void AddAliases(CompilationOptions options, ArrayBuilder<(string aliasName, string symbolName)> aliases); + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/IncrementalContexts.cs b/src/Compilers/Core/Portable/SourceGeneration/IncrementalContexts.cs index 275f62214ab3e..56988c5b19c25 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/IncrementalContexts.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/IncrementalContexts.cs @@ -8,30 +8,47 @@ using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// /// Context passed to an incremental generator when is called /// - public readonly struct IncrementalGeneratorInitializationContext + public readonly partial struct IncrementalGeneratorInitializationContext { private readonly ArrayBuilder _syntaxInputBuilder; private readonly ArrayBuilder _outputNodes; private readonly string _sourceExtension; - internal IncrementalGeneratorInitializationContext(ArrayBuilder syntaxInputBuilder, ArrayBuilder outputNodes, string sourceExtension) + internal readonly ISyntaxHelper SyntaxHelper; + + internal IncrementalGeneratorInitializationContext( + ArrayBuilder syntaxInputBuilder, + ArrayBuilder outputNodes, + ISyntaxHelper syntaxHelper, + string sourceExtension) { _syntaxInputBuilder = syntaxInputBuilder; _outputNodes = outputNodes; + SyntaxHelper = syntaxHelper; _sourceExtension = sourceExtension; } - public SyntaxValueProvider SyntaxProvider => new SyntaxValueProvider(_syntaxInputBuilder, RegisterOutput); + public SyntaxValueProvider SyntaxProvider => new(this, _syntaxInputBuilder, RegisterOutput, SyntaxHelper); public IncrementalValueProvider CompilationProvider => new IncrementalValueProvider(SharedInputNodes.Compilation.WithRegisterOutput(RegisterOutput).WithTrackingName(WellKnownGeneratorInputs.Compilation)); + // Use a ReferenceEqualityComparer as we want to rerun this stage whenever the CompilationOptions changes at all + // (e.g. we don't care if it has the same conceptual value, we're ok rerunning as long as the actual instance + // changes). + internal IncrementalValueProvider CompilationOptionsProvider + => new(SharedInputNodes.CompilationOptions.WithRegisterOutput(RegisterOutput) + .WithComparer(ReferenceEqualityComparer.Instance) + .WithTrackingName(WellKnownGeneratorInputs.CompilationOptions)); + public IncrementalValueProvider ParseOptionsProvider => new IncrementalValueProvider(SharedInputNodes.ParseOptions.WithRegisterOutput(RegisterOutput).WithTrackingName(WellKnownGeneratorInputs.ParseOptions)); public IncrementalValuesProvider AdditionalTextsProvider => new IncrementalValuesProvider(SharedInputNodes.AdditionalTexts.WithRegisterOutput(RegisterOutput).WithTrackingName(WellKnownGeneratorInputs.AdditionalTexts)); diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxStrategy.cs index c5fb5abf53414..fb07730cdc751 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxStrategy.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.SourceGeneration; namespace Microsoft.CodeAnalysis { diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs index 5765a69131d77..90eb622b3af5e 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -15,12 +16,17 @@ namespace Microsoft.CodeAnalysis internal sealed class PredicateSyntaxStrategy : ISyntaxSelectionStrategy { private readonly Func _transformFunc; + private readonly ISyntaxHelper _syntaxHelper; private readonly Func _filterFunc; private readonly object _filterKey = new object(); - internal PredicateSyntaxStrategy(Func filterFunc, Func transformFunc) + internal PredicateSyntaxStrategy( + Func filterFunc, + Func transformFunc, + ISyntaxHelper syntaxHelper) { _transformFunc = transformFunc; + _syntaxHelper = syntaxHelper; _filterFunc = filterFunc; } @@ -87,7 +93,7 @@ public void VisitTree( foreach (SyntaxNode node in nodes) { var stopwatch = SharedStopwatch.StartNew(); - var value = new GeneratorSyntaxContext(node, model); + var value = new GeneratorSyntaxContext(node, model, _owner._syntaxHelper); var transformed = _owner._transformFunc(value, cancellationToken); // The SemanticModel we provide to GeneratorSyntaxContext is never guaranteed to be the same between runs, diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SharedInputNodes.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SharedInputNodes.cs index 2f10ffd477e5a..390c2299e4dd4 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SharedInputNodes.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SharedInputNodes.cs @@ -14,6 +14,8 @@ internal static class SharedInputNodes { public static readonly InputNode Compilation = new InputNode(b => ImmutableArray.Create(b.Compilation)); + public static readonly InputNode CompilationOptions = new(b => ImmutableArray.Create(b.Compilation.Options)); + public static readonly InputNode ParseOptions = new InputNode(b => ImmutableArray.Create(b.DriverState.ParseOptions)); public static readonly InputNode AdditionalTexts = new InputNode(b => b.DriverState.AdditionalTexts); diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxNodeGrouping.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxNodeGrouping.cs new file mode 100644 index 0000000000000..d138b6d166de5 --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxNodeGrouping.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +public partial struct SyntaxValueProvider +{ + /// + /// Wraps a grouping of nodes within a syntax tree so we can have value-semantics around them usable by the + /// incremental driver. Note: we do something very sneaky here. Specifically, as long as we have the same from before, then we know we must have the same nodes as before (since the nodes are + /// entirely determined from the text+options which is exactly what the syntax tree represents). Similarly, if the + /// syntax tree changes, we will always get different nodes (since they point back at the syntax tree). So we can + /// just use the syntax tree itself to determine value semantics here. + /// + private class SyntaxNodeGrouping : IEquatable> + where TSyntaxNode : SyntaxNode + { + public readonly SyntaxTree SyntaxTree; + public readonly ImmutableArray SyntaxNodes; + + public SyntaxNodeGrouping(IGrouping grouping) + { + SyntaxTree = grouping.Key; + SyntaxNodes = grouping.OrderBy(static n => n.FullSpan.Start).ToImmutableArray(); + } + + public override int GetHashCode() + => SyntaxTree.GetHashCode(); + + public override bool Equals(object? obj) + => Equals(obj as SyntaxNodeGrouping); + + public bool Equals(SyntaxNodeGrouping? obj) + => this.SyntaxTree == obj?.SyntaxTree; + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs index ec5aeb56e0872..f1ce649a6dd96 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; +using Microsoft.CodeAnalysis.SourceGeneration; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -14,11 +15,16 @@ internal sealed class SyntaxReceiverStrategy : ISyntaxSelectionStrategy { private readonly SyntaxContextReceiverCreator _receiverCreator; private readonly Action _registerOutput; + private readonly ISyntaxHelper _syntaxHelper; - public SyntaxReceiverStrategy(SyntaxContextReceiverCreator receiverCreator, Action registerOutput) + public SyntaxReceiverStrategy( + SyntaxContextReceiverCreator receiverCreator, + Action registerOutput, + ISyntaxHelper syntaxHelper) { _receiverCreator = receiverCreator; _registerOutput = registerOutput; + _syntaxHelper = syntaxHelper; } public ISyntaxInputBuilder GetBuilder(StateTableStore table, object key, bool trackIncrementalSteps, string? name, IEqualityComparer? comparer) => new Builder(this, key, table, trackIncrementalSteps); @@ -48,7 +54,7 @@ public Builder(SyntaxReceiverStrategy owner, object key, StateTableStore driv if (_receiver is object) { - _walker = new GeneratorSyntaxWalker(_receiver); + _walker = new GeneratorSyntaxWalker(_receiver, owner._syntaxHelper); } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.ImmutableArrayValueComparer.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.ImmutableArrayValueComparer.cs new file mode 100644 index 0000000000000..d1cadedcf503d --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.ImmutableArrayValueComparer.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +public partial struct SyntaxValueProvider +{ + private class ImmutableArrayValueComparer : IEqualityComparer> + { + public static readonly IEqualityComparer> Instance = new ImmutableArrayValueComparer(); + + public bool Equals(ImmutableArray x, ImmutableArray y) + { + if (x == y) + return true; + + return x.SequenceEqual(y, 0, static (a, b, _) => EqualityComparer.Default.Equals(a, b)); + } + + public int GetHashCode(ImmutableArray obj) + { + var hashCode = 0; + foreach (var value in obj) + hashCode = Hash.Combine(hashCode, EqualityComparer.Default.GetHashCode(value!)); + + return hashCode; + } + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.cs index 69703c2a79256..b5655a724f5d5 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider.cs @@ -6,21 +6,30 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; namespace Microsoft.CodeAnalysis { /// /// Allows a user to create Syntax based input nodes for incremental generation /// - public readonly struct SyntaxValueProvider + public readonly partial struct SyntaxValueProvider { + private readonly IncrementalGeneratorInitializationContext _context; private readonly ArrayBuilder _inputNodes; private readonly Action _registerOutput; + private readonly ISyntaxHelper _syntaxHelper; - internal SyntaxValueProvider(ArrayBuilder inputNodes, Action registerOutput) + internal SyntaxValueProvider( + IncrementalGeneratorInitializationContext context, + ArrayBuilder inputNodes, + Action registerOutput, + ISyntaxHelper syntaxHelper) { + _context = context; _inputNodes = inputNodes; _registerOutput = registerOutput; + _syntaxHelper = syntaxHelper; } /// @@ -33,7 +42,10 @@ internal SyntaxValueProvider(ArrayBuilder inputNodes, Action CreateSyntaxProvider(Func predicate, Func transform) { // registration of the input is deferred until we know the node is used - return new IncrementalValuesProvider(new SyntaxInputNode(new PredicateSyntaxStrategy(predicate.WrapUserFunction(), transform.WrapUserFunction()), RegisterOutputAndDeferredInput)); + return new IncrementalValuesProvider( + new SyntaxInputNode( + new PredicateSyntaxStrategy(predicate.WrapUserFunction(), transform.WrapUserFunction(), _syntaxHelper), + RegisterOutputAndDeferredInput)); } /// @@ -41,7 +53,8 @@ public IncrementalValuesProvider CreateSyntaxProvider(Func internal IncrementalValueProvider CreateSyntaxReceiverProvider(SyntaxContextReceiverCreator creator) { - var node = new SyntaxInputNode(new SyntaxReceiverStrategy(creator, _registerOutput), RegisterOutputAndDeferredInput); + var node = new SyntaxInputNode( + new SyntaxReceiverStrategy(creator, _registerOutput, _syntaxHelper), RegisterOutputAndDeferredInput); _inputNodes.Add(node); return new IncrementalValueProvider(node); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithMetadataName.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithMetadataName.cs new file mode 100644 index 0000000000000..4fd4fc272b937 --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithMetadataName.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Xml.Serialization; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.SourceGeneration; +using System.Threading; + +namespace Microsoft.CodeAnalysis; + +using Aliases = ArrayBuilder<(string aliasName, string symbolName)>; + +public readonly struct GeneratorAttributeSyntaxContext +{ + /// + /// The syntax node the attribute is attached to. For example, with [CLSCompliant] class C { } this would + /// the class declaration node. + /// + public SyntaxNode TargetNode { get; } + + /// + /// The symbol that the attribute is attached to. For example, with [CLSCompliant] class C { } this would be + /// the for "C". + /// + public ISymbol TargetSymbol { get; } + + /// + /// Semantic model for the file that is contained within. + /// + public SemanticModel SemanticModel { get; } + + /// + /// s for any matching attributes on . Always non-empty. All + /// these attributes will have an whose fully qualified name metadata + /// name matches the name requested in . + /// + /// To get the entire list of attributes, use on . + /// + /// + public ImmutableArray Attributes { get; } + + internal GeneratorAttributeSyntaxContext( + SyntaxNode targetNode, + ISymbol targetSymbol, + SemanticModel semanticModel, + ImmutableArray attributes) + { + TargetNode = targetNode; + TargetSymbol = targetSymbol; + SemanticModel = semanticModel; + Attributes = attributes; + } +} + +public partial struct SyntaxValueProvider +{ + private static readonly char[] s_nestedTypeNameSeparators = new char[] { '+' }; + private static readonly SymbolDisplayFormat s_metadataDisplayFormat = + SymbolDisplayFormat.QualifiedNameArityFormat.AddCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes); + + /// + /// Creates an that can provide a transform over all s if that node has an attribute on it that binds to a with the + /// same fully-qualified metadata as the provided . should be the fully-qualified, metadata name of the attribute, including the + /// Attribute suffix. For example "System.CLSCompliantAttribute for . + /// + /// A function that determines if the given attribute target () should be transformed. Nodes that do not pass this + /// predicate will not have their attributes looked at at all. + /// A function that performs the transform. This will only be passed nodes that return for and which have a matching whose + /// has the same fully qualified, metadata name as . + public IncrementalValuesProvider ForAttributeWithMetadataName( + string fullyQualifiedMetadataName, + Func predicate, + Func transform) + { + var metadataName = fullyQualifiedMetadataName.Contains('+') + ? MetadataTypeName.FromFullName(fullyQualifiedMetadataName.Split(s_nestedTypeNameSeparators).Last()) + : MetadataTypeName.FromFullName(fullyQualifiedMetadataName); + + var nodesWithAttributesMatchingSimpleName = this.ForAttributeWithSimpleName(metadataName.UnmangledTypeName, predicate); + + var collectedNodes = nodesWithAttributesMatchingSimpleName + .Collect() + .WithComparer(ImmutableArrayValueComparer.Instance) + .WithTrackingName("collectedNodes_ForAttributeWithMetadataName"); + + // Group all the nodes by syntax tree, so we can process a whole syntax tree at a time. This will let us make + // the required semantic model for it once, instead of potentially many times (in the rare, but possible case of + // a single file with a ton of matching nodes in it). + var groupedNodes = collectedNodes.SelectMany( + static (array, cancellationToken) => + array.GroupBy(static n => n.SyntaxTree) + .Select(static g => new SyntaxNodeGrouping(g))).WithTrackingName("groupedNodes_ForAttributeWithMetadataName"); + + var compilationAndGroupedNodesProvider = groupedNodes + .Combine(_context.CompilationProvider) + .WithTrackingName("compilationAndGroupedNodes_ForAttributeWithMetadataName"); + + var syntaxHelper = _context.SyntaxHelper; + var finalProvider = compilationAndGroupedNodesProvider.SelectMany((tuple, cancellationToken) => + { + var (grouping, compilation) = tuple; + + var result = ArrayBuilder.GetInstance(); + try + { + var syntaxTree = grouping.SyntaxTree; + var semanticModel = compilation.GetSemanticModel(syntaxTree); + + foreach (var targetNode in grouping.SyntaxNodes) + { + cancellationToken.ThrowIfCancellationRequested(); + + var targetSymbol = + targetNode is ICompilationUnitSyntax compilationUnit ? semanticModel.Compilation.Assembly : + syntaxHelper.IsLambdaExpression(targetNode) ? semanticModel.GetSymbolInfo(targetNode, cancellationToken).Symbol : + semanticModel.GetDeclaredSymbol(targetNode, cancellationToken); + if (targetSymbol is null) + continue; + + var attributes = getMatchingAttributes(targetNode, targetSymbol, fullyQualifiedMetadataName); + if (attributes.Length > 0) + { + result.Add(transform( + new GeneratorAttributeSyntaxContext(targetNode, targetSymbol, semanticModel, attributes), + cancellationToken)); + } + } + + return result.ToImmutable(); + } + finally + { + result.Free(); + } + }).WithTrackingName("result_ForAttributeWithMetadataName"); + + return finalProvider; + + static ImmutableArray getMatchingAttributes( + SyntaxNode attributeTarget, + ISymbol symbol, + string fullyQualifiedMetadataName) + { + var targetSyntaxTree = attributeTarget.SyntaxTree; + var result = ArrayBuilder.GetInstance(); + + addMatchingAttributes(symbol.GetAttributes()); + addMatchingAttributes((symbol as IMethodSymbol)?.GetReturnTypeAttributes()); + + if (symbol is IAssemblySymbol assemblySymbol) + { + foreach (var module in assemblySymbol.Modules) + addMatchingAttributes(module.GetAttributes()); + } + + return result.ToImmutableAndFree(); + + void addMatchingAttributes(ImmutableArray? attributes) + { + if (!attributes.HasValue) + return; + + foreach (var attribute in attributes.Value) + { + if (attribute.ApplicationSyntaxReference?.SyntaxTree == targetSyntaxTree && + attribute.AttributeClass?.ToDisplayString(s_metadataDisplayFormat) == fullyQualifiedMetadataName) + { + result.Add(attribute); + } + } + } + } + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs new file mode 100644 index 0000000000000..8a61b367ef86e --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxValueProvider_ForAttributeWithSimpleName.cs @@ -0,0 +1,284 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using System.Transactions; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SourceGeneration; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +using Aliases = ArrayBuilder<(string aliasName, string symbolName)>; + +public partial struct SyntaxValueProvider +{ + private static readonly ObjectPool> s_stackPool = new(static () => new()); + + /// + /// Returns all syntax nodes of that match if that node has an attribute on it that + /// could possibly bind to the provided . should be the + /// simple, non-qualified, name of the attribute, including the Attribute suffix, and not containing any + /// generics, containing types, or namespaces. For example CLSCompliantAttribute for . + /// This provider understands (Import in Visual Basic) aliases and will find + /// matches even when the attribute references an alias name. For example, given: + /// + /// using XAttribute = System.CLSCompliantAttribute; + /// [X] + /// class C { } + /// + /// Then + /// context.SyntaxProvider.CreateSyntaxProviderForAttribute(nameof(CLSCompliantAttribute), (node, c) => node is ClassDeclarationSyntax) + /// will find the C class. + /// + internal IncrementalValuesProvider ForAttributeWithSimpleName( + string simpleName, + Func predicate) + { + var syntaxHelper = _context.SyntaxHelper; + + // Create a provider that provides (and updates) the global aliases for any particular file when it is edited. + var individualFileGlobalAliasesProvider = this.CreateSyntaxProvider( + static (n, _) => n is ICompilationUnitSyntax, + static (context, _) => getGlobalAliasesInCompilationUnit(context.SyntaxHelper, context.Node)).WithTrackingName("individualFileGlobalAliases_ForAttribute"); + + // Create an aggregated view of all global aliases across all files. This should only update when an individual + // file changes its global aliases or a file is added / removed from the compilation + var collectedGlobalAliasesProvider = individualFileGlobalAliasesProvider + .Collect() + .WithComparer(ImmutableArrayValueComparer.Instance) + .WithTrackingName("collectedGlobalAliases_ForAttribute"); + + var allUpGlobalAliasesProvider = collectedGlobalAliasesProvider + .Select(static (arrays, _) => GlobalAliases.Create(arrays.SelectMany(a => a.AliasAndSymbolNames).ToImmutableArray())) + .WithTrackingName("allUpGlobalAliases_ForAttribute"); + + // Regenerate our data if the compilation options changed. VB can supply global aliases with compilation options, + // so we have to reanalyze everything if those changed. + var compilationGlobalAliases = _context.CompilationOptionsProvider.Select( + (o, _) => + { + var aliases = Aliases.GetInstance(); + syntaxHelper.AddAliases(o, aliases); + return GlobalAliases.Create(aliases.ToImmutableAndFree()); + }).WithTrackingName("compilationGlobalAliases_ForAttribute"); + + allUpGlobalAliasesProvider = allUpGlobalAliasesProvider + .Combine(compilationGlobalAliases) + .Select((tuple, _) => GlobalAliases.Concat(tuple.Left, tuple.Right)) + .WithTrackingName("allUpIncludingCompilationGlobalAliases_ForAttribute"); + + // Create a syntax provider for every compilation unit. + var compilationUnitProvider = this.CreateSyntaxProvider( + static (n, _) => n is ICompilationUnitSyntax, + static (context, _) => context.Node).WithTrackingName("compilationUnit_ForAttribute"); + + // Combine the two providers so that we reanalyze every file if the global aliases change, or we reanalyze a + // particular file when it's compilation unit changes. + var compilationUnitAndGlobalAliasesProvider = compilationUnitProvider + .Combine(allUpGlobalAliasesProvider) + .WithTrackingName("compilationUnitAndGlobalAliases_ForAttribute"); + + // For each pair of compilation unit + global aliases, walk the compilation unit + var result = compilationUnitAndGlobalAliasesProvider + .SelectMany((globalAliasesAndCompilationUnit, cancellationToken) => GetMatchingNodes( + syntaxHelper, globalAliasesAndCompilationUnit.Right, globalAliasesAndCompilationUnit.Left, simpleName, predicate, cancellationToken)) + .WithTrackingName("result_ForAttribute"); + + return result; + + static GlobalAliases getGlobalAliasesInCompilationUnit( + ISyntaxHelper syntaxHelper, + SyntaxNode compilationUnit) + { + Debug.Assert(compilationUnit is ICompilationUnitSyntax); + var globalAliases = Aliases.GetInstance(); + + syntaxHelper.AddAliases(compilationUnit, globalAliases, global: true); + + return GlobalAliases.Create(globalAliases.ToImmutableAndFree()); + } + } + + private static ImmutableArray GetMatchingNodes( + ISyntaxHelper syntaxHelper, + GlobalAliases globalAliases, + SyntaxNode compilationUnit, + string name, + Func predicate, + CancellationToken cancellationToken) + { + Debug.Assert(compilationUnit is ICompilationUnitSyntax); + + var isCaseSensitive = syntaxHelper.IsCaseSensitive; + var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + + // As we walk down the compilation unit and nested namespaces, we may encounter additional using aliases local + // to this file. Keep track of them so we can determine if they would allow an attribute in code to bind to the + // attribute being searched for. + var localAliases = Aliases.GetInstance(); + var nameHasAttributeSuffix = name.HasAttributeSuffix(isCaseSensitive); + + // Used to ensure that as we recurse through alias names to see if they could bind to attributeName that we + // don't get into cycles. + var seenNames = s_stackPool.Allocate(); + var results = ArrayBuilder.GetInstance(); + var attributeTargets = ArrayBuilder.GetInstance(); + + try + { + recurse(compilationUnit); + } + finally + { + localAliases.Free(); + seenNames.Clear(); + s_stackPool.Free(seenNames); + attributeTargets.Free(); + } + + results.RemoveDuplicates(); + return results.ToImmutableAndFree(); + + void recurse(SyntaxNode node) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (node is ICompilationUnitSyntax) + { + syntaxHelper.AddAliases(node, localAliases, global: false); + + recurseChildren(node); + } + else if (syntaxHelper.IsAnyNamespaceBlock(node)) + { + var localAliasCount = localAliases.Count; + syntaxHelper.AddAliases(node, localAliases, global: false); + + recurseChildren(node); + + // after recursing into this namespace, dump any local aliases we added from this namespace decl itself. + localAliases.Count = localAliasCount; + } + else if (syntaxHelper.IsAttributeList(node)) + { + foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node)) + { + // Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix. + // e.g. if there is [X] then we have to lookup with X and with XAttribute. + var simpleAttributeName = syntaxHelper.GetUnqualifiedIdentifierOfName( + syntaxHelper.GetNameOfAttribute(attribute)).ValueText; + if (matchesAttributeName(simpleAttributeName, withAttributeSuffix: false) || + matchesAttributeName(simpleAttributeName, withAttributeSuffix: true)) + { + attributeTargets.Clear(); + syntaxHelper.AddAttributeTargets(node, attributeTargets); + + foreach (var target in attributeTargets) + { + if (predicate(target, cancellationToken)) + results.Add(target); + } + + return; + } + } + + // attributes can't have attributes inside of them. so no need to recurse when we're done. + } + else + { + // For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot + // terminate the search anywhere as attributes may be found on things like local functions, and that + // means having to dive deep into statements and expressions. + recurseChildren(node); + } + + return; + + void recurseChildren(SyntaxNode node) + { + foreach (var child in node.ChildNodesAndTokens()) + { + if (child.IsNode) + recurse(child.AsNode()!); + } + } + } + + // Checks if `name` is equal to `matchAgainst`. if `withAttributeSuffix` is true, then + // will check if `name` + "Attribute" is equal to `matchAgainst` + bool matchesName(string name, string matchAgainst, bool withAttributeSuffix) + { + if (withAttributeSuffix) + { + return name.Length + "Attribute".Length == matchAgainst.Length && + matchAgainst.HasAttributeSuffix(isCaseSensitive) && + matchAgainst.StartsWith(name, comparison); + } + else + { + return name.Equals(matchAgainst, comparison); + } + } + + bool matchesAttributeName(string currentAttributeName, bool withAttributeSuffix) + { + // If the names match, we're done. + if (withAttributeSuffix) + { + if (nameHasAttributeSuffix && + matchesName(currentAttributeName, name, withAttributeSuffix)) + { + return true; + } + } + else + { + if (matchesName(currentAttributeName, name, withAttributeSuffix: false)) + return true; + } + + // Otherwise, keep searching through aliases. Check that this is the first time seeing this name so we + // don't infinite recurse in error code where aliases reference each other. + // + // note: as we recurse up the aliases, we do not want to add the attribute suffix anymore. aliases must + // reference the actual real name of the symbol they are aliasing. + if (seenNames.Contains(currentAttributeName)) + return false; + + seenNames.Push(currentAttributeName); + + foreach (var (aliasName, symbolName) in localAliases) + { + // see if user wrote `[SomeAlias]`. If so, if we find a `using SomeAlias = ...` recurse using the + // ... name portion to see if it might bind to the attr name the caller is searching for. + if (matchesName(currentAttributeName, aliasName, withAttributeSuffix) && + matchesAttributeName(symbolName, withAttributeSuffix: false)) + { + return true; + } + } + + foreach (var (aliasName, symbolName) in globalAliases.AliasAndSymbolNames) + { + if (matchesName(currentAttributeName, aliasName, withAttributeSuffix) && + matchesAttributeName(symbolName, withAttributeSuffix: false)) + { + return true; + } + } + + seenNames.Pop(); + return false; + } + } +} diff --git a/src/Compilers/Core/Portable/SourceGeneration/WellKnownGeneratorInputs.cs b/src/Compilers/Core/Portable/SourceGeneration/WellKnownGeneratorInputs.cs index 33561e3e435e5..28b24b0d3e134 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/WellKnownGeneratorInputs.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/WellKnownGeneratorInputs.cs @@ -11,6 +11,8 @@ public static class WellKnownGeneratorInputs { public const string Compilation = nameof(Compilation); + internal const string CompilationOptions = nameof(CompilationOptions); + public const string ParseOptions = nameof(ParseOptions); public const string AdditionalTexts = nameof(AdditionalTexts); diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs index 15ac4fbde7c89..5ebdbeffa65da 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs @@ -62,5 +62,10 @@ internal enum SymbolDisplayCompilerInternalOptions /// Display `System.[U]IntPtr` instead of `n[u]int`. /// UseNativeIntegerUnderlyingType = 1 << 7, + + /// + /// Separate out nested types from containing types using + instead of . (dot). + /// + UsePlusForNestedTypes = 1 << 8, } } diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs index a41c45d1070d7..d8a5dc6aba65e 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs @@ -758,6 +758,12 @@ public SymbolDisplayFormat RemoveLocalOptions(SymbolDisplayLocalOptions options) return this.WithLocalOptions(this.LocalOptions & ~options); } + /// + /// Creates a copy of the SymbolDisplayFormat but with added set of . + /// + internal SymbolDisplayFormat AddCompilerInternalOptions(SymbolDisplayCompilerInternalOptions options) + => WithCompilerInternalOptions(this.CompilerInternalOptions | options); + /// /// Creates a copy of the SymbolDisplayFormat but with replaced set of . /// diff --git a/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicGeneratorDriver.vb b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicGeneratorDriver.vb index d07df30ad0cc9..1a28650bf1cd2 100644 --- a/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicGeneratorDriver.vb +++ b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicGeneratorDriver.vb @@ -6,6 +6,9 @@ Imports System.Collections.Immutable Imports System.ComponentModel Imports System.Threading Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.SourceGeneration +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic @@ -50,6 +53,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + Friend Overrides ReadOnly Property SyntaxHelper As ISyntaxHelper = VisualBasicSyntaxHelper.Instance End Class - End Namespace diff --git a/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb new file mode 100644 index 0000000000000..8aa7563127965 --- /dev/null +++ b/src/Compilers/VisualBasic/Portable/SourceGeneration/VisualBasicSyntaxHelper.vb @@ -0,0 +1,120 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.SourceGeneration +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic + Friend NotInheritable Class VisualBasicSyntaxHelper + Inherits AbstractSyntaxHelper + + Public Shared ReadOnly Instance As ISyntaxHelper = New VisualBasicSyntaxHelper() + + Private Sub New() + End Sub + + Public Overrides ReadOnly Property IsCaseSensitive As Boolean = False + + Public Overrides Function IsValidIdentifier(name As String) As Boolean + Return SyntaxFacts.IsValidIdentifier(name) + End Function + + Public Overrides Function IsAnyNamespaceBlock(node As SyntaxNode) As Boolean + Return TypeOf node Is NamespaceBlockSyntax + End Function + + Public Overrides Function IsAttribute(node As SyntaxNode) As Boolean + Return TypeOf node Is AttributeSyntax + End Function + + Public Overrides Function GetNameOfAttribute(node As SyntaxNode) As SyntaxNode + Return DirectCast(node, AttributeSyntax).Name + End Function + + Public Overrides Function IsAttributeList(node As SyntaxNode) As Boolean + Return TypeOf node Is AttributeListSyntax + End Function + + Public Overrides Sub AddAttributeTargets(node As SyntaxNode, targets As ArrayBuilder(Of SyntaxNode)) + Dim attributeList = DirectCast(node, AttributeListSyntax) + + Dim container = attributeList.Parent + If TypeOf container Is AttributesStatementSyntax Then + ' for attribute statements (like ``) we want to get the parent compilation unit as that's + ' what symbol will actually own the attribute. + targets.Add(container.Parent) + ElseIf TypeOf container Is FieldDeclarationSyntax Then + Dim field = DirectCast(container, FieldDeclarationSyntax) + For Each varDecl In field.Declarators + For Each id In varDecl.Names + targets.Add(id) + Next + Next + Else + targets.Add(container) + End If + End Sub + + Public Overrides Function GetAttributesOfAttributeList(node As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) + Return DirectCast(node, AttributeListSyntax).Attributes + End Function + + Public Overrides Function IsLambdaExpression(node As SyntaxNode) As Boolean + Return TypeOf node Is LambdaExpressionSyntax + End Function + + Public Overrides Function GetUnqualifiedIdentifierOfName(node As SyntaxNode) As SyntaxToken + Dim name = DirectCast(node, NameSyntax) + + Dim qualifiedName = TryCast(name, QualifiedNameSyntax) + If qualifiedName IsNot Nothing Then + Return qualifiedName.Right.Identifier + End If + + Dim simpleName = TryCast(node, SimpleNameSyntax) + If simpleName IsNot Nothing Then + Return simpleName.Identifier + End If + + Throw ExceptionUtilities.UnexpectedValue(node.Kind()) + End Function + + Public Overrides Sub AddAliases(node As SyntaxNode, aliases As ArrayBuilder(Of (aliasName As String, symbolName As String)), [global] As Boolean) + ' VB does not have global aliases at the syntax level. + If [global] Then + Return + End If + + Dim compilationUnit = TryCast(node, CompilationUnitSyntax) + If compilationUnit Is Nothing Then + Return + End If + + For Each importsStatement In compilationUnit.Imports + For Each importsClause In importsStatement.ImportsClauses + ProcessImportsClause(aliases, importsClause) + Next + Next + End Sub + + Public Overrides Sub AddAliases(options As CompilationOptions, aliases As ArrayBuilder(Of (aliasName As String, symbolName As String))) + Dim vbOptions = DirectCast(options, VisualBasicCompilationOptions) + + For Each globalImport In vbOptions.GlobalImports + Dim clause = globalImport.Clause + ProcessImportsClause(aliases, clause) + Next + End Sub + + Private Sub ProcessImportsClause(aliases As ArrayBuilder(Of (aliasName As String, symbolName As String)), clause As ImportsClauseSyntax) + Dim importsClause = TryCast(clause, SimpleImportsClauseSyntax) + If importsClause?.Alias IsNot Nothing Then + aliases.Add((importsClause.Alias.Identifier.ValueText, GetUnqualifiedIdentifierOfName(importsClause.Name).ValueText)) + End If + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb index 9a722fabd82c5..c198747a14905 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb @@ -160,7 +160,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If containingType IsNot Nothing Then visitedParents = True containingType.Accept(Me.NotFirstVisitor()) - AddOperator(SyntaxKind.DotToken) + + If format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) Then + AddOperator(SyntaxKind.PlusToken) + Else + AddOperator(SyntaxKind.DotToken) + End If End If End If diff --git a/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.vb b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.vb new file mode 100644 index 0000000000000..f2c81d39c6d44 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_FullyQualifiedName.vb @@ -0,0 +1,1311 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Collections.Immutable +Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests +Imports Roslyn.Test.Utilities.TestGenerators + +Namespace Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.SourceGeneration + Friend Module IncrementalGeneratorInitializationContextExtensions + + Public Function ForAttributeWithSimpleName(Of T As SyntaxNode)( + context As IncrementalGeneratorInitializationContext, simpleName As String) As IncrementalValuesProvider(Of T) + + Return context.SyntaxProvider.ForAttributeWithSimpleName( + simpleName, + Function(node, c) TypeOf node Is T).Select(Function(node, c) DirectCast(node, T)) + End Function + + + Public Function ForAttributeWithMetadataName(Of t As SyntaxNode)( + context As IncrementalGeneratorInitializationContext, fullyQualifiedMetadataName As String) As IncrementalValuesProvider(Of t) + + Return context.SyntaxProvider.ForAttributeWithMetadataName( + fullyQualifiedMetadataName, + Function(node, c) TypeOf node Is t, + Function(ctx, c) DirectCast(ctx.TargetNode, t)) + End Function + End Module + + Public Class GeneratorDriverTests_Attributes_FullyQualifiedName + Inherits BasicTestBase + + Private Shared Function IsClassStatementWithName(value As Object, name As String) As Boolean + If TypeOf value IsNot ClassStatementSyntax Then + Return False + End If + + Return DirectCast(value, ClassStatementSyntax).Identifier.ValueText = name + End Function + +#Region "Non-Incremental tests" + + ' These tests just validate basic correctness of results in different scenarios, without actually validating + ' that the incremental nature of this provider works properly. + + + Public Sub FindCorrectAttributeOnTopLevelClass_WhenSearchingForClassDeclaration1() + Dim source = " + +class C1 +end class + +class C2 +end class + +namespace N1 + class XAttribute + inherits System.Attribute + end class +end namespace + +namespace N2 + class XAttribute + inherits System.Attribute + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("N1.XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C1"))) + End Sub + + + Public Sub FindCorrectAttributeOnTopLevelClass_WhenSearchingForClassDeclaration2() + Dim source = " + +class C1 +end class + +class C2 +end class + +namespace N1 + class XAttribute + inherits System.Attribute + end class +end namespace + +namespace N2 + class XAttribute + inherits System.Attribute + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("N2.XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), +Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C2"))) + End Sub + + + + + Public Sub DoNotAttributeOnTopLevelClass_WhenSearchingForSimpleName1(name As String) + Dim source = " + +class C1 +end class + +class C2 +end class + +namespace N1 + class XAttribute + inherits System.Attribute + end class +end namespace + +namespace N2 + class XAttribute + inherits System.Attribute + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)(name) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + End Sub + + + + + + + Public Sub FindAssemblyAttribute1(attribute As String) + Dim source = $" +Imports System + +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of CompilationUnitSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, CompilationUnitSyntax).SyntaxTree Is compilation.SyntaxTrees.First)) + End Sub + + + + + + + Public Sub FindModuleAttribute1(attribute As String) + Dim source = $" +Imports System + +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of CompilationUnitSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, CompilationUnitSyntax).SyntaxTree Is compilation.SyntaxTrees.First)) + End Sub + + + Public Sub FindAssemblyAttribute4() + Dim source1 = " +Imports System + +" + Dim source2 = " +Imports System +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of CompilationUnitSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, CompilationUnitSyntax).SyntaxTree Is compilation.SyntaxTrees.First), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, CompilationUnitSyntax).SyntaxTree Is compilation.SyntaxTrees.Last)) + End Sub + + + Public Sub FindMethodStatementAttribute1() + Dim source = " +Imports System + +Class C + + Public Sub M() + End Sub +End Class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of MethodStatementSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, MethodStatementSyntax).Identifier.ValueText = "M")) + End Sub + + + Public Sub FindMethodStatementAttribute2() + Dim source = " +Imports System + +MustInherit Class C + + Public MustOverride Sub M() +End Class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of MethodStatementSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, MethodStatementSyntax).Identifier.ValueText = "M")) + End Sub + + + Public Sub FindFieldAttribute1() + Dim source = " +Imports System + +Class C + + dim a as integer +End Class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ModifiedIdentifierSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(DirectCast(_step.Outputs.Single().Value, ModifiedIdentifierSyntax).Identifier.ValueText = "a")) + End Sub + + + Public Sub FindFieldAttribute3() + Dim source = " +Imports System + +Class C + + dim a, b as integer +End Class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ModifiedIdentifierSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.Collection(_step.Outputs, + Sub(v) Assert.True(DirectCast(v.Value, ModifiedIdentifierSyntax).Identifier.ValueText = "a"), + Sub(v) Assert.True(DirectCast(v.Value, ModifiedIdentifierSyntax).Identifier.ValueText = "b"))) + End Sub + + + Public Sub FindFieldAttribute2() + Dim source = " +Imports System + +Class C + + dim a as string, b as integer +End Class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ModifiedIdentifierSyntax)("System.CLSCompliantAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.Collection(_step.Outputs, + Sub(v) Assert.True(DirectCast(v.Value, ModifiedIdentifierSyntax).Identifier.ValueText = "a"), + Sub(v) Assert.True(DirectCast(v.Value, ModifiedIdentifierSyntax).Identifier.ValueText = "b"))) + End Sub + + + Public Sub FindNestedAttribute1() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute + inherits System.Attribute + end class +end class + +class Outer2 + public class InnerAttribute + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer1+InnerAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C1"))) + End Sub + + + Public Sub FindNestedAttribute2() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute + inherits System.Attribute + end class +end class + +class Outer2 + public class InnerAttribute + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer2+InnerAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C2"))) + End Sub + + + Public Sub FindNestedGenericAttribute1() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute(of T1) + inherits System.Attribute + end class +end class +class Outer2 + public class InnerAttribute(of T1, T2) + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer1+InnerAttribute`1") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + End Sub + + + Public Sub FindNestedGenericAttribute2() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute(of T1) + inherits System.Attribute + end class +end class +class Outer2 + public class InnerAttribute(of T1, T2) + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer2+InnerAttribute`2") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + End Sub + + + Public Sub DoNotFindNestedGenericAttribute1() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute(of T1) + inherits System.Attribute + end class +end class +class Outer2 + public class InnerAttribute(of T1, T2) + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer1+InnerAttribute`2") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + End Sub + + + Public Sub DoNotFindNestedGenericAttribute2() + Dim source = " + +class C1 +end class + +class C2 +end class + +class Outer1 + public class InnerAttribute(of T1) + inherits System.Attribute + end class +end class + +class Outer2 + public class InnerAttribute(of T1, T2) + inherits System.Attribute + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("Outer2+InnerAttribute`1") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList1() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(2, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList1B() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(2, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList2() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(1, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList2B() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(1, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList3() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(1, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_MultipleAttributesInList3B() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.SyntaxProvider.ForAttributeWithMetadataName(Of ClassStatementSyntax)( + "XAttribute", + Function(a, b) True, + Function(ctx1, c) + Assert.Equal(1, ctx1.Attributes.Length) + Return DirectCast(ctx1.TargetNode, ClassStatementSyntax) + End Function) + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + +#End Region + +#Region "Incremental tests" + + ' These tests validate minimal recomputation performed after changes are made to the compilation. + + + Public Sub RerunOnSameCompilationCachesResultFully() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + ' re-run without changes + driver = driver.RunGenerators(compilation) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunWithReferencesChange() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + ' re-run without changes + driver = driver.RunGenerators(compilation.WithReferences(compilation.References.Take(compilation.References.Count() - 1))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunWithAddedFile1() + Dim source = " + +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From("")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunWithAddedFile2() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" +class XAttribute + inherits System.Attribute +end class +")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunWithAddedFile_MultipleResults_SameFile1() + Dim source = " + +class C1 +end class + +class C2 +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" +class XAttribute + inherits System.Attribute +end class")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.Collection(_step.Outputs, + Sub(t) Assert.True(IsClassStatementWithName(t.Value, "C1")), + Sub(t) Assert.True(IsClassStatementWithName(t.Value, "C2")))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Collection(runResult.TrackedSteps("result_ForAttribute").Single().Outputs, + Sub(t) Assert.Equal(IncrementalStepRunReason.Cached, t.Reason), + Sub(t) Assert.Equal(IncrementalStepRunReason.Cached, t.Reason)) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs, + Sub(t) Assert.Equal(IncrementalStepRunReason.Modified, t.Reason), + Sub(t) Assert.Equal(IncrementalStepRunReason.Modified, t.Reason)) + End Sub + + + Public Sub RerunWithAddedFile_MultipleResults_MultipleFile1() + Dim source1 = " + +class C1 +end class +" + Dim source2 = " + +class C2 +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees(compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" +class XAttribute + inherits System.Attribute +end class")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), +Sub(_step) Assert.Collection(_step.Outputs, Sub(t) Assert.True(IsClassStatementWithName(t.Value, "C1"))), +Sub(_step) Assert.Collection(_step.Outputs, Sub(t) Assert.True(IsClassStatementWithName(t.Value, "C2")))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Collection(runResult.TrackedSteps("compilationUnit_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)) + Assert.Collection(runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason)) + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Collection(runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName"), + Sub(s) Assert.Collection(s.Outputs, + Sub(t) Assert.Equal(IncrementalStepRunReason.Cached, t.Reason), + Sub(t) Assert.Equal(IncrementalStepRunReason.Cached, t.Reason))) + Assert.Collection(runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason)) + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Modified, s.Outputs.Single().Reason)) + End Sub + + + Public Sub RerunWithChangedFileThatNowReferencesAttribute1() + Dim source = " +class C +end class + +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.First(), + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" + +class C +end class + +class XAttribute + inherits System.Attribute +end class +")))) + + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunWithChangedFileThatNowReferencesAttribute2() + Dim source1 = " +class C +end class +" + Dim source2 = " +class XAttribute + inherits System.Attribute +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation As Compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithMetadataName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttributeWithMetadataName")) + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.First(), + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" + +class C +end class +")))) + + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttributeWithMetadataName"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason), + Sub(s) Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("groupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps("compilationAndGroupedNodes_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps("result_ForAttributeWithMetadataName").Single().Outputs.Single().Reason) + End Sub + +#End Region + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb new file mode 100644 index 0000000000000..7572289006634 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb @@ -0,0 +1,1543 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests +Imports Roslyn.Test.Utilities.TestGenerators + +Namespace Microsoft.CodeAnalysis.VisualBasic.Semantic.UnitTests.SourceGeneration + Public Class GeneratorDriverTests_Attributes_SimpleName + Inherits BasicTestBase + + Private Function IsClassStatementWithName(value As Object, name As String) As Boolean + If TypeOf value IsNot ClassStatementSyntax Then + Return False + End If + + Return DirectCast(value, ClassStatementSyntax).Identifier.ValueText = name + End Function + +#Region "Non-Incremental tests" + + ' These tests just validate basic correctness of results in different scenarios, without actually validating + ' that the incremental nature of this provider works properly. + + + ")> + ")> + ")> + ")> + ")> + ")> + ")> + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration1(attribute As String) + Dim source = $" +{attribute} +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator( + Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + + + + + + + Public Sub FindFullAttributeNameOnTopLevelClass(attribute As String) + Dim source = $" +<{attribute}> +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForDelegateDeclaration1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of DelegateStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForDifferentName() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of DelegateStatementSyntax)("YAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForSyntaxNode1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of SyntaxNode)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration1() + Dim source = " + +class C +end class + +class D +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) + Assert.True(IsClassStatementWithName(_step.Outputs.First().Value, "C")) + Assert.True(IsClassStatementWithName(_step.Outputs.Last().Value, "D")) + End Sub) + End Sub + + + Public Sub FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration2() + Dim source = " + +class C +end class + +class D +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) + Assert.True(IsClassStatementWithName(_step.Outputs.First().Value, "C")) + Assert.False(_step.Outputs.Any(Function(o) IsClassStatementWithName(o.Value, "D"))) + End Sub) + End Sub + + + Public Sub FindAttributeOnTopLevelClasses_WhenSearchingForClassDeclaration3() + Dim source = " + +class C +end class + +class D +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) + Assert.False(_step.Outputs.Any(Function(o) IsClassStatementWithName(o.Value, "C"))) + Assert.True(IsClassStatementWithName(_step.Outputs.Last().Value, "D")) + End Sub) + End Sub + + + Public Sub FindAttributeOnNestedClasses_WhenSearchingForClassDeclaration1() + Dim source = " + +class C + + class D + end class +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) + Assert.True(IsClassStatementWithName(_step.Outputs.First().Value, "C")) + Assert.True(IsClassStatementWithName(_step.Outputs.Last().Value, "D")) + End Sub) + End Sub + + + Public Sub FindAttributeOnClassInNamespace_WhenSearchingForClassDeclaration1() + Dim source = " +namespace N + + class C + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.First().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_FullAttributeName1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ShortAttributeName1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("X") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindFullAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_FullAttributeName1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + + + + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias1(text As String) + Dim source = $" +imports {text} + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias3() + Dim source = " +imports AAttribute = XAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias1() + Dim source = " +imports AAttribute : X + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_WithLocalAlias2() + Dim source = " +imports AAttribute : XAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases1() + Dim source = " +imports B = XAttribute +namespace N + imports A = B + + + class C + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_OuterAliasReferencesInnerAlias() + ' note: this is not legal. + Dim source = " +imports BAttribute = AAttribute +namespace N + imports AAttribute = XAttribute + + + class C + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases2() + Dim source = " +imports B = XAttribute +namespace N + imports AAttribute = B + + + class C + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_ThroughMultipleAliases2() + Dim source = " +imports BAttribute = XAttribute +namespace N + imports AAttribute = B + + + class C + end class +end namespace +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias1() + Dim source = " +imports AAttribute = BAttribute +imports BAttribute = AAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias2() + Dim source = " +imports A = BAttribute +imports B = AAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_RecursiveAlias3() + Dim source = " +imports A = B +imports B = A + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_LocalAliasInDifferentFile1() + Dim source1 = " + +class C +end class +" + Dim source2 = " +imports A = XAttribute +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub DoNotFindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_LocalAliasInDifferentFile2() + Dim source1 = " + +class C +end class +" + Dim source2 = " +imports AAttribute = XAttribute +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasInSameFile1() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("A = XAttribute")), + parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasInSameFile2() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasInSameFile1() + Dim source = " +imports B = AAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasInSameFile2() + Dim source = " +imports BAttribute = AAttribute + + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasDifferentFile1() + Dim source1 = " + +class C +end class +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1}, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("A = XAttribute")), + parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasDifferentFile2() + Dim source1 = " + +class C +end class +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1}, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_BothGlobalAndLocalAliasDifferentFile1() + Dim source1 = " + +class C +end class +" + Dim source2 = " +global imports AAttribute = XAttribute +imports B = AAttribute +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAliasLoop1() + Dim source1 = " + +class C +end class +" + Dim source2 = " +global imports AAttribute = BAttribute +global imports BAttribute = AAttribute +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1, source2}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasDifferentFile1() + Dim source1 = " +imports B = AAttribute + +class C +end class +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1}, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_GlobalAndLocalAliasDifferentFile2() + Dim source1 = " +imports BAttribute = AAttribute + +class C +end class +" + + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source1}, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("AAttribute = XAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + +#End Region + +#Region "Incremental tests" + + ' These tests validate minimal recomputation performed after changes are made to the compilation. + + + Public Sub RerunOnSameCompilationCachesResultFully() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + ' re-run without changes + driver = driver.RunGenerators(compilation) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub RerunOnCompilationWithReferencesChangeCachesResultFully() + Dim source = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation(source, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Assert.Single(compilation.SyntaxTrees) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + ' re-run with just changes to references. this helper is entirely syntactic, so nothing should change. + driver = driver.RunGenerators(compilation.RemoveAllReferences()) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub TestSourceFileRemoved1() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + ' re-run with the file with the class removed. this will remove the actual output. + driver = driver.RunGenerators(compilation.RemoveSyntaxTrees(compilation.SyntaxTrees.Last())) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Removed, _step.Outputs.Single().Reason)) + + ' the per-file global aliases get changed (because the last file is removed). + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + ' however, the collected global aliases stays the same. + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub TestSourceFileChanged_AttributeRemoved1() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(" +class C +end class +")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub TestSourceFileChanged_AttributeAdded1() + Dim source3 = " +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(" + +class C +end class +")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub TestRemoveGlobalAttributeFile1() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.WithOptions(compilation.Options.WithGlobalImports( + GlobalImport.Parse("BAttribute = AAttribute")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub TestAddGlobalAttributeFile1() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports(GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + + driver = driver.RunGenerators( + compilation.WithOptions(compilation.Options.WithGlobalImports( + compilation.Options.GlobalImports.Add(GlobalImport.Parse("AAttribute = XAttribute"))))) + + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub TestAddGlobalAttributeFile2() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, options:=TestOptions.DebugDll, parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.False(runResult.TrackedSteps.ContainsKey("result_ForAttribute")) + + driver = driver.RunGenerators(compilation.WithOptions(compilation.Options.WithGlobalImports( + compilation.Options.GlobalImports.Add(GlobalImport.Parse("BAttribute = XAttribute"))))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + End Sub + + + Public Sub TestAddSourceFileWithoutAttribute() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" +class D +end class")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason), + Sub(_step) Assert.Equal(IncrementalStepRunReason.New, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + End Sub + + + Public Sub TestAddSourceFileWithAttribute() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.AddSyntaxTrees( + compilation.SyntaxTrees.First().WithChangedText(SourceText.From(" + +class D +end class")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason), + Sub(_step) Assert.Equal(IncrementalStepRunReason.New, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Collection(runResult.TrackedSteps("compilationUnit_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason), + Sub(_step) Assert.Equal(IncrementalStepRunReason.New, _step.Outputs.Single().Reason)) + Assert.Collection(runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Cached, _step.Outputs.Single().Reason), + Sub(_step) Assert.Equal(IncrementalStepRunReason.New, _step.Outputs.Single().Reason)) + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Cached, _step.Outputs.Single().Reason), + Sub(_step) Assert.Equal(IncrementalStepRunReason.New, _step.Outputs.Single().Reason)) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C")), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "D"))) + End Sub + + + Public Sub TestReplaceSourceFileWithDifferentAttribute() + Dim source3 = " + +class C +end class +" + Dim parseOptions = TestOptions.RegularLatest + Dim compilation = CreateCompilation({source3}, + options:=TestOptions.DebugDll.WithGlobalImports( + GlobalImport.Parse("AAttribute = XAttribute"), + GlobalImport.Parse("BAttribute = AAttribute")), parseOptions:=parseOptions) + + Dim generator = New IncrementalGeneratorWrapper(New PipelineCallbackGenerator(Sub(ctx) + Dim input = ctx.ForAttributeWithSimpleName(Of ClassStatementSyntax)("XAttribute") + ctx.RegisterSourceOutput(input, Sub(spc, node) + End Sub) + End Sub)) + + Dim driver As GeneratorDriver = VisualBasicGeneratorDriver.Create(ImmutableArray.Create(Of ISourceGenerator)(generator), parseOptions:=parseOptions, driverOptions:=New GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps:=True)) + driver = driver.RunGenerators(compilation) + Dim runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "C"))) + + driver = driver.RunGenerators(compilation.ReplaceSyntaxTree( + compilation.SyntaxTrees.Last(), + compilation.SyntaxTrees.Last().WithChangedText(SourceText.From(" + +class D +end class")))) + runResult = driver.GetRunResult().Results(0) + + Assert.Collection(runResult.TrackedSteps("individualFileGlobalAliases_ForAttribute"), + Sub(_step) Assert.Equal(IncrementalStepRunReason.Unchanged, _step.Outputs.Single().Reason)) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("collectedGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps("allUpGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnit_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("compilationUnitAndGlobalAliases_ForAttribute").Single().Outputs.Single().Reason) + Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps("result_ForAttribute").Single().Outputs.Single().Reason) + + Assert.Collection(runResult.TrackedSteps("result_ForAttribute"), + Sub(_step) Assert.True(IsClassStatementWithName(_step.Outputs.Single().Value, "D"))) + End Sub + +#End Region + + End Class +End Namespace diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index e1f19c559fe98..d9bb084a1bf28 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -270,9 +270,10 @@ public void CopyTo(T[] array, int start) } public T Last() - { - return _builder[_builder.Count - 1]; - } + => _builder[_builder.Count - 1]; + + internal T? LastOrDefault() + => Count == 0 ? default : Last(); public T First() { diff --git a/src/Tools/IdeCoreBenchmarks/IncrementalSourceGeneratorBenchmarks.cs b/src/Tools/IdeCoreBenchmarks/IncrementalSourceGeneratorBenchmarks.cs index f9ad032814390..9584cc3c0021a 100644 --- a/src/Tools/IdeCoreBenchmarks/IncrementalSourceGeneratorBenchmarks.cs +++ b/src/Tools/IdeCoreBenchmarks/IncrementalSourceGeneratorBenchmarks.cs @@ -133,7 +133,8 @@ public async Task RunGenerator() return node; }); #else - var input = ctx.ForAttributeWithMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute"); + // var input = ctx.ForAttributeWithMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute"); + // var input = ctx.ForAttributeWithSimpleName("JsonSerializableAttribute"); #endif ctx.RegisterSourceOutput(input, (spc, node) => { }); })).AsSourceGenerator(); @@ -160,7 +161,7 @@ public async Task RunGenerator() Console.WriteLine("Start profiling now"); var totalIncrementalTime = TimeSpan.Zero; - for (var i = 0; i < 10000; i++) + for (var i = 0; i < 1000; i++) { var changedText = sourceText.WithChanges(new TextChange(new TextSpan(0, 0), $"// added text{i}\r\n")); var changedTree = syntaxTree.WithChangedText(changedText);