/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs
755 строк
40 KB
Jared Parsons
Rework analyzer assembly loading (#77004)
24 мар 2025, 20:11
Не верифицирован
24 мар 2025, 20:11
28b8657
Код
Авторство
О чём код?
// 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.IO; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests { [CollectionDefinition(Name)] public class AssemblyLoadTestFixtureCollection : ICollectionFixture<AssemblyLoadTestFixture> { public const string Name = nameof(AssemblyLoadTestFixtureCollection); private AssemblyLoadTestFixtureCollection() { } } [Collection(AssemblyLoadTestFixtureCollection.Name)] public class AnalyzerFileReferenceTests : TestBase { private static readonly AnalyzerAssemblyLoader s_analyzerLoader = new AnalyzerAssemblyLoader(); private readonly AssemblyLoadTestFixture _testFixture; public AnalyzerFileReferenceTests(AssemblyLoadTestFixture testFixture) { _testFixture = testFixture; } public static AnalyzerFileReference CreateAnalyzerFileReference(string fullPath) { return new AnalyzerFileReference(fullPath, s_analyzerLoader); } [Fact] public void AnalyzerFileReference_Errors() { Assert.Throws<ArgumentNullException>("fullPath", () => new AnalyzerFileReference(null!, s_analyzerLoader)); Assert.Throws<ArgumentNullException>("assemblyLoader", () => new AnalyzerFileReference(TempRoot.Root, null!)); // path must be absolute Assert.Throws<ArgumentException>("fullPath", () => new AnalyzerFileReference("a.dll", s_analyzerLoader)); } [Fact] public void DisplayAndId_BadPath() { var loader = new ThrowingLoader(); var refBadPath = new AnalyzerFileReference(PathUtilities.CombinePathsUnchecked(TempRoot.Root, "\0<>|*.xyz"), loader); Assert.Equal("\0<>|*", refBadPath.Display); Assert.Equal("\0<>|*", refBadPath.Id); } [Fact] public void Equality() { var path1 = Path.Combine(TempRoot.Root, "dir"); var path2 = Path.Combine(TempRoot.Root, "dir", "..", "dir"); // Equals/GetHashCode should not load the analyzer var loader1 = new ThrowingLoader(); var loader2 = new ThrowingLoader(); var refA = new AnalyzerFileReference(path1, loader1); var refB = new AnalyzerFileReference(path1, loader1); Assert.False(refA.Equals(null)); Assert.True(refA.Equals(refA)); Assert.True(refA.Equals(refB)); Assert.Equal(refA.GetHashCode(), refB.GetHashCode()); // paths are compared for exact equality, it's up to the host to normalize them: Assert.False(refA.Equals(new AnalyzerFileReference(path2, loader1))); // different loader: Assert.False(refA.Equals(new AnalyzerFileReference(path1, loader2))); // legacy overload: Assert.True(refA.Equals((AnalyzerReference)refA)); Assert.False(refA.Equals((AnalyzerReference?)null)); Assert.True(refA!.Equals((AnalyzerReference)refB)); Assert.True(refA.Equals(new TestAnalyzerReference(path1))); Assert.False(refA.Equals(new TestAnalyzerReference(path2))); } [Fact] public void TestMetadataParse() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var analyzerTypeNameMap = reference.GetAnalyzerTypeNameMap(); Assert.Equal(2, analyzerTypeNameMap.Keys.Count()); Assert.Equal(6, analyzerTypeNameMap[LanguageNames.CSharp].Count); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AbstractAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.OpenGenericAnalyzer`1", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.DoesNotContain("Microsoft.CodeAnalysis.UnitTests.Test.NotAnAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp]); Assert.Equal(6, analyzerTypeNameMap[LanguageNames.VisualBasic].Count); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AbstractAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.OpenGenericAnalyzer`1", analyzerTypeNameMap[LanguageNames.VisualBasic]); Assert.DoesNotContain("Microsoft.CodeAnalysis.UnitTests.Test.NotAnAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic]); } [Fact] public void TestGetAnalyzersPerLanguage() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var analyzers = reference.GetAnalyzers(LanguageNames.CSharp); Assert.Equal(4, analyzers.Length); var analyzerNames = analyzers.Select(a => a.GetType().Name); Assert.Contains("TestAnalyzer", analyzerNames); Assert.Contains("TestAnalyzerCS", analyzerNames); Assert.Contains("TestAnalyzerCSVB", analyzerNames); Assert.Contains("NestedAnalyzer", analyzerNames); analyzers = reference.GetAnalyzers(LanguageNames.VisualBasic); analyzerNames = analyzers.Select(a => a.GetType().Name); Assert.Equal(4, analyzers.Length); Assert.Contains("TestAnalyzerVB", analyzerNames); Assert.Contains("TestAnalyzerCSVB", analyzerNames); Assert.Contains("TestAnalyzer", analyzerNames); Assert.Contains("NestedAnalyzer", analyzerNames); } [Fact] public void TestLoadErrors1() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(2, errors.Count); var failedTypes = errors.Where(e => e.ErrorCode == AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer).Select(e => e.TypeName); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AbstractAnalyzer", failedTypes); Assert.Contains("Microsoft.CodeAnalysis.UnitTests.OpenGenericAnalyzer`1", failedTypes); } [Fact] public void TestLoadErrors2() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Path.Combine(TempRoot.Root, "random.dll")); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(1, errors.Count); Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer, errors.First().ErrorCode); } [Fact] public void TestLoadErrors3() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.Alpha); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(0, errors.Count); } [Fact] [WorkItem(1029928, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1029928")] public void BadAnalyzerReference_DisplayName() { var directory = Temp.CreateDirectory(); var textFile = directory.CreateFile("Goo.txt").WriteAllText("I am the very model of a modern major general.").Path; AnalyzerFileReference reference = CreateAnalyzerFileReference(textFile); Assert.Equal(expected: "Goo", actual: reference.Display); } [Fact] public void ValidAnalyzerReference_DisplayName() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.Alpha); Assert.Equal(expected: "Alpha", actual: reference.Display); } [Fact] [WorkItem(2781, "https://github.com/dotnet/roslyn/issues/2781")] [WorkItem(2782, "https://github.com/dotnet/roslyn/issues/2782")] public void ValidAnalyzerReference_Id() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.Alpha); AssemblyIdentity.TryParseDisplayName("Alpha, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", out var expectedIdentity); Assert.Equal(expected: expectedIdentity, actual: reference.Id); } [Fact] [WorkItem(2781, "https://github.com/dotnet/roslyn/issues/2781")] [WorkItem(2782, "https://github.com/dotnet/roslyn/issues/2782")] public void BadAnalyzerReference_Id() { var directory = Temp.CreateDirectory(); var textFile = directory.CreateFile("Goo.txt").WriteAllText("I am the very model of a modern major general.").Path; AnalyzerFileReference reference = CreateAnalyzerFileReference(textFile); Assert.Equal(expected: "Goo", actual: reference.Id); } [Fact] [WorkItem(1032909, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1032909")] public void TestFailedLoadDoesntCauseNoAnalyzersWarning() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.FaultyAnalyzer); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(1, errors.Count); Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, errors.First().ErrorCode); } [Fact] [WorkItem(1032909, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1032909")] public void TestReferencingFakeCompiler() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.AnalyzerWithFakeCompilerDependency); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(1, errors.Count); var error = errors[0]; // failure is in the analyzer itself, i.e. abstract members on DiagnosticAnalyzer are not implemented. Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, error.ErrorCode); Assert.Equal("Analyzer", error.TypeName); } [Fact] [WorkItem(1032909, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1032909")] public void TestReferencingLaterFakeCompiler() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.AnalyzerWithLaterFakeCompilerDependency); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); EventHandler<AnalyzerLoadFailureEventArgs> errorHandler = (o, e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(); reference.AddAnalyzers(builder, LanguageNames.CSharp); var analyzers = builder.ToImmutable(); reference.AnalyzerLoadFailed -= errorHandler; Assert.Equal(1, errors.Count); var error = errors[0]; Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.ReferencesNewerCompiler, error.ErrorCode); Assert.Null(error.TypeName); } private class AnalyzerLoaderMockCSharpCompiler : CSharpCompiler { public AnalyzerLoaderMockCSharpCompiler(CSharpCommandLineParser parser, string? responseFile, string[] args, BuildPaths buildPaths, string? additionalReferenceDirectories, IAnalyzerAssemblyLoader assemblyLoader, GeneratorDriverCache? driverCache = null, ICommonCompilerFileSystem? fileSystem = null) : base(parser, responseFile, args, buildPaths, additionalReferenceDirectories, assemblyLoader, driverCache, fileSystem) { } } [ConditionalFact(typeof(IsEnglishLocal))] public void AssemblyLoading_ReferencesLaterFakeCompiler_EndToEnd_CSharp() { var directory = Temp.CreateDirectory(); TempFile corlib = directory.CreateFile("mscorlib.dll"); corlib.WriteAllBytes(TestResources.NetFX.Minimal.mincorlib); TempFile source = directory.CreateFile("in.cs"); source.WriteAllText("int x = 0;"); var compiler = new AnalyzerLoaderMockCSharpCompiler( CSharpCommandLineParser.Default, responseFile: null, args: new[] { "/nologo", $@"/analyzer:""{_testFixture.AnalyzerWithLaterFakeCompilerDependency}""", "/nostdlib", $@"/r:""{corlib}""", "/out:something.dll", source.Path }, new BuildPaths(clientDir: directory.Path, workingDir: directory.Path, sdkDir: null, tempDir: null), additionalReferenceDirectories: null, new AnalyzerAssemblyLoader()); var writer = new StringWriter(); var result = compiler.Run(writer); Assert.Equal(0, result); AssertEx.Equal($""" warning CS9057: Analyzer assembly '{_testFixture.AnalyzerWithLaterFakeCompilerDependency}' cannot be used because it references version '100.0.0.0' of the compiler, which is newer than the currently running version '42.42.42.42'. in.cs(1,5): warning CS0219: The variable 'x' is assigned but its value is never used """, writer.ToString()); } [ConditionalFact(typeof(IsEnglishLocal), AlwaysSkip = "https://github.com/dotnet/roslyn/issues/63856")] public void DuplicateAnalyzerReference() { var directory = Temp.CreateDirectory(); TempFile corlib = directory.CreateFile("mscorlib.dll"); corlib.WriteAllBytes(TestResources.NetFX.Minimal.mincorlib); TempFile source = directory.CreateFile("in.cs"); source.WriteAllText("int x = 0;"); var compiler = new AnalyzerLoaderMockCSharpCompiler( CSharpCommandLineParser.Default, responseFile: null, args: new[] { "/nologo", $@"/analyzer:""{_testFixture.AnalyzerWithFakeCompilerDependency}""", $@"/analyzer:""{_testFixture.AnalyzerWithFakeCompilerDependency}""", "/nostdlib", $@"/r:""{corlib}""", "/out:something.dll", source.Path }, new BuildPaths(clientDir: directory.Path, workingDir: directory.Path, sdkDir: null, tempDir: null), additionalReferenceDirectories: null, new AnalyzerAssemblyLoader()); var writer = new StringWriter(); var result = compiler.Run(writer); Assert.Equal(0, result); AssertEx.Equal($""" warning CS9067: Analyzer reference '{_testFixture.AnalyzerWithFakeCompilerDependency}' specified multiple times warning CS8032: An instance of analyzer Analyzer cannot be created from {_testFixture.AnalyzerWithFakeCompilerDependency} : Method 'get_SupportedDiagnostics' in type 'Analyzer' from assembly 'AnalyzerWithFakeCompilerDependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.. in.cs(1,5): warning CS0219: The variable 'x' is assigned but its value is never used """, writer.ToString()); } [ConditionalFact(typeof(CoreClrOnly), Reason = "Can't load a framework targeting generator, which these are in desktop")] public void TestLoadGenerators() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGeneratorsForAllLanguages(); var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", "Microsoft.CodeAnalysis.UnitTests.TestGenerator", "Microsoft.CodeAnalysis.UnitTests.BaseGenerator", "Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", "Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", "Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", "Microsoft.CodeAnalysis.UnitTests.FSharpGenerator", "Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", "Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator" }, typeNames); } [ConditionalFact(typeof(CoreClrOnly))] public void TestLoadGeneratorsWithoutArgumentOnlyLoadsCSharp() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGenerators(LanguageNames.CSharp); #pragma warning disable CS0618 // Type or member is obsolete var generators2 = reference.GetGenerators(); #pragma warning restore CS0618 // Type or member is obsolete Assert.Equal(generators, generators2); } [ConditionalFact(typeof(CoreClrOnly))] public void TestLoadCSharpGenerators() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGenerators(LanguageNames.CSharp); var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", "Microsoft.CodeAnalysis.UnitTests.TestGenerator", "Microsoft.CodeAnalysis.UnitTests.BaseGenerator", "Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", "Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", "Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", "Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator" }, typeNames); } [ConditionalFact(typeof(CoreClrOnly))] public void TestLoadVisualBasicGenerators() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGenerators(LanguageNames.VisualBasic); var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { "Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", "Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", "Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator" }, typeNames); } // can't load a coreclr targeting generator on net framework / mono [ConditionalFact(typeof(CoreClrOnly), AlwaysSkip = "https://github.com/dotnet/roslyn/issues/60762")] public void TestGeneratorsCantTargetNetFramework() { var directory = Temp.CreateDirectory(); // core var errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETCoreApp,Version=v5.0"); Assert.Empty(errors); // netstandard errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETStandard,Version=v2.0"); Assert.Empty(errors); // no target errors = buildAndLoadGeneratorAndReturnAnyErrors(targetFramework: null); Assert.Empty(errors); // framework errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETFramework,Version=v4.7.2"); Assert.Equal(2, errors.Count); Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.ReferencesFramework, errors.First().ErrorCode); List<AnalyzerLoadFailureEventArgs> buildAndLoadGeneratorAndReturnAnyErrors(string? targetFramework) { string targetFrameworkAttributeText = targetFramework is object ? $"[assembly: System.Runtime.Versioning.TargetFramework(\"{targetFramework}\")]" : string.Empty; string generatorSource = $@" using Microsoft.CodeAnalysis; {targetFrameworkAttributeText} [Generator] public class Generator : ISourceGenerator {{ public void Execute(GeneratorExecutionContext context) {{ }} public void Initialize(GeneratorInitializationContext context) {{ }} }}"; var directory = Temp.CreateDirectory(); var generatorPath = Path.Combine(directory.Path, $"generator_{targetFramework}.dll"); var compilation = CSharpCompilation.Create($"generator_{targetFramework}", new[] { CSharpTestSource.Parse(generatorSource) }, TargetFrameworkUtil.GetReferences(TargetFramework.Standard, new[] { MetadataReference.CreateFromAssemblyInternal(typeof(ISourceGenerator).Assembly) }), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); compilation.VerifyDiagnostics(); var result = compilation.Emit(generatorPath); Assert.True(result.Success); AnalyzerFileReference reference = CreateAnalyzerFileReference(generatorPath); List<AnalyzerLoadFailureEventArgs> errors = new List<AnalyzerLoadFailureEventArgs>(); void errorHandler(object? o, AnalyzerLoadFailureEventArgs e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder<ISourceGenerator>(); reference.AddGenerators(builder, LanguageNames.CSharp); reference.AnalyzerLoadFailed -= errorHandler; if (errors.Count > 0) { Assert.Empty(builder); } else { Assert.Single(builder); } return errors; } } [Fact] [WorkItem(52035, "https://github.com/dotnet/roslyn/issues/52035")] public void TestLoadedAnalyzerOrderIsDeterministic() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var csharpAnalyzers = reference.GetAnalyzers(LanguageNames.CSharp).Select(a => a.GetType().FullName).ToArray(); Assert.Equal(4, csharpAnalyzers.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", csharpAnalyzers[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", csharpAnalyzers[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", csharpAnalyzers[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", csharpAnalyzers[3]); var vbAnalyzers = reference.GetAnalyzers(LanguageNames.VisualBasic).Select(a => a.GetType().FullName).ToArray(); Assert.Equal(4, vbAnalyzers.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", vbAnalyzers[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", vbAnalyzers[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", vbAnalyzers[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", vbAnalyzers[3]); // analyzers return C#, then VB, including duplicates var allAnalyzers = reference.GetAnalyzersForAllLanguages().Select(a => a.GetType().FullName).ToArray(); Assert.Equal(8, allAnalyzers.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", allAnalyzers[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", allAnalyzers[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", allAnalyzers[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", allAnalyzers[3]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", allAnalyzers[4]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", allAnalyzers[5]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", allAnalyzers[6]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", allAnalyzers[7]); } [ConditionalFact(typeof(CoreClrOnly), Reason = "Can't load a framework targeting generator, which these are in desktop")] [WorkItem(52035, "https://github.com/dotnet/roslyn/issues/52035")] public void TestLoadedGeneratorOrderIsDeterministic() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var csharpGenerators = reference.GetGenerators(LanguageNames.CSharp).Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(10, csharpGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", csharpGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", csharpGenerators[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", csharpGenerators[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", csharpGenerators[3]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", csharpGenerators[4]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", csharpGenerators[5]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestGenerator", csharpGenerators[6]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator", csharpGenerators[7]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", csharpGenerators[8]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", csharpGenerators[9]); var vbGenerators = reference.GetGenerators(LanguageNames.VisualBasic).Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(5, vbGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", vbGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator", vbGenerators[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", vbGenerators[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", vbGenerators[3]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", vbGenerators[4]); // generators load in language order (C#, F#, VB), and *do not* include duplicates var allGenerators = reference.GetGeneratorsForAllLanguages().Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(12, allGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", allGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", allGenerators[1]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", allGenerators[2]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", allGenerators[3]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", allGenerators[4]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", allGenerators[5]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestGenerator", allGenerators[6]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator", allGenerators[7]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", allGenerators[8]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", allGenerators[9]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.FSharpGenerator", allGenerators[10]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", allGenerators[11]); } // NOTE: the order in which these are emitted can change the test 'TestLoadedAnalyzerOrderIsDeterministic' // and other determinism tests in this file. // Ensure you do not re-arrange them alphabetically, as that will invalidate the tests, without // explicitly failing them [DiagnosticAnalyzer(LanguageNames.CSharp, new string[] { LanguageNames.VisualBasic })] public class TestAnalyzer : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [DiagnosticAnalyzer(LanguageNames.CSharp)] public class TestAnalyzerCS : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [DiagnosticAnalyzer(LanguageNames.VisualBasic, new string[] { })] public class TestAnalyzerVB : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [Generator] public class TestGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } public class SomeType { [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class NestedAnalyzer : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [Generator] public class NestedGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } } } namespace Test { public class DiagnosticAnalyzer : Attribute { } [Test.DiagnosticAnalyzer] public class NotAnAnalyzer { } public class Generator : Attribute { } [Test.Generator] public class NotAGenerator { } } [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class TestAnalyzerCSVB : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } public class TestAnalyzerNone { } [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public abstract class AbstractAnalyzer : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class OpenGenericAnalyzer<T> : DiagnosticAnalyzer { public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } } [Generator] public class TestGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } public class TestGeneratorNoAttrib : ISourceGenerator { public void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } [Generator] public class BaseGenerator : ISourceGenerator { public virtual void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } [Generator] public class SubClassedGenerator : BaseGenerator { public override void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); } [Generator] public class NotAGenerator { } [Generator(LanguageNames.CSharp)] public class ExplicitCSharpOnlyGenerator : TestGenerator { } [Generator(LanguageNames.VisualBasic)] public class VisualBasicOnlyGenerator : TestGenerator { } [Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class CSharpAndVisualBasicGenerator : TestGenerator { } [Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class VisualBasicAndCSharpGenerator : TestGenerator { } [Generator(LanguageNames.FSharp)] public class FSharpGenerator : TestGenerator { } [Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class TestIncrementalGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); } public class TestIncrementalGeneratorWithNoAttrib : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); } [Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class TestSourceAndIncrementalGenerator : IIncrementalGenerator, ISourceGenerator { public void Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); public void Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } file sealed class ThrowingLoader : IAnalyzerAssemblyLoaderInternal { public void AddDependencyLocation(string fullPath) { } public bool IsHostAssembly(Assembly assembly) => false; public Assembly LoadFromPath(string fullPath) => throw new Exception(); public string? GetOriginalDependencyLocation(AssemblyName assembly) => throw new Exception(); public void Dispose() { } } }