public void Generate_Sonar_Rules_Added() { // Arrange var generator = new RuleSetGenerator(); var rules = new[] { CreateRule("csharpsquid", "active1", true, SonarQubeIssueSeverity.Major), // Even though this rule is for VB it will be added as C#, see NOTE below CreateRule("vbnet", "active2", true, SonarQubeIssueSeverity.Major), CreateRule("csharpsquid", "inactive1", false), // Even though this rule is for VB it will be added as C#, see NOTE below CreateRule("vbnet", "inactive2", false), }; // Act var ruleSet = generator.Generate("cs", rules, ValidSonarCSharpProperties); // Assert ruleSet.Rules.Should().HaveCount(1); // NOTE: The RuleNamespace and AnalyzerId are taken from the language parameter of the // Generate method. The FetchArgumentsAndRulesets method will retrieve active/inactive // rules from SonarQube per language/quality profile and mixture of VB-C# rules is not // expected. ruleSet.Rules[0].RuleNamespace.Should().Be("SonarAnalyzer.CSharp"); ruleSet.Rules[0].AnalyzerId.Should().Be("SonarAnalyzer.CSharp"); ruleSet.Rules[0].RuleList.Should().HaveCount(4); ruleSet.Rules[0].RuleList.Select(r => r.Id).Should().BeEquivalentTo( "active1", "active2", "inactive1", "inactive2"); ruleSet.Rules[0].RuleList.Select(r => r.Action).Should().BeEquivalentTo( "Warning", "Warning", "None", "None"); }
public void Generate_ArgumentChecks() { var generator = new RuleSetGenerator(); const string language = "cs"; Action act1 = () => generator.Generate(null, EmptyRules, EmptyProperties); act1.Should().ThrowExactly <ArgumentNullException>().And.ParamName.Should().Be("language"); Action act3 = () => generator.Generate(language, null, EmptyProperties); act3.Should().ThrowExactly <ArgumentNullException>().And.ParamName.Should().Be("rules"); Action act2 = () => generator.Generate(language, EmptyRules, null); act2.Should().ThrowExactly <ArgumentNullException>().And.ParamName.Should().Be("sonarProperties"); }
public void Generate_Common_Parameters() { // Arrange var generator = new RuleSetGenerator(); // Act var ruleSet = generator.Generate("cs", EmptyRules, EmptyProperties); // Assert ruleSet.Description.Should().Be("This rule set was automatically generated from SonarQube"); ruleSet.ToolsVersion.Should().Be("14.0"); ruleSet.Name.Should().Be("Rules for SonarQube"); }
public void Generate_Rules_AreGroupAndSorted() { // Arrange var generator = new RuleSetGenerator(); var properties = new Dictionary <string, string> { // The rules should be grouped by the analyzer id { "sonaranalyzer-cs.analyzerId", "SonarAnalyzer.CSharp" }, { "wintellect.analyzerId", "AAA" }, { "myanalyzer.analyzerId", "ZZZ" }, // The namespace properties are required but shouldn't be used for sorting { "sonaranalyzer-cs.ruleNamespace", "SonarAnalyzer.CSharp" }, { "wintellect.ruleNamespace", "XXX" }, { "myanalyzer.ruleNamespace", "BBB" }, }; var rules = new[] { CreateRule("roslyn.myanalyzer", "my 1", true), CreateRule("roslyn.wintellect", "win2", true), CreateRule("roslyn.wintellect", "win1", true), CreateRule("roslyn.wintellect", "win0", false), CreateRule("csharpsquid", "S999", true), CreateRule("csharpsquid", "S111", false), }; // Act var ruleSet = generator.Generate("cs", rules, properties); // Assert ruleSet.Rules.Should().HaveCount(3); // Expecting groups to be sorted alphabetically by analyzer id (not namespace)... ruleSet.Rules[0].AnalyzerId.Should().Be("AAA"); ruleSet.Rules[1].AnalyzerId.Should().Be("SonarAnalyzer.CSharp"); ruleSet.Rules[2].AnalyzerId.Should().Be("ZZZ"); // ... and rules in groups to be sorted by rule key ruleSet.Rules[0].RuleList[0].Id.Should().Be("win0"); ruleSet.Rules[0].RuleList[1].Id.Should().Be("win1"); ruleSet.Rules[0].RuleList[2].Id.Should().Be("win2"); ruleSet.Rules[1].RuleList[0].Id.Should().Be("S111"); ruleSet.Rules[1].RuleList[1].Id.Should().Be("S999"); ruleSet.Rules[2].RuleList[0].Id.Should().Be("my 1"); }
public void Generate_RuleNamespace_Property_Missing() { // Arrange var generator = new RuleSetGenerator(); var properties = new Dictionary <string, string> { { "sonaranalyzer-cs.analyzerId", "SonarAnalyzer.CSharp" }, }; // Act & Assert var action = new Action(() => generator.Generate("cs", ValidSonarCSharpRules, properties)); action.Should().ThrowExactly <InvalidOperationException>().And .Message.Should().StartWith( "Property does not exist: sonaranalyzer-cs.ruleNamespace. This property should be set by the plugin in SonarQube."); }
public void Generate_Unsupported_Rules_Ignored() { // Arrange var generator = new RuleSetGenerator(); var rules = new[] { CreateRule("other.repo", "other.rule1", true), CreateRule("other.repo", "other.rule2", false), }; // Act var ruleSet = generator.Generate("cs", rules, EmptyProperties); // Assert ruleSet.Rules.Should().BeEmpty(); }
public void Generate_ActiveRules_VsSeverity_IsCorrectlyMapped() { // Arrange var generator = new RuleSetGenerator(); var activeRules = new List <SonarQubeRule> { CreateSonarCSharpRule("rule1", isActive: true, sqSeverity: SonarQubeIssueSeverity.Info), CreateSonarCSharpRule("rule2", isActive: true, sqSeverity: SonarQubeIssueSeverity.Critical) }; // Act var ruleSet = generator.Generate("cs", activeRules, ValidSonarCSharpProperties); // Assert ruleSet.Rules.Should().HaveCount(1); ruleSet.Rules[0].RuleList.Select(r => r.Action).Should().BeEquivalentTo("Info", "Warning"); }
public void Generate_EmptyProperties() { // Arrange var generator = new RuleSetGenerator(); var rules = new List <SonarQubeRule> { CreateRule("repo", "key"), }; // Act var ruleSet = generator.Generate("cs", rules, EmptyProperties); // Assert ruleSet.Rules.Should().BeEmpty(); // No analyzers ruleSet.Description.Should().Be("This rule set was automatically generated from SonarQube"); ruleSet.ToolsVersion.Should().Be("14.0"); ruleSet.Name.Should().Be("Rules for SonarQube"); }
public void Generate_InactiveRules_VSseverity_IsAlwaysNone() { // Arrange var generator = new RuleSetGenerator(); var inactiveRules = new List <SonarQubeRule> { CreateSonarCSharpRule("rule1", isActive: false, sqSeverity: SonarQubeIssueSeverity.Major), CreateSonarCSharpRule("rule2", isActive: false, sqSeverity: SonarQubeIssueSeverity.Info), }; // Act var ruleSet = generator.Generate("cs", inactiveRules, ValidSonarCSharpProperties); // Assert ruleSet.Rules.Should().HaveCount(1); ruleSet.Rules[0].RuleList.Select(r => r.Action).Should().BeEquivalentTo("None", "None"); }
public void Generate_RoslynSDK_Rules_Added() { // Arrange var generator = new RuleSetGenerator(); var properties = new Dictionary <string, string> { ["custom1.analyzerId"] = "CustomAnalyzer1", ["custom1.ruleNamespace"] = "CustomNamespace1", ["custom2.analyzerId"] = "CustomAnalyzer2", ["custom2.ruleNamespace"] = "CustomNamespace2", }; var rules = new[] { CreateRule("roslyn.custom1", "active1", true, SonarQubeIssueSeverity.Info), CreateRule("roslyn.custom2", "active2", true, SonarQubeIssueSeverity.Critical), CreateRule("roslyn.custom1", "inactive1", false), CreateRule("roslyn.custom2", "inactive2", false), }; // Act var ruleSet = generator.Generate("cs", rules, properties); // Assert ruleSet.Rules.Should().HaveCount(2); ruleSet.Rules[0].RuleNamespace.Should().Be("CustomNamespace1"); ruleSet.Rules[0].AnalyzerId.Should().Be("CustomAnalyzer1"); ruleSet.Rules[0].RuleList.Should().HaveCount(2); ruleSet.Rules[0].RuleList.Select(r => r.Id).Should().BeEquivalentTo("active1", "inactive1"); ruleSet.Rules[0].RuleList.Select(r => r.Action).Should().BeEquivalentTo("Info", "None"); ruleSet.Rules[1].RuleNamespace.Should().Be("CustomNamespace2"); ruleSet.Rules[1].AnalyzerId.Should().Be("CustomAnalyzer2"); ruleSet.Rules[1].RuleList.Should().HaveCount(2); ruleSet.Rules[1].RuleList.Select(r => r.Id).Should().BeEquivalentTo("active2", "inactive2"); ruleSet.Rules[1].RuleList.Select(r => r.Action).Should().BeEquivalentTo("Warning", "None"); }
private static void Main(string[] args) { if (args == null || args.Length == 0) { #if DEBUG args = new[] { @"..\..\..\..\.." }; #else args = new string[] { Environment.CurrentDirectory }; #endif } string rootPath = args[0]; StringComparer comparer = StringComparer.InvariantCulture; var metadata = new RoslynatorMetadata(rootPath); ImmutableArray <AnalyzerMetadata> analyzers = metadata.Analyzers; ImmutableArray <AnalyzerMetadata> codeAnalysisAnalyzers = metadata.CodeAnalysisAnalyzers; ImmutableArray <AnalyzerMetadata> formattingAnalyzers = metadata.FormattingAnalyzers; ImmutableArray <RefactoringMetadata> refactorings = metadata.Refactorings; ImmutableArray <CodeFixMetadata> codeFixes = metadata.CodeFixes; ImmutableArray <CompilerDiagnosticMetadata> compilerDiagnostics = metadata.CompilerDiagnostics; WriteCompilationUnit( @"Refactorings\CSharp\RefactoringIdentifiers.Generated.cs", RefactoringIdentifiersGenerator.Generate(refactorings, obsolete: false, comparer: comparer)); WriteCompilationUnit( @"Refactorings\CSharp\RefactoringIdentifiers.Deprecated.Generated.cs", RefactoringIdentifiersGenerator.Generate(refactorings, obsolete: true, comparer: comparer)); WriteCompilationUnit( @"VisualStudio.Common\RefactoringsOptionsPage.Generated.cs", RefactoringsOptionsPageGenerator.Generate(refactorings.Where(f => !f.IsObsolete), comparer)); WriteDiagnostics(@"Analyzers\CSharp", analyzers, @namespace: "Roslynator.CSharp"); WriteDiagnostics(@"CodeAnalysis.Analyzers\CSharp", codeAnalysisAnalyzers, @namespace: "Roslynator.CodeAnalysis.CSharp"); WriteDiagnostics(@"Formatting.Analyzers\CSharp", formattingAnalyzers, @namespace: "Roslynator.Formatting.CSharp"); WriteCompilationUnit( @"CodeFixes\CSharp\CompilerDiagnosticDescriptors.Generated.cs", CompilerDiagnosticDescriptorsGenerator.Generate(compilerDiagnostics, comparer: comparer, @namespace: "Roslynator.CSharp"), normalizeWhitespace: false); WriteCompilationUnit( @"CodeFixes\CSharp\CodeFixDescriptors.Generated.cs", CodeFixDescriptorsGenerator.Generate(codeFixes.Where(f => !f.IsObsolete), comparer: comparer, @namespace: "Roslynator.CSharp"), normalizeWhitespace: false); WriteCompilationUnit( @"CodeFixes\CSharp\CodeFixIdentifiers.Generated.cs", CodeFixIdentifiersGenerator.Generate(codeFixes, comparer)); WriteCompilationUnit( @"VisualStudio.Common\CodeFixesOptionsPage.Generated.cs", CodeFixesOptionsPageGenerator.Generate(codeFixes, comparer)); WriteCompilationUnit( @"CSharp\CSharp\CompilerDiagnosticIdentifiers.Generated.cs", CompilerDiagnosticIdentifiersGenerator.Generate(compilerDiagnostics, comparer)); WriteCompilationUnit( @"Tools\CodeGeneration\CSharp\Symbols.Generated.cs", SymbolsGetKindsGenerator.Generate()); WriteCompilationUnit( @"CSharp\CSharp\SyntaxWalkers\CSharpSyntaxNodeWalker.cs", CSharpSyntaxNodeWalkerGenerator.Generate()); string ruleSetXml = File.ReadAllText(Path.Combine(rootPath, @"Tools\CodeGeneration\DefaultRuleSet.xml")); WriteCompilationUnit( @"VisualStudio.Common\RuleSetHelpers.Generated.cs", RuleSetGenerator.Generate(ruleSetXml)); File.WriteAllText( Path.Combine(rootPath, @"VisualStudioCode\package\src\configurationFiles.generated.ts"), @"export const configurationFileContent = { ruleset: `" + ruleSetXml + @"`, config: `<?xml version=""1.0"" encoding=""utf-8""?> <Roslynator> <Settings> <General> <!-- <PrefixFieldIdentifierWithUnderscore>true</PrefixFieldIdentifierWithUnderscore> --> </General> <Refactorings> <!-- <Refactoring Id=""RRxxxx"" IsEnabled=""false"" /> --> </Refactorings> <CodeFixes> <!-- <CodeFix Id=""CSxxxx.RCFxxxx"" IsEnabled=""false"" /> --> <!-- <CodeFix Id=""CSxxxx"" IsEnabled=""false"" /> --> <!-- <CodeFix Id=""RCFxxxx"" IsEnabled=""false"" /> --> </CodeFixes> </Settings> </Roslynator>` };", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); Console.WriteLine($"number of analyzers: {analyzers.Count(f => !f.IsObsolete)}"); Console.WriteLine($"number of code analysis analyzers: {codeAnalysisAnalyzers.Count(f => !f.IsObsolete)}"); Console.WriteLine($"number of formatting analyzers: {formattingAnalyzers.Count(f => !f.IsObsolete)}"); Console.WriteLine($"number of refactorings: {refactorings.Length}"); Console.WriteLine($"number of code fixes: {codeFixes.Length}"); Console.WriteLine($"number of fixable compiler diagnostics: {codeFixes.SelectMany(f => f.FixableDiagnosticIds).Distinct().Count()}"); void WriteDiagnostics( string dirPath, ImmutableArray <AnalyzerMetadata> analyzers, string @namespace, string descriptorsClassName = "DiagnosticDescriptors", string identifiersClassName = "DiagnosticIdentifiers") { WriteCompilationUnit( Path.Combine(dirPath, $"{descriptorsClassName}.Generated.cs"), DiagnosticDescriptorsGenerator.Generate(analyzers, obsolete: false, comparer: comparer, @namespace: @namespace, className: descriptorsClassName, identifiersClassName: identifiersClassName), normalizeWhitespace: false); WriteCompilationUnit( Path.Combine(dirPath, $"{descriptorsClassName}.Deprecated.Generated.cs"), DiagnosticDescriptorsGenerator.Generate(analyzers, obsolete: true, comparer: comparer, @namespace: @namespace, className: descriptorsClassName, identifiersClassName: identifiersClassName), normalizeWhitespace: false); WriteCompilationUnit( Path.Combine(dirPath, $"{identifiersClassName}.Generated.cs"), DiagnosticIdentifiersGenerator.Generate(analyzers, obsolete: false, comparer: comparer, @namespace: @namespace, className: identifiersClassName)); WriteCompilationUnit( Path.Combine(dirPath, $"{identifiersClassName}.Deprecated.Generated.cs"), DiagnosticIdentifiersGenerator.Generate(analyzers, obsolete: true, comparer: comparer, @namespace: @namespace, className: identifiersClassName)); IEnumerable <AnalyzerMetadata> optionAnalyzers = analyzers.SelectMany(f => f.OptionAnalyzers); if (optionAnalyzers.Any()) { WriteCompilationUnit( Path.Combine(dirPath, "AnalyzerOptions.Generated.cs"), DiagnosticDescriptorsGenerator.Generate(optionAnalyzers, obsolete: false, comparer: comparer, @namespace: @namespace, className: "AnalyzerOptions", identifiersClassName: "AnalyzerOptionIdentifiers"), normalizeWhitespace: false, fileMustExist: false); WriteCompilationUnit( Path.Combine(dirPath, "AnalyzerOptionIdentifiers.Generated.cs"), DiagnosticIdentifiersGenerator.Generate(optionAnalyzers, obsolete: false, comparer: comparer, @namespace: @namespace, className: "AnalyzerOptionIdentifiers"), fileMustExist: false); } IEnumerable <string> analyzerOptionIdentifiers = analyzers .SelectMany(f => f.OptionAnalyzers) .Select(f => f.Identifier); WriteCompilationUnit( Path.Combine(dirPath, "AnalyzerOptionsAnalyzer.Generated.cs"), AnalyzerOptionsAnalyzerGenerator.Generate(analyzerOptionIdentifiers, @namespace: @namespace), fileMustExist: false); } void WriteCompilationUnit( string path, CompilationUnitSyntax compilationUnit, bool autoGenerated = true, bool normalizeWhitespace = true, bool fileMustExist = true, bool overwrite = true) { CodeGenerationHelpers.WriteCompilationUnit( path: Path.Combine(rootPath, path), compilationUnit: compilationUnit, banner: "Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.", autoGenerated: autoGenerated, normalizeWhitespace: normalizeWhitespace, fileMustExist: fileMustExist, overwrite: overwrite); } }