Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove language property from SonarLintXmlReader #6905

Merged
Expand Up @@ -30,13 +30,9 @@ public class SonarAnalysisContextBase
protected static readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<string, bool>> FileInclusionCache = new();
protected static readonly ConditionalWeakTable<Compilation, ImmutableHashSet<string>> UnchangedFilesCache = new();
protected static readonly SourceTextValueProvider<ProjectConfigReader> ProjectConfigProvider = new(x => new ProjectConfigReader(x));
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
protected static readonly SourceTextValueProvider<SonarLintXmlReader> SonarLintXmlProviderCS = new(x => new SonarLintXmlReader(x, LanguageNames.CSharp));
protected static readonly SourceTextValueProvider<SonarLintXmlReader> SonarLintXmlProviderVB = new(x => new SonarLintXmlReader(x, LanguageNames.VisualBasic));
protected static readonly SourceTextValueProvider<SonarLintXmlReader> SonarLintXmlProvider = new(x => new SonarLintXmlReader(x));
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved

protected SonarAnalysisContextBase() { }

protected static SourceTextValueProvider<SonarLintXmlReader> SonarLintXmlReader(string language) =>
language == LanguageNames.CSharp ? SonarLintXmlProviderCS : SonarLintXmlProviderVB;
}

public abstract class SonarAnalysisContextBase<TContext> : SonarAnalysisContextBase
Expand All @@ -58,7 +54,7 @@ protected SonarAnalysisContextBase(SonarAnalysisContext analysisContext, TContex
/// <param name="generatedCodeRecognizer">When set, generated trees are analyzed only when language-specific 'analyzeGeneratedCode' configuration property is also set.</param>
public bool ShouldAnalyzeTree(SyntaxTree tree, GeneratedCodeRecognizer generatedCodeRecognizer) =>
SonarLintFile() is var sonarLintReader
&& (generatedCodeRecognizer is null || sonarLintReader.AnalyzeGeneratedCode || !tree.IsGenerated(generatedCodeRecognizer, Compilation))
&& (generatedCodeRecognizer is null || sonarLintReader.AnalyzeGeneratedCode(Compilation.Language) || !tree.IsGenerated(generatedCodeRecognizer, Compilation))
&& (tree is null || (!IsUnchanged(tree) && ShouldAnalyzeFile(sonarLintReader, tree.FilePath)));

/// <summary>
Expand Down Expand Up @@ -88,7 +84,7 @@ public SonarLintXmlReader SonarLintFile()
if (Options.SonarLintXml() is { } sonarLintXml)
{
return sonarLintXml.GetText() is { } sourceText
&& AnalysisContext.TryGetValue(sourceText, SonarLintXmlReader(Compilation.Language), out var sonarLintXmlReader)
&& AnalysisContext.TryGetValue(sourceText, SonarLintXmlProvider, out var sonarLintXmlReader)
? sonarLintXmlReader
: throw new InvalidOperationException($"File '{Path.GetFileName(sonarLintXml.Path)}' has been added as an AdditionalFile but could not be read and parsed.");
}
Expand Down
Expand Up @@ -23,5 +23,7 @@ namespace SonarAnalyzer.Common
public sealed class UnexpectedLanguageException : Exception
{
public UnexpectedLanguageException(AnalyzerLanguage language) : base($"Unexpected language: {language}") { }

public UnexpectedLanguageException(string language) : base($"Unexpected language: {language}") { }
}
}
32 changes: 21 additions & 11 deletions analyzers/src/SonarAnalyzer.Common/Helpers/SonarLintXmlReader.cs
Expand Up @@ -28,16 +28,29 @@ namespace SonarAnalyzer.Helpers;

