Skip to content

Commit

Permalink
Remove language property from SonarLintXmlReader (#6905)
Browse files Browse the repository at this point in the history
  • Loading branch information
mary-georgiou-sonarsource authored and csaba-sagi-sonarsource committed Mar 16, 2023
1 parent 04495f3 commit a167f88
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 36 deletions.
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));
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));

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 @@ -22,6 +22,8 @@ namespace SonarAnalyzer.Common
{
public sealed class UnexpectedLanguageException : Exception
{
public UnexpectedLanguageException(AnalyzerLanguage language) : base($"Unexpected language: {language}") { }
public UnexpectedLanguageException(AnalyzerLanguage language) : this(language.LanguageName) { }

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) =>
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 @@ -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 @@ -25,6 +25,10 @@ namespace SonarAnalyzer.UnitTest.Common
[TestClass]
public class UnexpectedLanguageExceptionTest
{
[TestMethod]
public void Message_String_Ctor() =>
new UnexpectedLanguageException("F#").Message.Should().Be("Unexpected language: F#");

[TestMethod]
public void Message_CS() =>
new UnexpectedLanguageException(AnalyzerLanguage.CSharp).Message.Should().Be("Unexpected language: C#");
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"?>
<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>

0 comments on commit a167f88

Please sign in to comment.