Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion analyzers/src/SonarAnalyzer.CFG/Helpers/RoslynHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,45 @@ 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) { }

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.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
11 changes: 11 additions & 0 deletions its/projects/Roslyn.1.3.2/Roslyn.1.3.2.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>

<ItemGroup>
<!-- This will enforce using Roslyn 1.3.2 -->
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="1.0.3" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions its/projects/Roslyn.1.3.2/packages.lock.json
Original file line number Diff line number Diff line change
@@ -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=="
}
}
}
}
7 changes: 7 additions & 0 deletions its/projects/Roslyn.2.4.0/OldRoslyn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Roslyn
{
public class OldRoslyn
{
// Nothing to see here
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>

<ItemGroup>
<!-- This will enforce using Roslyn 1.3.1 -->
<!-- This will enforce using Roslyn 2.4.0 -->
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="1.0.8" />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Microsoft.CodeDom.Providers.DotNetCompilerPlatform version 1.0.8 was actually enforcing Roslyn version 2.4.0, thus raising the MSBuild15 deprecation warning.
I created another test project with a DotNetCompilerPlatform package version 1.0.3 that's enforcing Roslyn version 1.3.2 to generate and test the MSBuild14 phase-out support warning.

</ItemGroup>
</Project>
25 changes: 25 additions & 0 deletions its/projects/Roslyn.2.4.0/Roslyn.2.4.0.sln
Original file line number Diff line number Diff line change
@@ -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
14 changes: 11 additions & 3 deletions its/src/test/java/com/sonar/it/csharp/AnalysisWarningsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}