public class SonarLintXmlReader
{
public static readonly SonarLintXmlReader Empty = new(null, LanguageNames.CSharp);
public static readonly SonarLintXmlReader Empty = new(null);

private readonly SonarLintXml sonarLintXml;
private readonly string propertyLanguage;

private bool? ignoreHeaderComments;
public bool IgnoreHeaderComments => ignoreHeaderComments ??= ReadBoolean(ReadSettingsProperty($"sonar.{propertyLanguage}.ignoreHeaderComments"));

private bool? analyzeGeneratedCode;
public bool AnalyzeGeneratedCode => analyzeGeneratedCode ??= ReadBoolean(ReadSettingsProperty($"sonar.{propertyLanguage}.analyzeGeneratedCode"));
private bool? ignoreHeaderCommentsCS;
private bool? ignoreHeaderCommentsVB;
public bool IgnoreHeaderComments(string language) =>
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
language switch
{
LanguageNames.CSharp => ignoreHeaderCommentsCS ??= ReadBoolean(ReadSettingsProperty("sonar.cs.ignoreHeaderComments")),
LanguageNames.VisualBasic => ignoreHeaderCommentsVB ??= ReadBoolean(ReadSettingsProperty("sonar.vbnet.ignoreHeaderComments")),
_ => throw new UnexpectedLanguageException(language)
};

private bool? analyzeGeneratedCodeCS;
private bool? analyzeGeneratedCodeVB;
public bool AnalyzeGeneratedCode(string language) =>
language switch
{
LanguageNames.CSharp => analyzeGeneratedCodeCS ??= ReadBoolean(ReadSettingsProperty("sonar.cs.analyzeGeneratedCode")),
LanguageNames.VisualBasic => analyzeGeneratedCodeVB ??= ReadBoolean(ReadSettingsProperty("sonar.vbnet.analyzeGeneratedCode")),
_ => throw new UnexpectedLanguageException(language)
};

private string[] exclusions;
public string[] Exclusions => exclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.exclusions"));
Expand All @@ -60,11 +73,8 @@ public class SonarLintXmlReader
private List<SonarLintXmlRule> parametrizedRules;
public List<SonarLintXmlRule> ParametrizedRules => parametrizedRules ??= ReadRuleParameters();

public SonarLintXmlReader(SourceText sonarLintXml, string language = LanguageNames.CSharp)
{
public SonarLintXmlReader(SourceText sonarLintXml) =>
this.sonarLintXml = sonarLintXml == null ? SonarLintXml.Empty : ParseContent(sonarLintXml);
propertyLanguage = language == LanguageNames.CSharp ? "cs" : "vbnet";
}

private static SonarLintXml ParseContent(SourceText sonarLintXml)
{
Expand Down
Expand Up @@ -61,8 +61,8 @@ protected void ReadParameters(SonarCompilationStartAnalysisContext context)
if (context.Options.SonarLintXml() != null && !string.IsNullOrEmpty(outPath))
{
var sonarLintXml = context.SonarLintFile();
IgnoreHeaderComments = sonarLintXml.IgnoreHeaderComments;
AnalyzeGeneratedCode = sonarLintXml.AnalyzeGeneratedCode;
IgnoreHeaderComments = sonarLintXml.IgnoreHeaderComments(context.Compilation.Language);
AnalyzeGeneratedCode = sonarLintXml.AnalyzeGeneratedCode(context.Compilation.Language);
OutPath = Path.Combine(outPath, context.Compilation.Language == LanguageNames.CSharp ? "output-cs" : "output-vbnet");
IsAnalyzerEnabled = true;
IsTestProject = context.IsTestProject();
Expand Down
Expand Up @@ -454,7 +454,7 @@ private static void VerifyEmpty(string fileName, string snippet, DiagnosticAnaly
{
LanguageNames.CSharp => builder.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest),
LanguageNames.VisualBasic => builder.WithLanguageVersion(Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Latest),
_ => throw new UnexpectedLanguageException(language)
_ => throw new UnexpectedLanguageException(language.LanguageName)
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
};
builder.VerifyNoIssueReported();
}
Expand Down
Expand Up @@ -181,8 +181,8 @@ public void SonarLintFile_LoadsExpectedValues(string language)
var options = AnalysisScaffolding.CreateOptions($"ResourceTests\\SonarLintXml\\All_properties_{language}\\SonarLint.xml");
var sut = CreateSut(compilation, options).SonarLintFile();

sut.IgnoreHeaderComments.Should().BeTrue();
sut.AnalyzeGeneratedCode.Should().BeFalse();
sut.IgnoreHeaderComments(analyzerLanguage.LanguageName).Should().BeTrue();
sut.AnalyzeGeneratedCode(analyzerLanguage.LanguageName).Should().BeFalse();
AssertArrayContent(sut.Exclusions, nameof(sut.Exclusions));
AssertArrayContent(sut.Inclusions, nameof(sut.Inclusions));
AssertArrayContent(sut.GlobalExclusions, nameof(sut.GlobalExclusions));
Expand Down Expand Up @@ -253,8 +253,8 @@ public void SonarLintFile_WhenFileIsMissing_ThrowException()

private static void CheckSonarLintXmlDefaultValues(SonarLintXmlReader sut)
{
sut.AnalyzeGeneratedCode.Should().BeFalse();
sut.IgnoreHeaderComments.Should().BeFalse();
sut.AnalyzeGeneratedCode(LanguageNames.CSharp).Should().BeFalse();
sut.IgnoreHeaderComments(LanguageNames.CSharp).Should().BeFalse();
sut.Exclusions.Should().NotBeNull().And.HaveCount(0);
sut.Inclusions.Should().NotBeNull().And.HaveCount(0);
sut.GlobalExclusions.Should().NotBeNull().And.HaveCount(0);
Expand Down
Expand Up @@ -20,6 +20,7 @@

using System.IO;
using Microsoft.CodeAnalysis.Text;
using SonarAnalyzer.Common;

namespace SonarAnalyzer.UnitTest.Helpers;

Expand All @@ -29,11 +30,11 @@ public class SonarLintXmlReaderTest
[DataTestMethod]
[DataRow(LanguageNames.CSharp, "cs")]
[DataRow(LanguageNames.VisualBasic, "vbnet")]
public void SonarLintXmlReader_WhenAllValuesAreSet_ExpectedValues(string language, string propertyLanguage)
public void SonarLintXmlReader_WhenAllValuesAreSet_ExpectedValues(string language, string xmlLanguageName)
{
var sut = CreateSonarLintXmlReader($"ResourceTests\\SonarLintXml\\All_Properties_{propertyLanguage}\\SonarLint.xml", language);
sut.IgnoreHeaderComments.Should().BeTrue();
sut.AnalyzeGeneratedCode.Should().BeFalse();
var sut = CreateSonarLintXmlReader($"ResourceTests\\SonarLintXml\\All_Properties_{xmlLanguageName}\\SonarLint.xml");
sut.IgnoreHeaderComments(language).Should().BeTrue();
sut.AnalyzeGeneratedCode(language).Should().BeFalse();
AssertArrayContent(sut.Exclusions, nameof(sut.Exclusions));
AssertArrayContent(sut.Inclusions, nameof(sut.Inclusions));
AssertArrayContent(sut.GlobalExclusions, nameof(sut.GlobalExclusions));
Expand All @@ -60,8 +61,8 @@ static void AssertArrayContent(string[] array, string folder)
public void SonarLintXmlReader_PartiallyMissingProperties_ExpectedAndDefaultValues()
{
var sut = CreateSonarLintXmlReader("ResourceTests\\SonarLintXml\\Partially_missing_properties\\SonarLint.xml");
sut.IgnoreHeaderComments.Should().BeFalse();
sut.AnalyzeGeneratedCode.Should().BeTrue();
sut.IgnoreHeaderComments(LanguageNames.CSharp).Should().BeFalse();
sut.AnalyzeGeneratedCode(LanguageNames.CSharp).Should().BeTrue();
AssertArrayContent(sut.Exclusions, nameof(sut.Exclusions));
AssertArrayContent(sut.Inclusions, nameof(sut.Inclusions));
sut.GlobalExclusions.Should().NotBeNull().And.HaveCount(0);
Expand All @@ -71,12 +72,22 @@ public void SonarLintXmlReader_PartiallyMissingProperties_ExpectedAndDefaultValu
sut.ParametrizedRules.Should().NotBeNull().And.HaveCount(0);
}

[TestMethod]
public void SonarLintXmlReader_PropertiesCSharpTrueVBNetFalse_ExpectedValues()
{
var sut = CreateSonarLintXmlReader("ResourceTests\\SonarLintXml\\PropertiesCSharpTrueVbnetFalse\\SonarLint.xml");
sut.IgnoreHeaderComments(LanguageNames.CSharp).Should().BeTrue();
sut.IgnoreHeaderComments(LanguageNames.VisualBasic).Should().BeFalse();
sut.AnalyzeGeneratedCode(LanguageNames.CSharp).Should().BeTrue();
sut.AnalyzeGeneratedCode(LanguageNames.VisualBasic).Should().BeFalse();
}

[DataTestMethod]
[DataRow("")]
[DataRow("this is not an xml")]
[DataRow(@"<?xml version=""1.0"" encoding=""UTF - 8""?><AnalysisInput><Settings>")]
public void SonarLintXmlReader_WithMalformedXml_DefaultBehaviour(string sonarLintXmlContent) =>
CheckSonarLintXmlReaderDefaultValues(new SonarLintXmlReader(SourceText.From(sonarLintXmlContent), LanguageNames.CSharp));
CheckSonarLintXmlReaderDefaultValues(new SonarLintXmlReader(SourceText.From(sonarLintXmlContent)));

[TestMethod]
public void SonarLintXmlReader_MissingProperties_DefaultBehaviour() =>
Expand All @@ -90,10 +101,18 @@ public void SonarLintXmlReader_PartiallyMissingProperties_ExpectedAndDefaultValu
public void SonarLintXmlReader_CheckEmpty_DefaultBehaviour() =>
CheckSonarLintXmlReaderDefaultValues(SonarLintXmlReader.Empty);

[TestMethod]
public void SonarLintXmlReader_LanguageDoesNotExist_Throws()
{
var sut = CreateSonarLintXmlReader($"ResourceTests\\SonarLintXml\\All_Properties_cs\\SonarLint.xml");
sut.Invoking(x => x.IgnoreHeaderComments(LanguageNames.FSharp)).Should().Throw<UnexpectedLanguageException>().WithMessage("Unexpected language: F#");
sut.Invoking(x => x.AnalyzeGeneratedCode(LanguageNames.FSharp)).Should().Throw<UnexpectedLanguageException>().WithMessage("Unexpected language: F#");
}

private static void CheckSonarLintXmlReaderDefaultValues(SonarLintXmlReader sut)
{
sut.AnalyzeGeneratedCode.Should().BeFalse();
sut.IgnoreHeaderComments.Should().BeFalse();
sut.AnalyzeGeneratedCode(LanguageNames.CSharp).Should().BeFalse();
sut.IgnoreHeaderComments(LanguageNames.CSharp).Should().BeFalse();
sut.Exclusions.Should().NotBeNull().And.HaveCount(0);
sut.Inclusions.Should().NotBeNull().And.HaveCount(0);
sut.GlobalExclusions.Should().NotBeNull().And.HaveCount(0);
Expand All @@ -110,6 +129,6 @@ private static void AssertArrayContent(string[] array, string folder)
array[1].Should().BeEquivalentTo($"Fake/{folder}/Second*/**/*");
}

private static SonarLintXmlReader CreateSonarLintXmlReader(string relativePath, string language = LanguageNames.CSharp) =>
new(SourceText.From(File.ReadAllText(relativePath)), language);
private static SonarLintXmlReader CreateSonarLintXmlReader(string relativePath) =>
new(SourceText.From(File.ReadAllText(relativePath)));
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
<AnalysisInput>
<Settings>
<Setting>
<Key>sonar.cs.ignoreHeaderComments</Key>
<Value>true</Value>
</Setting>
<Setting>
<Key>sonar.vbnet.ignoreHeaderComments</Key>
<Value>false</Value>
</Setting>
<Setting>
<Key>sonar.cs.analyzeGeneratedCode</Key>
<Value>true</Value>
</Setting>
<Setting>
<Key>sonar.vbnet.analyzeGeneratedCode</Key>
<Value>false</Value>
</Setting>
</Settings>
</AnalysisInput>