From deef83e1dad5d2f57753252d36ab59ae30b57f67 Mon Sep 17 00:00:00 2001 From: Alex Meseldzija Date: Mon, 6 Jan 2025 17:56:28 +0100 Subject: [PATCH] NET-908 ONLY raise on VS version >= 17.9 Co-authored-by: Andrei Epure <38876598+andrei-epure-sonarsource@users.noreply.github.com> --- .../SonarAnalyzer.CFG/Common/RoslynVersion.cs | 8 ++- .../Helpers/IssueReporter.cs | 17 +++++- .../Common/IssueReporterTest.cs | 52 ++++++++++++++++++- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/Common/RoslynVersion.cs b/analyzers/src/SonarAnalyzer.CFG/Common/RoslynVersion.cs index 871d6c55490..719cdda8b7e 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Common/RoslynVersion.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Common/RoslynVersion.cs @@ -25,5 +25,11 @@ public static bool IsRoslynCfgSupported(int minimalVersion = MinimalSupportedMaj !IsVersionLessThan(minimalVersion); public static bool IsVersionLessThan(int minimalVersion = MinimalSupportedMajorVersion) => - typeof(SemanticModel).Assembly.GetName().Version.Major < minimalVersion; + CurrentVersion().Major < minimalVersion; + + public static bool IsVersionLessThan(Version version) => + CurrentVersion() < version; + + private static Version CurrentVersion() => + typeof(SemanticModel).Assembly.GetName().Version; } diff --git a/analyzers/src/SonarAnalyzer.Core/Helpers/IssueReporter.cs b/analyzers/src/SonarAnalyzer.Core/Helpers/IssueReporter.cs index 7927d2c768b..83809525865 100644 --- a/analyzers/src/SonarAnalyzer.Core/Helpers/IssueReporter.cs +++ b/analyzers/src/SonarAnalyzer.Core/Helpers/IssueReporter.cs @@ -14,6 +14,8 @@ * along with this program; if not, see https://sonarsource.com/license/ssal/ */ +using SonarAnalyzer.CFG.Common; + namespace SonarAnalyzer.Helpers; public static class IssueReporter @@ -30,6 +32,10 @@ public static class IssueReporter "S1481", "S1871"); + // Minimum supported version for Razor IDE is Visual Studio 17.9/Roslyn 4.9.2 + // https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-version-support?view=vs-2022 + private static Version minimumDesignTimeRoslynVersion = new("4.9.2"); + public static void ReportIssueCore( Compilation compilation, Func hasMatchingScope, @@ -88,13 +94,22 @@ public static void ReportIssueCore( } } + internal static void SetMinimumDesignTimeRoslynVersion(Version version) => + minimumDesignTimeRoslynVersion = version; + + internal static Version GetMinimumDesignTimeRoslynVersion() => + minimumDesignTimeRoslynVersion; + private static bool ShouldRaiseOnRazorFile(ref Diagnostic diagnostic) { // On design time, we only raise on generated .ide.g.cs files if the diagnostic has a mapped location. if (GeneratedCodeRecognizer.IsDesignTimeRazorGeneratedFile(diagnostic.Location.SourceTree)) { return diagnostic.Location.GetMappedLineSpan().HasMappedPath - && !ExcludedFromDesignTimeRuleIds.Contains(diagnostic.Id); + && !ExcludedFromDesignTimeRuleIds.Contains(diagnostic.Id) + // We only want to raise on design time generated files if the Visual studio version is >= 17.9 which we infer from the Roslyn version + // https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-version-support?view=vs-2022 + && !RoslynVersion.IsVersionLessThan(minimumDesignTimeRoslynVersion); } // On build time, if the diagnostic has a mapped location, we do the mapping ourselves and raise there. else if (GeneratedCodeRecognizer.IsBuildTimeRazorGeneratedFile(diagnostic.Location.SourceTree)) diff --git a/analyzers/tests/SonarAnalyzer.Core.Test/Common/IssueReporterTest.cs b/analyzers/tests/SonarAnalyzer.Core.Test/Common/IssueReporterTest.cs index 71be8e6dab7..444e6b05f10 100644 --- a/analyzers/tests/SonarAnalyzer.Core.Test/Common/IssueReporterTest.cs +++ b/analyzers/tests/SonarAnalyzer.Core.Test/Common/IssueReporterTest.cs @@ -24,6 +24,7 @@ namespace SonarAnalyzer.Core.Test.Common; [TestClass] public class IssueReporterTest { + private readonly Version defaultVersion = new Version("4.9.2"); private readonly DummyDiagnosticReporter reporter = new(); [DataTestMethod] @@ -60,16 +61,65 @@ public void ReportIssueCore_DesignTimeDiagnostic_HasMappedPath_False() reporter.LastDiagnostic.Should().BeNull(); } - private Diagnostic ReportDiagnostic(string filePath, bool hasMappedPath, string diagnosticId = "id") + [TestMethod] + public void ReportIssueCore_DesignTimeDiagnostic_RoslynVersion_1000_0_0() + { + ReportDiagnostic(@"C:\SonarSource\SomeFile.razor.-6NXeWT5Akt4vxdz.ide.g.cs", hasMappedPath: true, roslynVersion: new Version(1000, 0, 0)); + reporter.Counter.Should().Be(0); + reporter.LastDiagnostic.Should().BeNull(); + } + + [TestMethod] + public void ReportIssueCore_DesignTimeDiagnostic_RoslynVersion_Greater_Than_Current() + { + Version current = new(typeof(SemanticModel).Assembly.GetName().Version.ToString()); + Version greaterThanCurrent = new(current.Major, current.Minor, current.Build + 1); + + ReportDiagnostic(@"C:\SonarSource\SomeFile.razor.-6NXeWT5Akt4vxdz.ide.g.cs", hasMappedPath: true, roslynVersion: greaterThanCurrent); + reporter.Counter.Should().Be(0); + reporter.LastDiagnostic.Should().BeNull(); + } + + [TestMethod] + public void ReportIssueCore_DesignTimeDiagnostic_RoslynVersion_Less_Than_Current() { + var lessthanCurrent = LessThanCurrent(); + + var diagnostic = ReportDiagnostic(@"C:\SonarSource\SomeFile.razor.-6NXeWT5Akt4vxdz.ide.g.cs", hasMappedPath: true, roslynVersion: lessthanCurrent); + reporter.Counter.Should().Be(1); + reporter.LastDiagnostic.Should().Be(diagnostic); + + static Version LessThanCurrent() + { + var roslynVersion = new Version(typeof(SemanticModel).Assembly.GetName().Version.ToString()); + return roslynVersion.Build switch + { + > 0 => new Version(roslynVersion.Major, roslynVersion.Minor, roslynVersion.Build - 1), + _ when roslynVersion.Minor > 0 => new Version(roslynVersion.Major, roslynVersion.Minor - 1), + _ => new Version(roslynVersion.Major - 1, 99, 99), + }; + } + } + + [TestMethod] + public void ReportIssueCore_MinimumRoslyVersion_Is_4_9_2() => + Assert.AreEqual(defaultVersion, IssueReporter.GetMinimumDesignTimeRoslynVersion()); + + private Diagnostic ReportDiagnostic(string filePath, bool hasMappedPath, string diagnosticId = "id", Version roslynVersion = null) + { + roslynVersion ??= defaultVersion; var tree = GetTree(filePath); var diagnostic = GetDiagnostic(diagnosticId, tree, hasMappedPath); + IssueReporter.SetMinimumDesignTimeRoslynVersion(roslynVersion); + IssueReporter.ReportIssueCore( x => true, x => new DummyReportingContext(reporter, x, tree), diagnostic); + IssueReporter.SetMinimumDesignTimeRoslynVersion(defaultVersion); + return diagnostic; }