From e66f0e7408902a03bf5bc2c8c3b5fb994dce47d8 Mon Sep 17 00:00:00 2001 From: Erik Ejlskov Jensen Date: Thu, 29 Feb 2024 08:43:09 +0100 Subject: [PATCH] Add ability to treat rule violations as errors (#523) * Add ability to treat rule violations as errors fixes #522 * PR updates --- README.md | 18 +++++++++++++++--- src/DacpacTool/PackageAnalyzer.cs | 13 +++++++++++-- src/DacpacTool/SqlRuleProblemExtensions.cs | 12 ++++++++++-- test/DacpacTool.Tests/PackageAnalyzerTests.cs | 10 ++++++---- .../TestProjectWithAnalyzers.csproj | 2 +- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0b67c007..ff50c06d 100644 --- a/README.md +++ b/README.md @@ -536,7 +536,7 @@ Starting with version 2.7.0 of the SDK, there is support for running static code - [SqlServer.Rules](https://github.com/tcartwright/SqlServer.Rules/blob/master/docs/table_of_contents.md) - [Smells](https://github.com/davebally/TSQL-Smells) -Static code analysis can be enabled by adding the following to the project file: +Static code analysis can be enabled by adding the `RunSqlCodeAnalysis` property to the project file: ```xml @@ -546,11 +546,23 @@ Static code analysis can be enabled by adding the following to the project file: ``` + +A xml file with the analysis results is created in the output folder. + The optional `CodeAnalysisRules` property allows you to disable individual rules or groups of rules. -A xml file with the analysis results is created in the output folder, and any problems found during analysis are reported as build warnings. +Any rule violations found during analysis are reported as build warnings. + +Individual rule violations can be configured to be reported as build errors as shown below. -`TreatWarningsAsErrors` does not affect found problems. +```xml + + + True + +!SqlServer.Rules.SRN0005 + + +``` ## Workaround for parser errors (SQL46010) This project relies on the publicly available T-SQL parser which may not support all T-SQL syntax constructions. Therefore you might encounter a SQL46010 error if you have a script file that contains unsupported syntax. If that happens, there's a couple of workarounds you can try: diff --git a/src/DacpacTool/PackageAnalyzer.cs b/src/DacpacTool/PackageAnalyzer.cs index 99ed6da2..8f3af980 100644 --- a/src/DacpacTool/PackageAnalyzer.cs +++ b/src/DacpacTool/PackageAnalyzer.cs @@ -12,6 +12,7 @@ public sealed class PackageAnalyzer private readonly IConsole _console; private readonly HashSet _ignoredRules = new(); private readonly HashSet _ignoredRuleSets = new(); + private readonly HashSet _errorRuleSets = new(); public PackageAnalyzer(IConsole console, string rulesExpression) { @@ -49,7 +50,7 @@ public void Analyze(TSqlModel model, FileInfo outputFile) { foreach (var err in result.Problems) { - _console.WriteLine(err.GetOutputMessage()); + _console.WriteLine(err.GetOutputMessage(_errorRuleSets)); } result.SerializeResultsToXml(GetOutputFileName(outputFile)); @@ -72,7 +73,7 @@ private void BuildRuleLists(string rulesExpression) .StartsWith("-", StringComparison.OrdinalIgnoreCase) && rule.Length > 1)) { - if (rule.EndsWith("*", StringComparison.OrdinalIgnoreCase)) + if (rule.Length > 2 && rule.EndsWith("*", StringComparison.OrdinalIgnoreCase)) { _ignoredRuleSets.Add(rule[1..^1]); } @@ -81,6 +82,14 @@ private void BuildRuleLists(string rulesExpression) _ignoredRules.Add(rule[1..]); } } + foreach (var rule in rulesExpression.Split(new[] { ';' }, + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(rule => rule + .StartsWith("+!", StringComparison.OrdinalIgnoreCase) + && rule.Length > 2)) + { + _errorRuleSets.Add(rule[2..]); + } } } diff --git a/src/DacpacTool/SqlRuleProblemExtensions.cs b/src/DacpacTool/SqlRuleProblemExtensions.cs index 665117be..07f50623 100644 --- a/src/DacpacTool/SqlRuleProblemExtensions.cs +++ b/src/DacpacTool/SqlRuleProblemExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using Microsoft.SqlServer.Dac.CodeAnalysis; @@ -9,9 +10,16 @@ namespace MSBuild.Sdk.SqlProj.DacpacTool /// public static class SqlRuleProblemExtensions { - public static string GetOutputMessage(this SqlRuleProblem sqlRuleProblem) + public static string GetOutputMessage(this SqlRuleProblem sqlRuleProblem, HashSet errorRules) { ArgumentNullException.ThrowIfNull(sqlRuleProblem); + + SqlRuleProblemSeverity sqlRuleProblemSeverity = sqlRuleProblem.Severity; + + if (errorRules.Contains(sqlRuleProblem.RuleId)) + { + sqlRuleProblemSeverity = SqlRuleProblemSeverity.Error; + } var stringBuilder = new StringBuilder(); stringBuilder.Append(sqlRuleProblem.SourceName); @@ -21,7 +29,7 @@ public static string GetOutputMessage(this SqlRuleProblem sqlRuleProblem) stringBuilder.Append(sqlRuleProblem.StartColumn); stringBuilder.Append("):"); stringBuilder.Append(' '); - stringBuilder.Append(sqlRuleProblem.Severity); + stringBuilder.Append(sqlRuleProblemSeverity); stringBuilder.Append(' '); stringBuilder.Append(sqlRuleProblem.ErrorMessageString); diff --git a/test/DacpacTool.Tests/PackageAnalyzerTests.cs b/test/DacpacTool.Tests/PackageAnalyzerTests.cs index 2c13e317..9ea15d96 100644 --- a/test/DacpacTool.Tests/PackageAnalyzerTests.cs +++ b/test/DacpacTool.Tests/PackageAnalyzerTests.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using Microsoft.SqlServer.Dac.Model; using Microsoft.VisualStudio.TestTools.UnitTesting; using Shouldly; @@ -38,7 +39,7 @@ public void RunsAnalyzerWithSupressions() var testConsole = (TestConsole)_console; testConsole.Lines.Clear(); var result = BuildSimpleModel(); - var packageAnalyzer = new PackageAnalyzer(_console, "-SqlServer.Rules.SRD0006;-Smells.SML005;-SqlServer.Rules.SRD999;;"); + var packageAnalyzer = new PackageAnalyzer(_console, "-SqlServer.Rules.SRD0006;-Smells.SML005;-SqlServer.Rules.SRD999;+!SqlServer.Rules.SRN0002;"); // Act packageAnalyzer.Analyze(result.model, result.fileInfo); @@ -47,8 +48,9 @@ public void RunsAnalyzerWithSupressions() testConsole.Lines.Count.ShouldBe(13); testConsole.Lines.ShouldContain($"Analyzing package '{result.fileInfo.FullName}'"); - testConsole.Lines.ShouldNotContain($"SRD0006"); - testConsole.Lines.ShouldNotContain($"SML005"); + testConsole.Lines.Any(l => l.Contains("SRD0006")).ShouldBeFalse(); + testConsole.Lines.Any(l => l.Contains("SML0005")).ShouldBeFalse(); + testConsole.Lines.Any(l => l.Contains("Error SRN0002")).ShouldBeTrue(); testConsole.Lines.ShouldContain($"Successfully analyzed package '{result.fileInfo.FullName}'"); } @@ -68,7 +70,7 @@ public void RunsAnalyzerWithWildcardSupressions() testConsole.Lines.Count.ShouldBe(13); testConsole.Lines.ShouldContain($"Analyzing package '{result.fileInfo.FullName}'"); - testConsole.Lines.ShouldNotContain($"SRD"); + testConsole.Lines.Any(l => l.Contains("SRD")).ShouldBeFalse(); testConsole.Lines.ShouldContain($"Successfully analyzed package '{result.fileInfo.FullName}'"); } diff --git a/test/TestProjectWithAnalyzers/TestProjectWithAnalyzers.csproj b/test/TestProjectWithAnalyzers/TestProjectWithAnalyzers.csproj index 969f34cb..eb0bced9 100644 --- a/test/TestProjectWithAnalyzers/TestProjectWithAnalyzers.csproj +++ b/test/TestProjectWithAnalyzers/TestProjectWithAnalyzers.csproj @@ -5,7 +5,7 @@ netstandard2.0 Sql150 True - -SqlServer.Rules.SRD0006;-Smells.* + -SqlServer.Rules.SRD0006;-Smells.*;+!SqlServer.Rules.SRN0002