Skip to content

Commit

Permalink
Use SonarLint data class to provide the rule parameters to the rules (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
cristian-ambrosini-sonarsource authored and csaba-sagi-sonarsource committed Mar 16, 2023
1 parent c0e49e9 commit 90bfd28
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 172 deletions.
Expand Up @@ -32,7 +32,7 @@ protected sealed override void Initialize(SonarAnalysisContext context)
context.RegisterCompilationStartAction(
c =>
{
ParameterLoader.SetParameterValues(this, c.Options);
ParameterLoader.SetParameterValues(this, c.SonarLintFile());
parameterContext.ExecutePostponedActions(c);
});
}
Expand Down
85 changes: 7 additions & 78 deletions analyzers/src/SonarAnalyzer.Common/Helpers/ParameterLoader.cs
Expand Up @@ -19,10 +19,7 @@
*/

using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;

namespace SonarAnalyzer.Helpers
{
Expand All @@ -39,84 +36,30 @@ internal static class ParameterLoader
* - diffing the contents of the configuration file
* - associating the file with a unique identifier for the build project
*/
internal static void SetParameterValues(ParametrizedDiagnosticAnalyzer parameteredAnalyzer,
AnalyzerOptions options)
internal static void SetParameterValues(ParametrizedDiagnosticAnalyzer parameteredAnalyzer, SonarLintXmlReader sonarLintXml)
{
var sonarLintXml = options.SonarLintXml();
if (sonarLintXml == null)
{
return;
}

var parameters = ParseParameters(sonarLintXml);
if (parameters.IsEmpty)
if (!sonarLintXml.ParametrizedRules.Any())
{
return;
}

var propertyParameterPairs = parameteredAnalyzer.GetType()
.GetRuntimeProperties()
.Select(p => new { Property = p, Descriptor = p.GetCustomAttributes<RuleParameterAttribute>().SingleOrDefault() })
.Where(p => p.Descriptor != null);
.Select(x => new { Property = x, Descriptor = x.GetCustomAttributes<RuleParameterAttribute>().SingleOrDefault() })
.Where(x => x.Descriptor is not null);

var ids = new HashSet<string>(parameteredAnalyzer.SupportedDiagnostics.Select(diagnostic => diagnostic.Id));
foreach (var propertyParameterPair in propertyParameterPairs)
{
var parameter = parameters
.FirstOrDefault(p => ids.Contains(p.RuleId));

var parameterValue = parameter?.ParameterValues
.FirstOrDefault(pv => pv.ParameterKey == propertyParameterPair.Descriptor.Key);

if (TryConvertToParameterType(parameterValue?.ParameterValue, propertyParameterPair.Descriptor.Type, out var value))
var parameter = sonarLintXml.ParametrizedRules.FirstOrDefault(x => ids.Contains(x.Key));
var parameterValue = parameter?.Parameters.FirstOrDefault(x => x.Key == propertyParameterPair.Descriptor.Key);
if (TryConvertToParameterType(parameterValue?.Value, propertyParameterPair.Descriptor.Type, out var value))
{
propertyParameterPair.Property.SetValue(parameteredAnalyzer, value);
}
}
}

private static ImmutableList<RuleParameterValues> ParseParameters(AdditionalText sonarLintXml)
{
try
{
var xml = XDocument.Parse(sonarLintXml.GetText().ToString());
return ParseParameters(xml);
}
catch (Exception ex) when (ex is IOException || ex is XmlException)
{
// cannot log exception
return ImmutableList.Create<RuleParameterValues>();
}
}

private static ImmutableList<RuleParameterValues> ParseParameters(XContainer xml)
{
var builder = ImmutableList.CreateBuilder<RuleParameterValues>();
foreach (var rule in xml.Descendants("Rule").Where(e => e.Elements("Parameters").Any()))
{
var analyzerId = rule.Elements("Key").Single().Value;

var parameterValues = rule
.Elements("Parameters").Single()
.Elements("Parameter")
.Select(e => new RuleParameterValue
{
ParameterKey = e.Elements("Key").Single().Value,
ParameterValue = e.Elements("Value").Single().Value
});

var pvs = new RuleParameterValues
{
RuleId = analyzerId
};
pvs.ParameterValues.AddRange(parameterValues);

builder.Add(pvs);
}

return builder.ToImmutable();
}

private static bool TryConvertToParameterType(string parameter, PropertyType type, out object result)
{
if (parameter == null)
Expand Down Expand Up @@ -144,19 +87,5 @@ private static bool TryConvertToParameterType(string parameter, PropertyType typ
return false;
}
}

private sealed class RuleParameterValues
{
public string RuleId { get; set; }

public List<RuleParameterValue> ParameterValues { get; } = new List<RuleParameterValue>();
}

private sealed class RuleParameterValue
{
public string ParameterKey { get; set; }

public string ParameterValue { get; set; }
}
}
}
28 changes: 18 additions & 10 deletions analyzers/src/SonarAnalyzer.Common/Helpers/SonarLintXmlReader.cs
Expand Up @@ -34,30 +34,33 @@ public class SonarLintXmlReader
private readonly string propertyLanguage;

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

