diff --git a/Compilers.sln b/Compilers.sln index c27f64deec044..a193c9bfe3aa9 100644 --- a/Compilers.sln +++ b/Compilers.sln @@ -161,34 +161,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "vbc-arm64", "src\Compilers\VisualBasic\vbc\arm64\vbc-arm64.csproj", "{48C93F90-8776-4847-96D8-127B896D6C80}" EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 - src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 - src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 - src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 - src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 - src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 - src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 5 - src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{48c93f90-8776-4847-96d8-127b896d6c80}*SharedItemsImports = 5 - src\Compilers\CSharp\csc\CscCommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 5 - src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{54e08bf5-f819-404f-a18d-0ab9ea81ea04}*SharedItemsImports = 13 - src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 - src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 - src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 - src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 - src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 - src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 - src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{869e3b79-4e91-45fd-ba37-56dbd2f34721}*SharedItemsImports = 5 - src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 - src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5 - src\Compilers\CSharp\csc\CscCommandLine.projitems*{b5a27411-77ff-4c43-b0e3-fe09fba5f887}*SharedItemsImports = 5 - src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13 - src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 - src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 - src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 5 - src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 - src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{e919dd77-34f8-4f57-8058-4d3ff4c2b241}*SharedItemsImports = 13 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -516,4 +488,35 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6F599E08-A9EA-4FAA-897F-5D824B0210E6} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 + src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 + src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 + src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 + src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 + src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 + src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 5 + src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{48c93f90-8776-4847-96d8-127b896d6c80}*SharedItemsImports = 5 + src\Compilers\CSharp\csc\CscCommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 5 + src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{54e08bf5-f819-404f-a18d-0ab9ea81ea04}*SharedItemsImports = 13 + src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 + src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 + src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{810b02ad-2ea5-4422-88ac-b71b8ab0df0b}*SharedItemsImports = 13 + src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{869e3b79-4e91-45fd-ba37-56dbd2f34721}*SharedItemsImports = 5 + src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 + src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13 + src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5 + src\Compilers\CSharp\csc\CscCommandLine.projitems*{b5a27411-77ff-4c43-b0e3-fe09fba5f887}*SharedItemsImports = 5 + src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13 + src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 + src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 + src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{d8ef0777-9d65-4849-a7d6-ac81e58e2317}*SharedItemsImports = 13 + src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 5 + src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 + src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{e919dd77-34f8-4f57-8058-4d3ff4c2b241}*SharedItemsImports = 13 + EndGlobalSection EndGlobal diff --git a/Roslyn.sln b/Roslyn.sln index 61c8682f2503f..88af9dee76752 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -1512,6 +1512,7 @@ Global src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{76242a2d-2600-49dd-8c15-fea07ecb1843}*SharedItemsImports = 5 src\Analyzers\Core\Analyzers\Analyzers.projitems*{76e96966-4780-4040-8197-bde2879516f4}*SharedItemsImports = 13 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{7b7f4153-ae93-4908-b8f0-430871589f83}*SharedItemsImports = 13 + src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{810b02ad-2ea5-4422-88ac-b71b8ab0df0b}*SharedItemsImports = 13 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{94faf461-2e74-4dbb-9813-6b2cde6f1880}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{975cd834-45f4-4ea0-a395-cb60dbd0e214}*SharedItemsImports = 5 @@ -1527,6 +1528,7 @@ Global src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{ac2bcefb-9298-4621-ac48-1ff5e639e48d}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{ace53515-482c-4c6a-e2d2-4242a687dfee}*SharedItemsImports = 5 + src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bb3ca047-5d00-48d4-b7d3-233c1265c065}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 5 @@ -1534,6 +1536,7 @@ Global src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{cec0dce7-8d52-45c3-9295-fc7b16bd2451}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 + src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{d8ef0777-9d65-4849-a7d6-ac81e58e2317}*SharedItemsImports = 13 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{da973826-c985-4128-9948-0b445e638bdb}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{dc8c78cc-b6fe-47bf-93b1-b65a1c67c08d}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{e512c6c1-f085-4ad7-b0d9-e8f1a0a2a510}*SharedItemsImports = 5 diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTestBase.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTestBase.cs index d9788dd37f7aa..ca6ea2255bbd5 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTestBase.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTestBase.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -48,21 +46,21 @@ string getSdkDirectory(TempRoot temp) } } - internal CSharpCommandLineArguments DefaultParse(IEnumerable args, string baseDirectory, string sdkDirectory = null, string additionalReferenceDirectories = null) + internal CSharpCommandLineArguments DefaultParse(IEnumerable args, string baseDirectory, string? sdkDirectory = null, string? additionalReferenceDirectories = null) { sdkDirectory = sdkDirectory ?? SdkDirectory; return CSharpCommandLineParser.Default.Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories); } - internal MockCSharpCompiler CreateCSharpCompiler(string[] args, ImmutableArray analyzers = default, ImmutableArray generators = default, AnalyzerAssemblyLoader loader = null, GeneratorDriverCache driverCache = null) + internal MockCSharpCompiler CreateCSharpCompiler(string[] args, DiagnosticAnalyzer[]? analyzers = null, ISourceGenerator[]? generators = null, AnalyzerAssemblyLoader? loader = null, GeneratorDriverCache? driverCache = null) { return CreateCSharpCompiler(null, WorkingDirectory, args, analyzers, generators, loader, driverCache); } - internal MockCSharpCompiler CreateCSharpCompiler(string responseFile, string workingDirectory, string[] args, ImmutableArray analyzers = default, ImmutableArray generators = default, AnalyzerAssemblyLoader loader = null, GeneratorDriverCache driverCache = null) + internal MockCSharpCompiler CreateCSharpCompiler(string? responseFile, string workingDirectory, string[] args, DiagnosticAnalyzer[]? analyzers = null, ISourceGenerator[]? generators = null, AnalyzerAssemblyLoader? loader = null, GeneratorDriverCache? driverCache = null) { var buildPaths = RuntimeUtilities.CreateBuildPaths(workingDirectory, sdkDirectory: SdkDirectory); - return new MockCSharpCompiler(responseFile, buildPaths, args, analyzers, generators, loader, driverCache); + return new MockCSharpCompiler(responseFile, buildPaths, args, analyzers.AsImmutableOrEmpty(), generators.AsImmutableOrEmpty(), loader, driverCache); } } } diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index caa82ebcfbd60..d248537de7b58 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -34,6 +34,7 @@ using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; +using TestResources.Analyzers; using Xunit; using Basic.Reference.Assemblies; using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers; @@ -9192,12 +9193,19 @@ public void ReportAnalyzerOutput() var srcDirectory = Path.GetDirectoryName(srcFile.Path); var outWriter = new StringWriter(CultureInfo.InvariantCulture); - var csc = CreateCSharpCompiler(null, srcDirectory, new[] { "/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, srcFile.Path }); + var csc = CreateCSharpCompiler( + responseFile: null, + srcDirectory, + new[] { "/reportanalyzer", "/t:library", srcFile.Path }, + analyzers: new[] { new WarningDiagnosticAnalyzer() }, + generators: new[] { new DoNothingGenerator().AsSourceGenerator() }); var exitCode = csc.Run(outWriter); Assert.Equal(0, exitCode); var output = outWriter.ToString(); Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal); - Assert.Contains("WarningDiagnosticAnalyzer (Warning01)", output, StringComparison.Ordinal); + Assert.Contains($"{nameof(WarningDiagnosticAnalyzer)} (Warning01)", output, StringComparison.Ordinal); + Assert.Contains(CodeAnalysisResources.GeneratorNameColumnHeader, output, StringComparison.Ordinal); + Assert.Contains(typeof(DoNothingGenerator).FullName, output, StringComparison.Ordinal); CleanupAllGeneratedFiles(srcFile.Path); } @@ -9234,16 +9242,55 @@ public void SkipAnalyzersParse() Assert.False(parsedArgs.SkipAnalyzers); } - [Theory, CombinatorialData] + [Fact] + [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")] + public void SkipAnalyzersFlagFiltersAnalyzers() + { + var srcFile = Temp.CreateFile().WriteAllText(@"class C {}"); + var srcDirectory = Path.GetDirectoryName(srcFile.Path); + + var args = new List() { "/reportanalyzer", "/t:library", "/a:" + typeof(DoNothingGenerator).Assembly.Location, srcFile.Path }; + var csc = CreateCSharpCompiler( + responseFile: null, + srcDirectory, + args.ToArray()); + + csc.ResolveAnalyzersFromArguments( + skipAnalyzers: false, + out _, + out var analyzers, + out var generators); + Assert.NotEmpty(analyzers); + Assert.NotEmpty(generators); + + csc.ResolveAnalyzersFromArguments( + skipAnalyzers: true, + out _, + out analyzers, + out generators); + Assert.All(analyzers, static x => Assert.IsAssignableFrom(x)); + Assert.NotEmpty(generators); + + CleanupAllGeneratedFiles(srcFile.Path); + } + + [Theory] + [CombinatorialData] [WorkItem(40926, "https://github.com/dotnet/roslyn/issues/40926")] - public void SkipAnalyzersSemantics(bool skipAnalyzers) + public void NoAnalyzersReportSemantics(bool skipAnalyzers) { var srcFile = Temp.CreateFile().WriteAllText(@"class C {}"); var srcDirectory = Path.GetDirectoryName(srcFile.Path); var outWriter = new StringWriter(CultureInfo.InvariantCulture); - var skipAnalyzersFlag = "/skipanalyzers" + (skipAnalyzers ? "+" : "-"); - var csc = CreateCSharpCompiler(null, srcDirectory, new[] { skipAnalyzersFlag, "/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, srcFile.Path }); + var analyzers = skipAnalyzers + ? Array.Empty() + : new DiagnosticAnalyzer[] { new HiddenDiagnosticAnalyzer(), new WarningDiagnosticAnalyzer() }; + var csc = CreateCSharpCompiler( + responseFile: null, + srcDirectory, + new[] { "/reportanalyzer", "/t:library", srcFile.Path }, + analyzers: analyzers); var exitCode = csc.Run(outWriter); Assert.Equal(0, exitCode); var output = outWriter.ToString(); @@ -9275,7 +9322,7 @@ class C { } null, workingDirectory: Path.GetDirectoryName(srcFile.Path), args: new[] { "/errorlog:" + errorLog.Path, "/warnaserror+", "/nologo", "/t:library", srcFile.Path }, - analyzers: ImmutableArray.Create(new WarningDiagnosticAnalyzer())); + analyzers: new[] { new WarningDiagnosticAnalyzer() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = csc.Run(outWriter); @@ -9297,7 +9344,7 @@ public void AnalyzerDiagnosticThrowsInGetMessage() var outWriter = new StringWriter(CultureInfo.InvariantCulture); var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", srcFile.Path }, - analyzers: ImmutableArray.Create(new AnalyzerThatThrowsInGetMessage())); + analyzers: new[] { new AnalyzerThatThrowsInGetMessage() }); var exitCode = csc.Run(outWriter); Assert.Equal(0, exitCode); @@ -9322,7 +9369,7 @@ public void AnalyzerExceptionDiagnosticCanBeConfigured() var outWriter = new StringWriter(CultureInfo.InvariantCulture); var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", $"/warnaserror:{AnalyzerExecutor.AnalyzerExceptionDiagnosticId}", srcFile.Path }, - analyzers: ImmutableArray.Create(new AnalyzerThatThrowsInGetMessage())); + analyzers: new[] { new AnalyzerThatThrowsInGetMessage() }); var exitCode = csc.Run(outWriter); Assert.NotEqual(0, exitCode); @@ -9344,7 +9391,7 @@ public void AnalyzerReportsMisformattedDiagnostic() var outWriter = new StringWriter(CultureInfo.InvariantCulture); var csc = CreateCSharpCompiler(null, WorkingDirectory, new[] { "/t:library", srcFile.Path }, - analyzers: ImmutableArray.Create(new AnalyzerReportingMisformattedDiagnostic())); + analyzers: new[] { new AnalyzerReportingMisformattedDiagnostic() }); var exitCode = csc.Run(outWriter); Assert.Equal(0, exitCode); @@ -10221,9 +10268,9 @@ private string VerifyOutput(TempDirectory sourceDir, TempFile sourceFile, int? expectedExitCode = null, bool errorlog = false, bool skipAnalyzers = false, - IEnumerable generators = null, - GeneratorDriverCache driverCache = null, - params DiagnosticAnalyzer[] analyzers) + DiagnosticAnalyzer[] analyzers = null, + ISourceGenerator[] generators = null, + GeneratorDriverCache driverCache = null) { var args = new[] { "/nologo", "/preferreduilang:en", "/t:library", @@ -10249,7 +10296,7 @@ private string VerifyOutput(TempDirectory sourceDir, TempFile sourceFile, args = args.Append(additionalFlags); } - var csc = CreateCSharpCompiler(null, sourceDir.Path, args, analyzers: analyzers.ToImmutableArrayOrEmpty(), generators: generators.ToImmutableArrayOrEmpty(), driverCache: driverCache); + var csc = CreateCSharpCompiler(null, sourceDir.Path, args, analyzers: analyzers, generators: generators, driverCache: driverCache); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = csc.Run(outWriter); var output = outWriter.ToString(); @@ -12170,7 +12217,7 @@ long M(int i) additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false, errorlog: true, - analyzers: suppressor); + analyzers: new[] { suppressor }); Assert.DoesNotContain($"error CS0078", output, StringComparison.Ordinal); Assert.DoesNotContain($"warning CS0078", output, StringComparison.Ordinal); @@ -12222,7 +12269,7 @@ void M(int i) suppressor.SuppressionDescriptor.Justification); output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: suppressor, errorlog: true, skipAnalyzers: skipAnalyzers); + analyzers: new[] { suppressor }, errorlog: true, skipAnalyzers: skipAnalyzers); Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal); Assert.Contains($"info SP0001", output, StringComparison.Ordinal); Assert.Contains(suppressionMessage, output, StringComparison.Ordinal); @@ -12239,7 +12286,7 @@ void M(int i) includeCurrentAssemblyAsAnalyzerReference: false, errorlog: true, skipAnalyzers: skipAnalyzers, - analyzers: suppressor); + analyzers: new[] { suppressor }); Assert.DoesNotContain($"error CS1522", output, StringComparison.Ordinal); Assert.DoesNotContain($"warning CS1522", output, StringComparison.Ordinal); Assert.Contains("info SP0001", output, StringComparison.Ordinal); @@ -12278,7 +12325,7 @@ class C suppressor.SuppressionDescriptor.Justification); output = VerifyOutput(srcDirectory, srcFile, expectedInfoCount: 1, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: suppressor, errorlog: true, skipAnalyzers: skipAnalyzers); + analyzers: new[] { suppressor }, errorlog: true, skipAnalyzers: skipAnalyzers); Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal); Assert.Contains("info SP0001", output, StringComparison.Ordinal); Assert.Contains(suppressionMessage, output, StringComparison.Ordinal); @@ -12295,7 +12342,7 @@ class C includeCurrentAssemblyAsAnalyzerReference: false, errorlog: true, skipAnalyzers: skipAnalyzers, - analyzers: suppressor); + analyzers: new[] { suppressor }); Assert.DoesNotContain($"error CS0169", output, StringComparison.Ordinal); Assert.DoesNotContain($"warning CS0169", output, StringComparison.Ordinal); Assert.Contains("info SP0001", output, StringComparison.Ordinal); @@ -12321,7 +12368,7 @@ class { }"; // Verify that compiler syntax error CS1001 cannot be suppressed with diagnostic suppressor. output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: new DiagnosticSuppressorForId("CS1001")); + analyzers: new[] { new DiagnosticSuppressorForId("CS1001") }); Assert.Contains("error CS1001", output, StringComparison.Ordinal); CleanupAllGeneratedFiles(srcFile.Path); @@ -12347,7 +12394,7 @@ void M(UndefinedType x) { } // Verify that compiler error CS0246 cannot be suppressed with diagnostic suppressor. output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: new DiagnosticSuppressorForId("CS0246")); + analyzers: new[] { new DiagnosticSuppressorForId("CS0246") }); Assert.Contains("error CS0246", output, StringComparison.Ordinal); CleanupAllGeneratedFiles(srcFile.Path); @@ -12367,7 +12414,7 @@ class C { }"; var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true); var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: analyzer); + analyzers: new[] { analyzer }); Assert.Contains($"warning {analyzer.Descriptor.Id}", output, StringComparison.Ordinal); // Verify that analyzer warning is suppressed with diagnostic suppressor @@ -12394,7 +12441,7 @@ class C { }"; output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, additionalFlags: new[] { "/warnAsError" }, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: analyzer); + analyzers: new[] { analyzer }); Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal); // Verify that analyzer warning is suppressed with diagnostic suppressor even with /warnaserror @@ -12434,7 +12481,7 @@ class C { }"; var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Error, configurable: true); var output = VerifyOutput(srcDirectory, srcFile, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, - analyzers: analyzer); + analyzers: new[] { analyzer }); Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal); // Verify that analyzer error cannot be suppressed with diagnostic suppressor. @@ -12711,7 +12758,7 @@ private void TestBulkAnalyzerConfigurationCore( } var cmd = CreateCSharpCompiler(null, dir.Path, arguments, - analyzers: ImmutableArray.Create(analyzer)); + analyzers: new[] { analyzer }); Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths)); @@ -12952,7 +12999,7 @@ private void TestAnalyzerConfigRespectedCore(DiagnosticAnalyzer analyzer, Diagno } var cmd = CreateCSharpCompiler(null, dir.Path, arguments, - analyzers: ImmutableArray.Create(analyzer)); + analyzers: new[] { analyzer }); Assert.Equal(analyzerConfig.Path, Assert.Single(cmd.Arguments.AnalyzerConfigPaths)); @@ -13041,7 +13088,7 @@ public void TestAnalyzerFilteringBasedOnSeverity(DiagnosticSeverity defaultSever if (errorlog) args = args.Append("/errorlog:errorlog"); - var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: ImmutableArray.Create(analyzer)); + var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: new[] { analyzer }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); Assert.Equal(0, exitCode); @@ -13077,7 +13124,7 @@ public void TestWarnAsErrorMinusDoesNotEnableDisabledByDefaultAnalyzers(Diagnost // Verify '/warnaserror-:DiagnosticId' behavior. var args = new[] { "/warnaserror+", $"/warnaserror-:{diagnosticId}", "/nologo", "/t:library", "/preferreduilang:en", src.Path }; - var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: ImmutableArray.Create(analyzer)); + var cmd = CreateCSharpCompiler(null, dir.Path, args, analyzers: new[] { analyzer }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); var expectedExitCode = !analyzerShouldBeSkipped && defaultSeverity == DiagnosticSeverity.Error ? 1 : 0; @@ -13650,7 +13697,7 @@ class C src.Path }; - var cmd = CreateCSharpCompiler(null, dir.Path, args, generators: ImmutableArray.Create(generator)); + var cmd = CreateCSharpCompiler(null, dir.Path, args, generators: new[] { generator }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); Assert.Equal(0, exitCode); @@ -13767,7 +13814,7 @@ public ValueTuple(T1 item1) null, WorkingDirectory, new[] { "/nologo", "/t:library", srcFile.Path }, - analyzers: ImmutableArray.Create(new FieldAnalyzer())); // at least one analyzer required + analyzers: new[] { new FieldAnalyzer() }); // at least one analyzer required var exitCode = csc.Run(outWriter); Assert.Equal(0, exitCode); var output = outWriter.ToString(); @@ -14062,9 +14109,9 @@ class C dotnet_diagnostic.Warning01.severity = error; "); - VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + globalConfig.Path }, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new WarningDiagnosticAnalyzer()); + VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + globalConfig.Path }, expectedErrorCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new[] { new WarningDiagnosticAnalyzer() }); - VerifyOutput(dir, src, additionalFlags: new[] { "/nowarn:Warning01", "/analyzerconfig:" + globalConfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new WarningDiagnosticAnalyzer()); + VerifyOutput(dir, src, additionalFlags: new[] { "/nowarn:Warning01", "/analyzerconfig:" + globalConfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false, analyzers: new[] { new WarningDiagnosticAnalyzer() }); } [Theory, CombinatorialData] @@ -14085,7 +14132,7 @@ public void TestAdditionalFileAnalyzer(bool registerFromInitialize) var output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false, additionalFlags: new[] { "/additionalfile:" + additionalFile.Path }, - analyzers: analyzer); + analyzers: new[] { analyzer }); Assert.Contains("b.txt(1,3): warning ID0001", output, StringComparison.Ordinal); CleanupAllGeneratedFiles(srcDirectory.Path); diff --git a/src/Compilers/CSharp/Test/CommandLine/SarifErrorLoggerTests.cs b/src/Compilers/CSharp/Test/CommandLine/SarifErrorLoggerTests.cs index 44f07f5579c65..6429cc64d8660 100644 --- a/src/Compilers/CSharp/Test/CommandLine/SarifErrorLoggerTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/SarifErrorLoggerTests.cs @@ -140,7 +140,7 @@ public class C string[] arguments = new[] { "/nologo", "/t:library", $"/out:{outputFilePath}", sourceFile, "/preferreduilang:en", $"/errorlog:{errorLogFile}{ErrorLogQualifier}" }; var cmd = CreateCSharpCompiler(null, WorkingDirectory, arguments, - analyzers: ImmutableArray.Create(new AnalyzerForErrorLogTest())); + analyzers: new[] { new AnalyzerForErrorLogTest() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -176,7 +176,7 @@ class C string[] arguments = new[] { "/nologo", "/t:library", sourceFile, "/preferreduilang:en", $"/errorlog:{errorLogFile}{ErrorLogQualifier}" }; var cmd = CreateCSharpCompiler(null, WorkingDirectory, arguments, - analyzers: ImmutableArray.Create(new AnalyzerForErrorLogTest())); + analyzers: new[] { new AnalyzerForErrorLogTest() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); @@ -211,7 +211,7 @@ class C string[] arguments = new[] { "/nologo", "/t:library", sourceFile, "/preferreduilang:en", $"/errorlog:{errorLogFile}{ErrorLogQualifier}" }; var cmd = CreateCSharpCompiler(null, WorkingDirectory, arguments, - analyzers: ImmutableArray.Create(new AnalyzerForErrorLogTest())); + analyzers: new[] { new AnalyzerForErrorLogTest() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); @@ -246,7 +246,7 @@ class C string[] arguments = new[] { "/nologo", "/t:library", sourceFile, "/preferreduilang:en", $"/errorlog:{errorLogFile}{ErrorLogQualifier}" }; var cmd = CreateCSharpCompiler(null, WorkingDirectory, arguments, - analyzers: ImmutableArray.Create(new AnalyzerForErrorLogTest())); + analyzers: new[] { new AnalyzerForErrorLogTest() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); @@ -281,7 +281,7 @@ class C string[] arguments = new[] { "/nologo", "/t:library", sourceFile, "/preferreduilang:en", $"/errorlog:{errorLogFile}{ErrorLogQualifier}" }; var cmd = CreateCSharpCompiler(null, WorkingDirectory, arguments, - analyzers: ImmutableArray.Create(new AnalyzerForErrorLogTest())); + analyzers: new[] { new AnalyzerForErrorLogTest() }); var outWriter = new StringWriter(CultureInfo.InvariantCulture); var exitCode = cmd.Run(outWriter); diff --git a/src/Compilers/CSharp/csc/CscCommandLine.shproj b/src/Compilers/CSharp/csc/CscCommandLine.shproj index 2bc914979c147..6b03b1d859f7a 100644 --- a/src/Compilers/CSharp/csc/CscCommandLine.shproj +++ b/src/Compilers/CSharp/csc/CscCommandLine.shproj @@ -11,6 +11,7 @@ Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props')" /> + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index c8c5d135b2883..a1a6cfab43f4d 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -728,4 +728,10 @@ Edit and Continue can't resume suspended iterator since the corresponding yield return statement has been deleted + + Generator + + + Total generator execution time: {0} seconds. + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 8baa2e86d268d..772ef2fef629e 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -730,15 +730,23 @@ public virtual int Run(TextWriter consoleOutput, CancellationToken cancellationT /// The compilation before any source generation has occurred. /// The to use when parsing any generated sources. /// The generators to run - /// A provider that returns analyzer config options + /// A provider that returns analyzer config options. /// Any additional texts that should be passed to the generators when run. - /// Any diagnostics that were produced during generation + /// Any diagnostics that were produced during generation. /// A compilation that represents the original compilation with any additional, generated texts added to it. - private protected Compilation RunGenerators(Compilation input, ParseOptions parseOptions, ImmutableArray generators, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, ImmutableArray additionalTexts, DiagnosticBag generatorDiagnostics) + private protected (Compilation Compilation, GeneratorDriverTimingInfo DriverTimingInfo) RunGenerators( + Compilation input, + ParseOptions parseOptions, + ImmutableArray generators, + AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, + ImmutableArray additionalTexts, + DiagnosticBag generatorDiagnostics) { GeneratorDriver? driver = null; string cacheKey = string.Empty; - bool disableCache = !Arguments.ParseOptions.Features.ContainsKey("enable-generator-cache") || string.IsNullOrWhiteSpace(Arguments.OutputFileName); + bool disableCache = + !Arguments.ParseOptions.Features.ContainsKey("enable-generator-cache") || + string.IsNullOrWhiteSpace(Arguments.OutputFileName); if (this.GeneratorDriverCache is object && !disableCache) { cacheKey = deriveCacheKey(); @@ -756,7 +764,8 @@ private protected Compilation RunGenerators(Compilation input, ParseOptions pars { this.GeneratorDriverCache?.CacheGenerator(cacheKey, driver); } - return compilationOut; + + return (compilationOut, driver.GetTimingInfo()); string deriveCacheKey() { @@ -878,8 +887,8 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger? errorLogger, Cancella diagnostics, cancellationToken, out CancellationTokenSource? analyzerCts, - out bool reportAnalyzer, - out var analyzerDriver); + out var analyzerDriver, + out var driverTimingInfo); // At this point analyzers are already complete in which case this is a no-op. Or they are // still running because the compilation failed before all of the compilation events were @@ -905,10 +914,9 @@ private int RunCore(TextWriter consoleOutput, ErrorLogger? errorLogger, Cancella } diagnostics.Free(); - if (reportAnalyzer) + if (Arguments.ReportAnalyzer) { - Debug.Assert(analyzerDriver is object); - ReportAnalyzerExecutionTime(consoleOutput, analyzerDriver, Culture, compilation.Options.ConcurrentBuild); + ReportAnalyzerUtil.Report(consoleOutput, analyzerDriver, driverTimingInfo, Culture, compilation.Options.ConcurrentBuild); } return exitCode; @@ -972,12 +980,12 @@ private void CompileAndEmit( DiagnosticBag diagnostics, CancellationToken cancellationToken, out CancellationTokenSource? analyzerCts, - out bool reportAnalyzer, - out AnalyzerDriver? analyzerDriver) + out AnalyzerDriver? analyzerDriver, + out GeneratorDriverTimingInfo? generatorTimingInfo) { analyzerCts = null; - reportAnalyzer = false; analyzerDriver = null; + generatorTimingInfo = null; // Print the diagnostics produced during the parsing stage and exit if there were any errors. compilation.GetDiagnostics(CompilationStage.Parse, includeEarlierStages: false, diagnostics, cancellationToken); @@ -1017,13 +1025,11 @@ private void CompileAndEmit( { // At this point we have a compilation with nothing yet computed. // We pass it to the generators, which will realize any symbols they require. - compilation = RunGenerators(compilation, Arguments.ParseOptions, generators, analyzerConfigProvider, additionalTextFiles, diagnostics); + (compilation, generatorTimingInfo) = RunGenerators(compilation, Arguments.ParseOptions, generators, analyzerConfigProvider, additionalTextFiles, diagnostics); bool hasAnalyzerConfigs = !Arguments.AnalyzerConfigPaths.IsEmpty; bool hasGeneratedOutputPath = !string.IsNullOrWhiteSpace(Arguments.GeneratedFilesOutputDirectory); - var generatedSyntaxTrees = compilation.SyntaxTrees.Skip(Arguments.SourceFiles.Length).ToList(); - var analyzerOptionsBuilder = hasAnalyzerConfigs ? ArrayBuilder.GetInstance(generatedSyntaxTrees.Count) : null; var embeddedTextBuilder = ArrayBuilder.GetInstance(generatedSyntaxTrees.Count); try @@ -1106,7 +1112,6 @@ private void CompileAndEmit( severityFilter, out compilation, analyzerCts.Token); - reportAnalyzer = Arguments.ReportAnalyzer && !analyzers.IsEmpty; } } @@ -1461,72 +1466,6 @@ protected virtual ImmutableArray ResolveAdditionalFilesFromA return builder.ToImmutableAndFree(); } - private static void ReportAnalyzerExecutionTime(TextWriter consoleOutput, AnalyzerDriver analyzerDriver, CultureInfo culture, bool isConcurrentBuild) - { - Debug.Assert(analyzerDriver.AnalyzerExecutionTimes != null); - if (analyzerDriver.AnalyzerExecutionTimes.IsEmpty) - { - return; - } - - var totalAnalyzerExecutionTime = analyzerDriver.AnalyzerExecutionTimes.Sum(kvp => kvp.Value.TotalSeconds); - Func getFormattedTime = d => d.ToString("##0.000", culture); - consoleOutput.WriteLine(); - consoleOutput.WriteLine(string.Format(CodeAnalysisResources.AnalyzerTotalExecutionTime, getFormattedTime(totalAnalyzerExecutionTime))); - - if (isConcurrentBuild) - { - consoleOutput.WriteLine(CodeAnalysisResources.MultithreadedAnalyzerExecutionNote); - } - - var analyzersByAssembly = analyzerDriver.AnalyzerExecutionTimes - .GroupBy(kvp => kvp.Key.GetType().GetTypeInfo().Assembly) - .OrderByDescending(kvp => kvp.Sum(entry => entry.Value.Ticks)); - - consoleOutput.WriteLine(); - - getFormattedTime = d => d < 0.001 ? - string.Format(culture, "{0,8:<0.000}", 0.001) : - string.Format(culture, "{0,8:##0.000}", d); - Func getFormattedPercentage = i => string.Format("{0,5}", i < 1 ? "<1" : i.ToString()); - Func getFormattedAnalyzerName = s => " " + s; - - // Table header - var analyzerTimeColumn = string.Format("{0,8}", CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader); - var analyzerPercentageColumn = string.Format("{0,5}", "%"); - var analyzerNameColumn = getFormattedAnalyzerName(CodeAnalysisResources.AnalyzerNameColumnHeader); - consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); - - // Table rows grouped by assembly. - foreach (var analyzerGroup in analyzersByAssembly) - { - var executionTime = analyzerGroup.Sum(kvp => kvp.Value.TotalSeconds); - var percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); - - analyzerTimeColumn = getFormattedTime(executionTime); - analyzerPercentageColumn = getFormattedPercentage(percentage); - analyzerNameColumn = getFormattedAnalyzerName(analyzerGroup.Key.FullName); - - consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); - - // Rows for each diagnostic analyzer in the assembly. - foreach (var kvp in analyzerGroup.OrderByDescending(kvp => kvp.Value)) - { - executionTime = kvp.Value.TotalSeconds; - percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); - - analyzerTimeColumn = getFormattedTime(executionTime); - analyzerPercentageColumn = getFormattedPercentage(percentage); - var analyzerIds = string.Join(", ", kvp.Key.SupportedDiagnostics.Select(d => d.Id).Distinct().OrderBy(id => id)); - analyzerNameColumn = getFormattedAnalyzerName($" {kvp.Key} ({analyzerIds})"); - - consoleOutput.WriteLine(analyzerTimeColumn + analyzerPercentageColumn + analyzerNameColumn); - } - - consoleOutput.WriteLine(); - } - } - /// /// Returns the name with which the assembly should be output /// diff --git a/src/Compilers/Core/Portable/CommandLine/ReportAnalyzerUtil.cs b/src/Compilers/Core/Portable/CommandLine/ReportAnalyzerUtil.cs new file mode 100644 index 0000000000000..bccdbf2d24980 --- /dev/null +++ b/src/Compilers/Core/Portable/CommandLine/ReportAnalyzerUtil.cs @@ -0,0 +1,135 @@ +// 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; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis +{ + internal static class ReportAnalyzerUtil + { + public static void Report( + TextWriter consoleOutput, + AnalyzerDriver? analyzerDriver, + GeneratorDriverTimingInfo? driverTimingInfo, + CultureInfo culture, + bool isConcurrentBuild) + { + if (isConcurrentBuild && (analyzerDriver is { } || driverTimingInfo is { })) + { + consoleOutput.WriteLine(CodeAnalysisResources.MultithreadedAnalyzerExecutionNote); + consoleOutput.WriteLine(); + } + + if (analyzerDriver is { }) + { + ReportAnalyzerExecutionTime(consoleOutput, analyzerDriver, culture); + } + + if (driverTimingInfo is { } info) + { + ReportGeneratorExecutionTime(consoleOutput, info, culture); + } + } + + private static string GetFormattedTime(double d, CultureInfo culture) => d < 0.001 ? + string.Format(culture, "{0,8:<0.000}", 0.001) : + string.Format(culture, "{0,8:##0.000}", d); + + private static string GetColumnHeader(string kind) + { + var time = string.Format("{0,8}", CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader); + var percent = string.Format("{0,5}", "%"); + return time + percent + " " + kind; + } + + private static string GetColumnEntry(double totalSeconds, int percentage, string? name, CultureInfo culture) + { + var time = GetFormattedTime(totalSeconds, culture); + var percent = string.Format("{0,5}", percentage < 1 ? "<1" : percentage.ToString(culture)); + + return time + percent + " " + name; + } + + private static void ReportAnalyzerExecutionTime(TextWriter consoleOutput, AnalyzerDriver analyzerDriver, CultureInfo culture) + { + Debug.Assert(analyzerDriver.AnalyzerExecutionTimes != null); + if (analyzerDriver.AnalyzerExecutionTimes.IsEmpty) + { + return; + } + + var totalAnalyzerExecutionTime = analyzerDriver.AnalyzerExecutionTimes.Sum(kvp => kvp.Value.TotalSeconds); + consoleOutput.WriteLine(string.Format(CodeAnalysisResources.AnalyzerTotalExecutionTime, totalAnalyzerExecutionTime.ToString("##0.000", culture))); + consoleOutput.WriteLine(); + + // Table header + consoleOutput.WriteLine(GetColumnHeader(CodeAnalysisResources.AnalyzerNameColumnHeader)); + + // Table rows grouped by assembly. + var analyzersByAssembly = analyzerDriver.AnalyzerExecutionTimes + .GroupBy(kvp => kvp.Key.GetType().Assembly) + .OrderByDescending(kvp => kvp.Sum(entry => entry.Value.Ticks)); + foreach (var analyzerGroup in analyzersByAssembly) + { + var executionTime = analyzerGroup.Sum(kvp => kvp.Value.TotalSeconds); + var percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); + consoleOutput.WriteLine(GetColumnEntry(executionTime, percentage, analyzerGroup.Key.FullName, culture)); + + // Rows for each diagnostic analyzer in the assembly. + foreach (var kvp in analyzerGroup.OrderByDescending(kvp => kvp.Value)) + { + executionTime = kvp.Value.TotalSeconds; + percentage = (int)(executionTime * 100 / totalAnalyzerExecutionTime); + + var analyzerIds = string.Join(", ", kvp.Key.SupportedDiagnostics.Select(d => d.Id).Distinct().OrderBy(id => id)); + var analyzerNameColumn = $" {kvp.Key} ({analyzerIds})"; + consoleOutput.WriteLine(GetColumnEntry(executionTime, percentage, analyzerNameColumn, culture)); + } + + consoleOutput.WriteLine(); + } + } + + private static void ReportGeneratorExecutionTime(TextWriter consoleOutput, GeneratorDriverTimingInfo driverTimingInfo, CultureInfo culture) + { + if (driverTimingInfo.GeneratorTimes.IsEmpty) + { + return; + } + + var totalTime = driverTimingInfo.ElapsedTime.TotalSeconds; + consoleOutput.WriteLine(string.Format(CodeAnalysisResources.GeneratorTotalExecutionTime, totalTime.ToString("##0.000", culture))); + consoleOutput.WriteLine(); + + // Table header + consoleOutput.WriteLine(GetColumnHeader(CodeAnalysisResources.GeneratorNameColumnHeader)); + + // Table rows grouped by assembly. + var generatorsByAssembly = driverTimingInfo.GeneratorTimes + .GroupBy(t => t.Generator.GetGeneratorType().Assembly) + .OrderByDescending(kvp => kvp.Sum(entry => entry.ElapsedTime.Ticks)); + + foreach (var generatorGroup in generatorsByAssembly) + { + var executionTime = generatorGroup.Sum(x => x.ElapsedTime.TotalSeconds); + var percentage = (int)(executionTime * 100 / totalTime); + consoleOutput.WriteLine(GetColumnEntry(executionTime, percentage, generatorGroup.Key.FullName, culture)); + + foreach (var timingInfo in generatorGroup.OrderByDescending(x => x.ElapsedTime)) + { + executionTime = timingInfo.ElapsedTime.TotalSeconds; + percentage = (int)(executionTime * 100 / totalTime); + consoleOutput.WriteLine(GetColumnEntry(executionTime, percentage, " " + timingInfo.Generator.GetGeneratorType().FullName, culture)); + } + } + } + } +} diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf index d5ddd74aeaa77..1595df5f6198c 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf @@ -49,6 +49,16 @@ Funkce Upravit a pokračovat nemůže obnovit pozastavený iterátor, protože odpovídající příkaz yield return byl odstraněn. + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. {0} hintName obsahuje neplatný znak {1} na pozici {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf index 0d1b952dba1ae..02a6c35b72c4f 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf @@ -49,6 +49,16 @@ "Bearbeiten und Fortfahren" kann den angehaltenen Iterator nicht fortsetzen, da die entsprechende yield return-Anweisung gelöscht wurde + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. Der hintName "{0}" enthält ein ungültiges Zeichen "{1}" an Position {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf index 3313141ef1385..1fa2ec04a7df6 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf @@ -49,6 +49,16 @@ Editar y continuar no puede reanudar el iterador suspendido porque se ha eliminado la instrucción yield return correspondiente + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. El elemento hintName "{0}" contiene un carácter no válido "{1}" en la posición {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf index dc71f8b68b575..6e0eb2c31e51d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf @@ -49,6 +49,16 @@ Modifier et continuer ne peut pas reprendre l’itérateur suspendu, car l’instruction yield return correspondante a été supprimée + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. Le hintName « {0} » contient un caractère non valide « {1} » à la position {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf index a066d93fa3b0a..0fa79eca3cf7e 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf @@ -49,6 +49,16 @@ Modifica e Continua non sono in grado di riprendere l'enumeratore sospeso perché l'istruzione yield return corrispondente è stata eliminata + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. L'elemento hintName '{0}' contiene un carattere non valido '{1}' alla posizione {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf index f85497a3fce75..f8c1d8e7002bf 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf @@ -49,6 +49,16 @@ 対応する yield return ステートメントが削除されているため、エディット コンティニュは中断された反復子を再開できません + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. hintName {0} の位置 {2} に無効な文字 '{1}' が含まれています。 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf index 60e4aa566cdf7..b35ac8df5b641 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf @@ -49,6 +49,16 @@ 해당 yield return 문이 삭제되었으므로 편집하고 계속하기가 일시 중단된 반복기를 다시 시작할 수 없습니다. + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. hintName의 {0} 위치 {2}에 잘못된 문자 '{1}'이(가) 있습니다. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf index 58617372883b4..d47b9d2f2daf3 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf @@ -49,6 +49,16 @@ Funkcja Edytuj i kontynuuj nie może wznowić wstrzymanego iteratora, ponieważ odpowiadająca jej instrukcja yield return została usunięta + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. Element hintName „{0}” zawiera nieprawidłowy znak „{1}” na pozycji {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf index 47e06314e8d34..793b0ee7284ca 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf @@ -49,6 +49,16 @@ Editar e Continuar não pode retomar o iterador suspenso já que a instrução yield return correspondente foi excluída + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. O hintName '{0}' contém um caractere inválido '{1}' na posição {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf index 7ab722c73869b..b7b60816a0fa3 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf @@ -49,6 +49,16 @@ Операция "Изменить и продолжить" не может возобновить приостановленный итератор, поскольку соответствующий оператор yield return удален + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. hintName "{0}" содержит недопустимый символ "{1}" в позиции {2}. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf index dc07e7b89bc62..f0f038f115f12 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf @@ -49,6 +49,16 @@ İlgili yield return deyimi silindiğinden Düzenle ve Devam Et, askıya alınmış yineleyiciyi sürdüremiyor + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. hintName '{0}', {2} konumunda geçersiz bir '{1}' karakteri içeriyor. diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf index 33d656da37269..d0930b72a274e 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf @@ -49,6 +49,16 @@ 编辑并继续无法恢复挂起的迭代器,因为相应的 yield return 语句已被删除 + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. hintName“{0}”在位置 {2} 处包含无效字符“{1}”。 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf index 7ae5c4ae799dc..a3b308e31afaa 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf @@ -49,6 +49,16 @@ 編輯和繼續無法繼續暫停的列舉程式,因為對應的 yield return 陳述式已刪除 + + Generator + Generator + + + + Total generator execution time: {0} seconds. + Total generator execution time: {0} seconds. + + The hintName '{0}' contains an invalid character '{1}' at position {2}. 位置 {2} 的 hintName {0} 包含無效的字元 '{1}'。 diff --git a/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.shproj b/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.shproj index dfd128d203cfa..0ba8351d4499b 100644 --- a/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.shproj +++ b/src/Compilers/Server/VBCSCompiler/VBCSCompilerCommandLine.shproj @@ -11,6 +11,7 @@ Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props')" /> + \ No newline at end of file diff --git a/src/Compilers/Test/Resources/Core/Analyzers/DoNothingAnalyzer.cs b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingAnalyzer.cs new file mode 100644 index 0000000000000..811b1d4afb201 --- /dev/null +++ b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingAnalyzer.cs @@ -0,0 +1,19 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace TestResources.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class DoNothingAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Empty; + + public override void Initialize(AnalysisContext context) + { + } +} diff --git a/src/Compilers/Test/Resources/Core/Analyzers/DoNothingGenerator.cs b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingGenerator.cs new file mode 100644 index 0000000000000..01438b14246ac --- /dev/null +++ b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingGenerator.cs @@ -0,0 +1,16 @@ +// 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; + +namespace TestResources.Analyzers; + +[Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class DoNothingGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + + } +} diff --git a/src/Compilers/Test/Resources/Core/Analyzers/DoNothingSuppressor.cs b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingSuppressor.cs new file mode 100644 index 0000000000000..eb09d12c8d2e3 --- /dev/null +++ b/src/Compilers/Test/Resources/Core/Analyzers/DoNothingSuppressor.cs @@ -0,0 +1,19 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace TestResources.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class DoNothingSuppressor : DiagnosticSuppressor +{ + public override ImmutableArray SupportedSuppressions => ImmutableArray.Empty; + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + } +} diff --git a/src/Compilers/Test/Resources/Core/Analyzers/README.md b/src/Compilers/Test/Resources/Core/Analyzers/README.md new file mode 100644 index 0000000000000..575fbd77f5356 --- /dev/null +++ b/src/Compilers/Test/Resources/Core/Analyzers/README.md @@ -0,0 +1,6 @@ +Analyzers / Generators +=== + +Many of our unit tests need to define and consume custom analyzers and generators. This is a problem because most of our unit test assemblies multi-target between `net472` and the latest .NET Core. The compiler pushes customers to define their analyzers / generators against `netstandard2.0`. For generators the compiler actually issues a diagnostic when targeting `net472` and eventually this will be an error. + +Analyzers and Generators that are defined for testing purposes therefore should be defined in this project. It is the only project in our test set that actually targets `netstandard2.0` hence the only place we can safely define analyzers / generators going forward. diff --git a/src/Compilers/Test/Resources/Core/Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj b/src/Compilers/Test/Resources/Core/Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj index 24da3b2a86e2f..28bb8dc578b26 100644 --- a/src/Compilers/Test/Resources/Core/Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj +++ b/src/Compilers/Test/Resources/Core/Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj @@ -374,6 +374,10 @@ + + + + diff --git a/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs b/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs index be90ad0ef1d3b..059b14b1b08b3 100644 --- a/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs +++ b/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs @@ -53,6 +53,16 @@ protected override void ResolveAnalyzersFromArguments( } } + public void ResolveAnalyzersFromArguments( + bool skipAnalyzers, + out List diagnostics, + out ImmutableArray analyzers, + out ImmutableArray generators) + { + diagnostics = new List(); + ResolveAnalyzersFromArguments(diagnostics, this.MessageProvider, skipAnalyzers, out analyzers, out generators); + } + public Compilation CreateCompilation( TextWriter consoleOutput, TouchedFileLogger touchedFilesLogger, diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb b/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb index 79d7450e56a55..e15a4b3f084e6 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb @@ -12,6 +12,7 @@ Friend Class MockVisualBasicCompiler Inherits VisualBasicCompiler Private ReadOnly _analyzers As ImmutableArray(Of DiagnosticAnalyzer) + Private ReadOnly _generators As ImmutableArray(Of ISourceGenerator) Public Compilation As Compilation Public AnalyzerOptions As AnalyzerOptions @@ -19,22 +20,23 @@ Friend Class MockVisualBasicCompiler MyClass.New(Nothing, baseDirectory, args, analyzer) End Sub - Public Sub New(responseFile As String, baseDirectory As String, args As String(), Optional analyzer As DiagnosticAnalyzer = Nothing) + Public Sub New(responseFile As String, baseDirectory As String, args As String(), analyzer As DiagnosticAnalyzer) MyClass.New(responseFile, CreateBuildPaths(baseDirectory, Path.GetTempPath()), args, analyzer) End Sub - Public Sub New(responseFile As String, buildPaths As BuildPaths, args As String(), Optional analyzer As DiagnosticAnalyzer = Nothing) - MyClass.New(responseFile, buildPaths, args, If(analyzer Is Nothing, ImmutableArray(Of DiagnosticAnalyzer).Empty, ImmutableArray.Create(analyzer))) + Public Sub New(responseFile As String, buildPaths As BuildPaths, args As String(), analyzer As DiagnosticAnalyzer) + MyClass.New(responseFile, buildPaths, args, If(analyzer Is Nothing, Nothing, {analyzer})) End Sub - Public Sub New(responseFile As String, workingDirectory As String, args As String(), analyzers As ImmutableArray(Of DiagnosticAnalyzer)) - MyClass.New(responseFile, CreateBuildPaths(workingDirectory, Path.GetTempPath()), args, analyzers) + Public Sub New(responseFile As String, workingDirectory As String, args As String(), Optional analyzers As DiagnosticAnalyzer() = Nothing, Optional generators As ISourceGenerator() = Nothing) + MyClass.New(responseFile, CreateBuildPaths(workingDirectory, Path.GetTempPath()), args, analyzers, generators) End Sub - Public Sub New(responseFile As String, buildPaths As BuildPaths, args As String(), analyzers As ImmutableArray(Of DiagnosticAnalyzer)) + Public Sub New(responseFile As String, buildPaths As BuildPaths, args As String(), Optional analyzers As DiagnosticAnalyzer() = Nothing, Optional generators As ISourceGenerator() = Nothing) MyBase.New(VisualBasicCommandLineParser.Default, responseFile, args, buildPaths, Environment.GetEnvironmentVariable("LIB"), New DefaultAnalyzerAssemblyLoader()) - _analyzers = analyzers + _analyzers = analyzers.AsImmutableOrEmpty() + _generators = generators.AsImmutableOrEmpty() End Sub Private Shared Function CreateBuildPaths(workingDirectory As String, tempDirectory As String) As BuildPaths @@ -52,6 +54,10 @@ Friend Class MockVisualBasicCompiler If Not _analyzers.IsDefaultOrEmpty Then analyzers = analyzers.InsertRange(0, _analyzers) End If + + If Not _generators.IsDefaultOrEmpty Then + generators = generators.InsertRange(0, _generators) + End If End Sub Public Overloads Function CreateCompilation(consoleOutput As TextWriter, touchedFilesLogger As TouchedFileLogger, errorLogger As ErrorLogger, syntaxTreeDiagnosticOptionsOpt As ImmutableArray(Of AnalyzerConfigOptionsResult)) As Compilation diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index c5b50b6ef4fc1..6b21bf94250fc 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -27,6 +27,7 @@ Imports Roslyn.Test.PdbUtilities Imports Roslyn.Test.Utilities Imports Roslyn.Test.Utilities.SharedResourceHelpers Imports Roslyn.Utilities +Imports TestResources.Analyzers Imports Xunit Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests @@ -7822,7 +7823,7 @@ BC2006: option 'analyzerconfig' requires ':']]> Optional expectedWarningCount As Integer = 0, Optional expectedErrorCount As Integer = 0, Optional errorlog As Boolean = False, - Optional analyzers As ImmutableArray(Of DiagnosticAnalyzer) = Nothing) As String + Optional analyzers As DiagnosticAnalyzer() = Nothing) As String Dim args = { "/nologo", "/preferreduilang:en", "/t:library", sourceFile.Path @@ -8844,13 +8845,20 @@ Class C End Class .Value).Path - Dim vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"/reportanalyzer", "/t:library", "/a:" + Assembly.GetExecutingAssembly().Location, source}) + Dim vbc = New MockVisualBasicCompiler( + Nothing, + _baseDirectory, + {"/reportanalyzer", "/t:library", source}, + analyzers:={New WarningDiagnosticAnalyzer()}, + generators:={New DoNothingGenerator().AsSourceGenerator()}) Dim outWriter = New StringWriter() Dim exitCode = vbc.Run(outWriter, Nothing) Assert.Equal(0, exitCode) Dim output = outWriter.ToString() Assert.Contains(New WarningDiagnosticAnalyzer().ToString(), output, StringComparison.Ordinal) Assert.Contains(CodeAnalysisResources.AnalyzerExecutionTimeColumnHeader, output, StringComparison.Ordinal) + Assert.Contains(CodeAnalysisResources.GeneratorNameColumnHeader, output, StringComparison.Ordinal) + Assert.Contains(GetType(DoNothingGenerator).FullName, output, StringComparison.Ordinal) CleanupAllGeneratedFiles(source) End Sub @@ -9680,10 +9688,9 @@ End Class" suppressor.SuppressionDescriptor.Id, suppressor.SuppressionDescriptor.Justification) - Dim suppressors = ImmutableArray.Create(Of DiagnosticAnalyzer)(suppressor) output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0, includeCurrentAssemblyAsAnalyzerReference:=False, - analyzers:=suppressors) + analyzers:={suppressor}) Assert.DoesNotContain("warning BC40008", output, StringComparison.Ordinal) Assert.Contains("info SP0001", output, StringComparison.Ordinal) Assert.Contains(suppressionMessage, output, StringComparison.Ordinal) @@ -9723,7 +9730,7 @@ End Class" ' and info diagnostic is logged with programmatic suppression information. Dim suppressor = New DiagnosticSuppressorForId("BC40008") - Dim suppressors = ImmutableArray.Create(Of DiagnosticAnalyzer)(suppressor) + Dim suppressors = {suppressor} output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0, expectedErrorCount:=0, additionalFlags:={"/warnaserror+"}, includeCurrentAssemblyAsAnalyzerReference:=False, @@ -9760,7 +9767,7 @@ End Class" Assert.Contains("error BC30203", output, StringComparison.Ordinal) ' Verify that compiler error BC30203 cannot be suppressed with diagnostic suppressor. - Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(New DiagnosticSuppressorForId("BC30203")) + Dim analyzers = {New DiagnosticSuppressorForId("BC30203")} output = VerifyOutput(dir, file, expectedErrorCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, analyzers:=analyzers) @@ -9781,7 +9788,7 @@ End Class" ' Verify that analyzer warning is reported. Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable:=True) - Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer) + Dim analyzers = {analyzer} Dim output = VerifyOutput(dir, file, expectedWarningCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, analyzers:=analyzers) @@ -9798,7 +9805,7 @@ End Class" suppressor.SuppressionDescriptor.Id, suppressor.SuppressionDescriptor.Justification) - Dim analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor) + Dim analyzerAndSuppressor As DiagnosticAnalyzer() = {analyzer, suppressor} output = VerifyOutput(dir, file, expectedInfoCount:=1, expectedWarningCount:=0, includeCurrentAssemblyAsAnalyzerReference:=False, analyzers:=analyzerAndSuppressor) @@ -9826,7 +9833,7 @@ End Class" ' Verify that "NotConfigurable" analyzer warning cannot be suppressed with diagnostic suppressor even with /warnaserror. analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable:=False) suppressor = New DiagnosticSuppressorForId(analyzer.Descriptor.Id) - analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor) + analyzerAndSuppressor = {analyzer, suppressor} output = VerifyOutput(dir, file, expectedWarningCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, analyzers:=analyzerAndSuppressor) @@ -9847,15 +9854,14 @@ End Class" ' Verify that analyzer error is reported. Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Error, configurable:=True) - Dim analyzers = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer) Dim output = VerifyOutput(dir, file, expectedErrorCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, - analyzers:=analyzers) + analyzers:={analyzer}) Assert.Contains($"error {analyzer.Descriptor.Id}", output, StringComparison.Ordinal) ' Verify that analyzer error cannot be suppressed with diagnostic suppressor. Dim suppressor = New DiagnosticSuppressorForId(analyzer.Descriptor.Id) - Dim analyzerAndSuppressor = ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer, suppressor) + Dim analyzerAndSuppressor As DiagnosticAnalyzer() = {analyzer, suppressor} output = VerifyOutput(dir, file, expectedErrorCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, analyzers:=analyzerAndSuppressor) @@ -10041,7 +10047,7 @@ End Class" additionalFlags, expectedErrorCount:=expectedErrorCount, expectedWarningCount:=expectedWarningCount, - analyzers:=ImmutableArray.Create(analyzer)) + analyzers:={analyzer}) Dim expectedODiagnosticSeverity = If(warnAsError, "error", "warning") Assert.Contains($"{expectedODiagnosticSeverity} {WarningDiagnosticAnalyzer.Warning01.Id}", output) @@ -10173,7 +10179,7 @@ dotnet_diagnostic.{diagnosticId}.severity = {severityString}") expectedWarningCount:=expectedWarningCount, expectedErrorCount:=expectedErrorCount, additionalFlags:=additionalFlags, - analyzers:=ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) + analyzers:={analyzer}) End Sub @@ -10281,7 +10287,7 @@ End Class" Dim output = VerifyOutput(srcDirectory, srcFile, expectedWarningCount:=1, includeCurrentAssemblyAsAnalyzerReference:=False, additionalFlags:={"/additionalfile:" & additionalFile.Path}, - analyzers:=ImmutableArray.Create(analyzer)) + analyzers:={analyzer}) Assert.Contains("b.txt(1) : warning ID0001", output, StringComparison.Ordinal) CleanupAllGeneratedFiles(srcDirectory.Path) End Sub @@ -10365,7 +10371,7 @@ dotnet_diagnostic.{diagnosticId}.severity = {analyzerConfigSeverity}") Dim output = VerifyOutput(dir, src, includeCurrentAssemblyAsAnalyzerReference:=False, additionalArgs, expectedErrorCount:=If(expectError, 1, 0), expectedWarningCount:=If(expectWarning, 1, 0), - analyzers:=analyzers.ToImmutableArrayOrEmpty()) + analyzers:=analyzers) If expectError Then Assert.Contains($"error {diagnosticId}", output) diff --git a/src/Compilers/VisualBasic/vbc/VbcCommandLine.shproj b/src/Compilers/VisualBasic/vbc/VbcCommandLine.shproj index 933268ed79293..6ddb4dd2ca08e 100644 --- a/src/Compilers/VisualBasic/vbc/VbcCommandLine.shproj +++ b/src/Compilers/VisualBasic/vbc/VbcCommandLine.shproj @@ -11,6 +11,7 @@ Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props')" /> + \ No newline at end of file