diff --git a/analyzers/src/SonarAnalyzer.CFG/Helpers/RoslynHelper.cs b/analyzers/src/SonarAnalyzer.CFG/Helpers/RoslynHelper.cs index adf692b344b..3813f713e83 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Helpers/RoslynHelper.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Helpers/RoslynHelper.cs @@ -22,12 +22,16 @@ namespace SonarAnalyzer.CFG.Helpers { public static class RoslynHelper { + public const int VS2017MajorVersion = 2; public const int MinimalSupportedMajorVersion = 3; public static bool IsRoslynCfgSupported(int minimalVersion = MinimalSupportedMajorVersion) => - typeof(SemanticModel).Assembly.GetName().Version.Major >= minimalVersion; + !IsVersionLessThan(minimalVersion); public static Type FlowAnalysisType(string typeName) => typeof(SemanticModel).Assembly.GetType("Microsoft.CodeAnalysis.FlowAnalysis." + typeName); + + public static bool IsVersionLessThan(int minimalVersion = MinimalSupportedMajorVersion) => + typeof(SemanticModel).Assembly.GetName().Version.Major < minimalVersion; } } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/AnalysisWarningAnalyzerBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/AnalysisWarningAnalyzerBase.cs index 75054efae67..256612afd7c 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/AnalysisWarningAnalyzerBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/AnalysisWarningAnalyzerBase.cs @@ -28,7 +28,8 @@ public abstract class AnalysisWarningAnalyzerBase : UtilityAnalyzerBase private const string DiagnosticId = "S9999-warning"; private const string Title = "Analysis Warning generator"; - protected virtual int MinimalSupportedRoslynVersion => RoslynHelper.MinimalSupportedMajorVersion; // For testing + protected virtual int VS2017MajorVersion => RoslynHelper.VS2017MajorVersion; // For testing + protected virtual int MinimalSupportedRoslynVersion => RoslynHelper.MinimalSupportedMajorVersion; // For testing protected AnalysisWarningAnalyzerBase() : base(DiagnosticId, Title) { } @@ -36,21 +37,36 @@ protected sealed override void Initialize(SonarAnalysisContext context) => context.RegisterCompilationAction(c => { var parameter = ReadParameters(c); - if (parameter.IsAnalyzerEnabled && !RoslynHelper.IsRoslynCfgSupported(MinimalSupportedRoslynVersion)) // MsBuild 15 is bound with Roslyn 2.x, where Roslyn CFG is not available. + if (!parameter.IsAnalyzerEnabled || parameter.OutPath is null) { - // This can be removed after we bump Microsoft.CodeAnalysis references to 3.0 or higher. - var path = Path.GetFullPath(Path.Combine(parameter.OutPath, "../../AnalysisWarnings.MsBuild.json")); - if (!File.Exists(path)) + return; + } + + var path = Path.GetFullPath(Path.Combine(parameter.OutPath, "../../AnalysisWarnings.MsBuild.json")); + if (!File.Exists(path)) + { + // This can be removed after we bump Microsoft.CodeAnalysis references to 2.0 or higher. MsBuild 14 is bound with Roslyn 1.x. + if (RoslynHelper.IsVersionLessThan(VS2017MajorVersion)) { - try - { - File.WriteAllText(path, """[{"text": "Analysis using MsBuild 14 and 15 build tools is deprecated. Please update your pipeline to MsBuild 16 or higher."}]"""); - } - catch - { - // Nothing to do here. Two compilations running on two different processes are unlikely to lock each other out on a small file write. - } + WriteAllText(path, "The analysis using MsBuild 14 is no longer supported and the analysis with MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."); + } + // This can be removed after we bump Microsoft.CodeAnalysis references to 3.0 or higher. MsBuild 15 is bound with Roslyn 2.x. + else if (RoslynHelper.IsVersionLessThan(MinimalSupportedRoslynVersion)) + { + WriteAllText(path, "The analysis using MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."); } } }); + + private static void WriteAllText(string path, string text) + { + try + { + File.WriteAllText(path, $$"""[{"text": "{{text}}"}]"""); + } + catch + { + // Nothing to do here. Two compilations running on two different processes are unlikely to lock each other out on a small file write. + } + } } diff --git a/analyzers/tests/SonarAnalyzer.Test/Rules/Utilities/AnalysisWarningAnalyzerTest.cs b/analyzers/tests/SonarAnalyzer.Test/Rules/Utilities/AnalysisWarningAnalyzerTest.cs index f6681dcad22..ab55580203c 100644 --- a/analyzers/tests/SonarAnalyzer.Test/Rules/Utilities/AnalysisWarningAnalyzerTest.cs +++ b/analyzers/tests/SonarAnalyzer.Test/Rules/Utilities/AnalysisWarningAnalyzerTest.cs @@ -37,37 +37,54 @@ public class AnalysisWarningAnalyzerTest [DataRow(LanguageNames.CSharp, false)] [DataRow(LanguageNames.VisualBasic, true)] [DataRow(LanguageNames.VisualBasic, false)] - public void SupportedRoslyn(string languageName, bool isAnalyzerEnabled) + public void AnalysisWarning_MSBuildSupportedScenario_NoWarning(string languageName, bool isAnalyzerEnabled) { - var expectedPath = ExecuteAnalyzer(languageName, isAnalyzerEnabled, RoslynHelper.MinimalSupportedMajorVersion); // Using production value that is lower than our UT Roslyn version + var expectedPath = ExecuteAnalyzer(languageName, isAnalyzerEnabled, RoslynHelper.VS2017MajorVersion, RoslynHelper.MinimalSupportedMajorVersion); // Using production value that is lower than our UT Roslyn version File.Exists(expectedPath).Should().BeFalse("Analysis warning file should not be generated."); } [DataTestMethod] [DataRow(LanguageNames.CSharp)] [DataRow(LanguageNames.VisualBasic)] - public void OldRoslyn(string languageName) + public void AnalysisWarning_MSBuild14UnsupportedScenario_GenerateWarning(string languageName) { - var expectedPath = ExecuteAnalyzer(languageName, true, 1000); // Requiring too high Roslyn version => we're under unsupported scenario + var expectedPath = ExecuteAnalyzer(languageName, true, 1000, 1001); // Requiring too high Roslyn version => we're under unsupported scenario File.Exists(expectedPath).Should().BeTrue(); - File.ReadAllText(expectedPath).Should().Be("""[{"text": "Analysis using MsBuild 14 and 15 build tools is deprecated. Please update your pipeline to MsBuild 16 or higher."}]"""); + File.ReadAllText(expectedPath).Should().Be("""[{"text": "The analysis using MsBuild 14 is no longer supported and the analysis with MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."}]"""); + } + + [DataTestMethod] + [DataRow(LanguageNames.CSharp)] + [DataRow(LanguageNames.VisualBasic)] + public void AnalysisWarning_MSBuild15DeprecatedScenario_GenerateWarning(string languageName) + { + var expectedPath = ExecuteAnalyzer(languageName, true, RoslynHelper.VS2017MajorVersion, 1000); // Requiring too high Roslyn version => we're under unsupported scenario + File.Exists(expectedPath).Should().BeTrue(); + File.ReadAllText(expectedPath).Should().Be("""[{"text": "The analysis using MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."}]"""); + } + [DataTestMethod] + [DataRow(LanguageNames.CSharp)] + [DataRow(LanguageNames.VisualBasic)] + public void AnalysisWarning_LockFile_PathShouldBeReused(string languageName) + { + var expectedPath = ExecuteAnalyzer(languageName, true, RoslynHelper.VS2017MajorVersion, 1000); // Lock file and run it for 2nd time using var lockedFile = new FileStream(expectedPath, FileMode.Open, FileAccess.Write, FileShare.None); - ExecuteAnalyzer(languageName, true, 1000).Should().Be(expectedPath, "path should be reused and analyzer should not fail"); + ExecuteAnalyzer(languageName, true, RoslynHelper.VS2017MajorVersion, 1000).Should().Be(expectedPath, "path should be reused and analyzer should not fail"); } [DataTestMethod] [DataRow(LanguageNames.CSharp)] [DataRow(LanguageNames.VisualBasic)] - public void FileExceptions_AreIgnored(string languageName) + public void AnalysisWarning_FileExceptions_AreIgnored(string languageName) { // This will not create the output directory, causing an exception in the File.WriteAllText(...) - var expectedPath = ExecuteAnalyzer(languageName, true, 1000, false); // Requiring too high Roslyn version => we're under unsupported scenario + var expectedPath = ExecuteAnalyzer(languageName, true, 500, 1000, false); // Requiring too high Roslyn version => we're under unsupported scenario File.Exists(expectedPath).Should().BeFalse(); } - private string ExecuteAnalyzer(string languageName, bool isAnalyzerEnabled, int minimalSupportedRoslynVersion, bool createDirectory = true) + private string ExecuteAnalyzer(string languageName, bool isAnalyzerEnabled, int vs2017MajorVersion, int minimalSupportedRoslynVersion, bool createDirectory = true) { var language = AnalyzerLanguage.FromName(languageName); var analysisOutPath = TestHelper.TestPath(TestContext, @$"{languageName}\.sonarqube\out"); @@ -78,8 +95,8 @@ private string ExecuteAnalyzer(string languageName, bool isAnalyzerEnabled, int } UtilityAnalyzerBase analyzer = language.LanguageName switch { - LanguageNames.CSharp => new TestAnalysisWarningAnalyzer_CS(isAnalyzerEnabled, minimalSupportedRoslynVersion, projectOutPath), - LanguageNames.VisualBasic => new TestAnalysisWarningAnalyzer_VB(isAnalyzerEnabled, minimalSupportedRoslynVersion, projectOutPath), + LanguageNames.CSharp => new TestAnalysisWarningAnalyzer_CS(isAnalyzerEnabled, vs2017MajorVersion, minimalSupportedRoslynVersion, projectOutPath), + LanguageNames.VisualBasic => new TestAnalysisWarningAnalyzer_VB(isAnalyzerEnabled, vs2017MajorVersion, minimalSupportedRoslynVersion, projectOutPath), _ => throw new UnexpectedLanguageException(language) }; new VerifierBuilder().AddAnalyzer(() => analyzer).AddSnippet(string.Empty).VerifyNoIssueReported(); // Nothing to analyze, just make it run @@ -90,11 +107,13 @@ private sealed class TestAnalysisWarningAnalyzer_CS : CS.AnalysisWarningAnalyzer { private readonly bool isAnalyzerEnabled; private readonly string outPath; + protected override int VS2017MajorVersion { get; } protected override int MinimalSupportedRoslynVersion { get; } - public TestAnalysisWarningAnalyzer_CS(bool isAnalyzerEnabled, int minimalSupportedRoslynVersion, string outPath) + public TestAnalysisWarningAnalyzer_CS(bool isAnalyzerEnabled, int vs2017MajorVersion, int minimalSupportedRoslynVersion, string outPath) { this.isAnalyzerEnabled = isAnalyzerEnabled; + VS2017MajorVersion = vs2017MajorVersion; MinimalSupportedRoslynVersion = minimalSupportedRoslynVersion; this.outPath = outPath; } @@ -107,11 +126,13 @@ private sealed class TestAnalysisWarningAnalyzer_VB : VB.AnalysisWarningAnalyzer { private readonly bool isAnalyzerEnabled; private readonly string outPath; + protected override int VS2017MajorVersion { get; } protected override int MinimalSupportedRoslynVersion { get; } - public TestAnalysisWarningAnalyzer_VB(bool isAnalyzerEnabled, int minimalSupportedRoslynVersion, string outPath) + public TestAnalysisWarningAnalyzer_VB(bool isAnalyzerEnabled, int vs2017MajorVersion, int minimalSupportedRoslynVersion, string outPath) { this.isAnalyzerEnabled = isAnalyzerEnabled; + VS2017MajorVersion = vs2017MajorVersion; MinimalSupportedRoslynVersion = minimalSupportedRoslynVersion; this.outPath = outPath; } diff --git a/its/projects/Roslyn.1.3.1/OldRoslyn.cs b/its/projects/Roslyn.1.3.2/OldRoslyn.cs similarity index 100% rename from its/projects/Roslyn.1.3.1/OldRoslyn.cs rename to its/projects/Roslyn.1.3.2/OldRoslyn.cs diff --git a/its/projects/Roslyn.1.3.2/Roslyn.1.3.2.csproj b/its/projects/Roslyn.1.3.2/Roslyn.1.3.2.csproj new file mode 100644 index 00000000000..f9041192003 --- /dev/null +++ b/its/projects/Roslyn.1.3.2/Roslyn.1.3.2.csproj @@ -0,0 +1,11 @@ + + + + net48 + + + + + + + diff --git a/its/projects/Roslyn.1.3.1/Roslyn.1.3.1.sln b/its/projects/Roslyn.1.3.2/Roslyn.1.3.2.sln similarity index 88% rename from its/projects/Roslyn.1.3.1/Roslyn.1.3.1.sln rename to its/projects/Roslyn.1.3.2/Roslyn.1.3.2.sln index 264db477dc7..cc74993e5aa 100644 --- a/its/projects/Roslyn.1.3.1/Roslyn.1.3.1.sln +++ b/its/projects/Roslyn.1.3.2/Roslyn.1.3.2.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32811.315 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Roslyn.1.3.1", "Roslyn.1.3.1.csproj", "{151E9036-31F1-481F-BBCE-6C663F21EB31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.1.3.2", "Roslyn.1.3.2.csproj", "{151E9036-31F1-481F-BBCE-6C663F21EB31}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/its/projects/Roslyn.1.3.2/packages.lock.json b/its/projects/Roslyn.1.3.2/packages.lock.json new file mode 100644 index 00000000000..bfdf68d3392 --- /dev/null +++ b/its/projects/Roslyn.1.3.2/packages.lock.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.CodeDom.Providers.DotNetCompilerPlatform": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "7G2GcPc9aveqUa2t1a4OZmfs4RqSlalpHC94KILFswIDLCTGrser0ymXi7TnUI5gAIHhfn2dEjpIb5taxPWU5g==", + "dependencies": { + "Microsoft.Net.Compilers": "1.3.2" + } + }, + "Microsoft.Net.Compilers": { + "type": "Transitive", + "resolved": "1.3.2", + "contentHash": "usbCzvNYmSWTdlsJM8FYm1NgGPdM+njnBzerAn3xKGy4/prztHcofq7CMiO7Lb0AK71jXKUzmUuW9Fc0cIzdbQ==" + } + } + } +} \ No newline at end of file diff --git a/its/projects/Roslyn.2.4.0/OldRoslyn.cs b/its/projects/Roslyn.2.4.0/OldRoslyn.cs new file mode 100644 index 00000000000..aa524e74323 --- /dev/null +++ b/its/projects/Roslyn.2.4.0/OldRoslyn.cs @@ -0,0 +1,7 @@ +namespace Roslyn +{ + public class OldRoslyn + { + // Nothing to see here + } +} diff --git a/its/projects/Roslyn.1.3.1/Roslyn.1.3.1.csproj b/its/projects/Roslyn.2.4.0/Roslyn.2.4.0.csproj similarity index 72% rename from its/projects/Roslyn.1.3.1/Roslyn.1.3.1.csproj rename to its/projects/Roslyn.2.4.0/Roslyn.2.4.0.csproj index 7bd0c451241..5f04cb99e30 100644 --- a/its/projects/Roslyn.1.3.1/Roslyn.1.3.1.csproj +++ b/its/projects/Roslyn.2.4.0/Roslyn.2.4.0.csproj @@ -1,11 +1,11 @@ - + net48 - + diff --git a/its/projects/Roslyn.2.4.0/Roslyn.2.4.0.sln b/its/projects/Roslyn.2.4.0/Roslyn.2.4.0.sln new file mode 100644 index 00000000000..c92130659f6 --- /dev/null +++ b/its/projects/Roslyn.2.4.0/Roslyn.2.4.0.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32811.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.2.4.0", "Roslyn.2.4.0.csproj", "{151E9036-31F1-481F-BBCE-6C663F21EB31}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {151E9036-31F1-481F-BBCE-6C663F21EB31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {151E9036-31F1-481F-BBCE-6C663F21EB31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {151E9036-31F1-481F-BBCE-6C663F21EB31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {151E9036-31F1-481F-BBCE-6C663F21EB31}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0E15ED11-FF3B-4CCE-8940-EFE27FCC42FD} + EndGlobalSection +EndGlobal diff --git a/its/projects/Roslyn.1.3.1/packages.lock.json b/its/projects/Roslyn.2.4.0/packages.lock.json similarity index 100% rename from its/projects/Roslyn.1.3.1/packages.lock.json rename to its/projects/Roslyn.2.4.0/packages.lock.json diff --git a/its/src/test/java/com/sonar/it/csharp/AnalysisWarningsTest.java b/its/src/test/java/com/sonar/it/csharp/AnalysisWarningsTest.java index aad6f18021d..ff6bbdf14c4 100644 --- a/its/src/test/java/com/sonar/it/csharp/AnalysisWarningsTest.java +++ b/its/src/test/java/com/sonar/it/csharp/AnalysisWarningsTest.java @@ -56,10 +56,18 @@ void analysisWarningsImport() throws IOException { } @Test - void analysisWarnings_OldRoslyn() throws IOException { - BuildResult buildResult = Tests.analyzeProject(temp, "Roslyn.1.3.1"); + void analysisWarnings_MSBuild14() throws IOException { + BuildResult buildResult = Tests.analyzeProject(temp, "Roslyn.1.3.2"); Ce.Task task = TestUtils.getAnalysisWarningsTask(ORCHESTRATOR, buildResult); assertThat(task.getStatus()).isEqualTo(Ce.TaskStatus.SUCCESS); - assertThat(task.getWarningsList()).containsExactly("Analysis using MsBuild 14 and 15 build tools is deprecated. Please update your pipeline to MsBuild 16 or higher."); + assertThat(task.getWarningsList()).containsExactly("The analysis using MsBuild 14 is no longer supported and the analysis with MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."); + } + + @Test + void analysisWarnings_MSBuild15() throws IOException { + BuildResult buildResult = Tests.analyzeProject(temp, "Roslyn.2.4.0"); + Ce.Task task = TestUtils.getAnalysisWarningsTask(ORCHESTRATOR, buildResult); + assertThat(task.getStatus()).isEqualTo(Ce.TaskStatus.SUCCESS); + assertThat(task.getWarningsList()).containsExactly("The analysis using MsBuild 15 is deprecated. Please update your pipeline to MsBuild 16 or higher."); } }