private bool? analyzeGeneratedCode;
public bool AnalyzeGeneratedCode => analyzeGeneratedCode ??= ReadBoolean(ReadProperty($"sonar.{propertyLanguage}.analyzeGeneratedCode"));
public bool AnalyzeGeneratedCode => analyzeGeneratedCode ??= ReadBoolean(ReadSettingsProperty($"sonar.{propertyLanguage}.analyzeGeneratedCode"));

private string[] exclusions;
public string[] Exclusions => exclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.exclusions"));
public string[] Exclusions => exclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.exclusions"));

private string[] inclusions;
public string[] Inclusions => inclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.inclusions"));
public string[] Inclusions => inclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.inclusions"));

private string[] globalExclusions;
public string[] GlobalExclusions => globalExclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.global.exclusions"));
public string[] GlobalExclusions => globalExclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.global.exclusions"));

private string[] testExclusions;
public string[] TestExclusions => testExclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.test.exclusions"));
public string[] TestExclusions => testExclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.test.exclusions"));

private string[] testInclusions;
public string[] TestInclusions => testInclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.test.inclusions"));
public string[] TestInclusions => testInclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.test.inclusions"));

private string[] globalTestExclusions;
public string[] GlobalTestExclusions => globalTestExclusions ??= ReadCommaSeparatedArray(ReadProperty("sonar.global.test.exclusions"));
public string[] GlobalTestExclusions => globalTestExclusions ??= ReadCommaSeparatedArray(ReadSettingsProperty("sonar.global.test.exclusions"));

public SonarLintXmlReader(SourceText sonarLintXml, string language)
private List<SonarLintXmlRule> parametrizedRules;
public List<SonarLintXmlRule> ParametrizedRules => parametrizedRules ??= ReadRuleParameters();

public SonarLintXmlReader(SourceText sonarLintXml, string language = LanguageNames.CSharp)
{
this.sonarLintXml = sonarLintXml == null ? SonarLintXml.Empty : ParseContent(sonarLintXml);
propertyLanguage = language == LanguageNames.CSharp ? "cs" : "vbnet";
Expand All @@ -80,7 +83,12 @@ private static SonarLintXml ParseContent(SourceText sonarLintXml)
}
}

private string ReadProperty(string property) =>
private List<SonarLintXmlRule> ReadRuleParameters() =>
sonarLintXml is { Rules: { } rules }
? rules.Where(x => x.Parameters.Any()).ToList()
: new();

private string ReadSettingsProperty(string property) =>
sonarLintXml is { Settings: { } settings }
? settings.Where(x => x.Key.Equals(property)).Select(x => x.Value).FirstOrDefault()
: string.Empty;
Expand Down

0 comments on commit 90bfd28

Please sign in to comment.