Skip to content

Commit afee469

Browse files
authored
Merge pull request #6427 from mavasani/CodeAnalysisTreatWarningsAsErrors_GlobalConfigs
Re-implement CodeAnalysisTreatWarningsAsErrors with globalconfig files
2 parents b858999 + 1d7244a commit afee469

File tree

3 files changed

+57
-63
lines changed

3 files changed

+57
-63
lines changed

src/Tools/GenerateAnalyzerNuspec/Program.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,15 @@
235235

236236
if (globalAnalyzerConfigsDir.Length > 0 && Directory.Exists(globalAnalyzerConfigsDir))
237237
{
238-
foreach (string editorconfig in Directory.EnumerateFiles(globalAnalyzerConfigsDir))
238+
foreach (string globalconfig in Directory.EnumerateFiles(globalAnalyzerConfigsDir))
239239
{
240-
if (Path.GetExtension(editorconfig) == ".editorconfig")
240+
if (Path.GetExtension(globalconfig) == ".globalconfig")
241241
{
242-
result.AppendLine(FileElement(Path.Combine(globalAnalyzerConfigsDir, editorconfig), $"build\\config"));
242+
result.AppendLine(FileElement(Path.Combine(globalAnalyzerConfigsDir, globalconfig), $"buildtransitive\\config"));
243243
}
244244
else
245245
{
246-
throw new InvalidDataException($"Encountered a file with unexpected extension: {editorconfig}");
246+
throw new InvalidDataException($"Encountered a file with unexpected extension: {globalconfig}");
247247
}
248248
}
249249
}

src/Tools/GenerateDocumentationAndConfigFiles/Program.cs

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ void createPropsFiles()
252252

253253
var fileContents =
254254
$@"<Project>
255-
{disableNetAnalyzersImport}{getCodeAnalysisTreatWarningsAsErrors()}{getCompilerVisibleProperties()}
255+
{disableNetAnalyzersImport}{getCompilerVisibleProperties()}
256256
</Project>";
257257
var directory = Directory.CreateDirectory(propsFileDir);
258258
var fileWithPath = Path.Combine(directory.FullName, propsFileName);
@@ -310,20 +310,6 @@ We rely on the additional props file '{propsFileToDisableNetAnalyzersInNuGetPack
310310
}
311311
}
312312

313-
string getCodeAnalysisTreatWarningsAsErrors()
314-
{
315-
var allRuleIds = string.Join(';', allRulesById.Keys);
316-
return $@"
317-
<!--
318-
This property group handles 'CodeAnalysisTreatWarningsAsErrors' for the CA rule ids implemented in this package.
319-
-->
320-
<PropertyGroup>
321-
<CodeAnalysisRuleIds>{allRuleIds}</CodeAnalysisRuleIds>
322-
<WarningsNotAsErrors Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'false'"">$(WarningsNotAsErrors);$(CodeAnalysisRuleIds)</WarningsNotAsErrors>
323-
<WarningsAsErrors Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'true' and '$(TreatWarningsAsErrors)' != 'true'"">$(WarningsAsErrors);$(CodeAnalysisRuleIds)</WarningsAsErrors>
324-
</PropertyGroup>";
325-
}
326-
327313
string getCompilerVisibleProperties()
328314
{
329315
return analyzerPackageName switch
@@ -794,12 +780,15 @@ void CreateGlobalConfigsForVersion(
794780
{
795781
var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix(version);
796782

797-
foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode)))
783+
foreach (var warnAsError in new[] { true, false })
798784
{
799-
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, releaseTrackingData, category: null);
800-
foreach (var category in categories!)
785+
foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode)))
801786
{
802-
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, releaseTrackingData, category);
787+
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, warnAsError, releaseTrackingData, category: null);
788+
foreach (var category in categories!)
789+
{
790+
CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, warnAsError, releaseTrackingData, category);
791+
}
803792
}
804793
}
805794
}
@@ -809,26 +798,38 @@ void CreateGlobalConfig(
809798
bool isShippedVersion,
810799
string analysisLevelVersionString,
811800
AnalysisMode analysisMode,
801+
bool warnAsError,
812802
ImmutableArray<ReleaseTrackingData> releaseTrackingData,
813803
string? category)
814804
{
815805
var analysisLevelPropName = "AnalysisLevel";
816806
var title = $"Rules from '{version}' release with '{analysisMode}' analysis mode";
817807
var description = $"Rules with enabled-by-default state from '{version}' release with '{analysisMode}' analysis mode. Rules that are first released in a version later than '{version}' are disabled.";
808+
818809
if (category != null)
819810
{
820811
analysisLevelPropName += category;
821812
title = $"'{category}' {title}";
822813
description = $"'{category}' {description}";
823814
}
824815

825-
CreateGlobalconfig(
826-
analyzerGlobalconfigsDir,
827816
#pragma warning disable CA1308 // Normalize strings to uppercase
828-
$"{analysisLevelPropName}_{analysisLevelVersionString}_{analysisMode!.ToString()!.ToLowerInvariant()}.editorconfig",
817+
var globalconfigFileName = $"{analysisLevelPropName}_{analysisLevelVersionString}_{analysisMode!.ToString()!.ToLowerInvariant()}";
829818
#pragma warning restore CA1308 // Normalize strings to uppercase
830-
title,
819+
820+
if (warnAsError)
821+
{
822+
globalconfigFileName += "_warnaserror";
823+
title += " escalated to 'error' severity";
824+
description += " Enabled rules with 'warning' severity are escalated to 'error' severity to respect 'CodeAnalysisTreatWarningsAsErrors' MSBuild property.";
825+
}
826+
827+
CreateGlobalconfig(
828+
analyzerGlobalconfigsDir,
829+
$"{globalconfigFileName}.globalconfig",
830+
title,
831831
description,
832+
warnAsError,
832833
analysisMode,
833834
category,
834835
allRulesById,
@@ -1158,34 +1159,37 @@ private static void Validate(string fileWithPath, string fileContents, List<stri
11581159

11591160
private static void CreateGlobalconfig(
11601161
string folder,
1161-
string editorconfigFileName,
1162-
string editorconfigTitle,
1163-
string editorconfigDescription,
1162+
string fileName,
1163+
string title,
1164+
string description,
1165+
bool warnAsError,
11641166
AnalysisMode analysisMode,
11651167
string? category,
11661168
SortedList<string, DiagnosticDescriptor> sortedRulesById,
11671169
(ImmutableArray<ReleaseTrackingData> releaseTrackingData, Version version, bool isShippedVersion) releaseTrackingDataAndVersion)
11681170
{
1169-
Debug.Assert(editorconfigFileName.EndsWith(".editorconfig", StringComparison.Ordinal));
1171+
Debug.Assert(fileName.EndsWith(".globalconfig", StringComparison.Ordinal));
11701172

11711173
var text = GetGlobalconfigText(
1172-
editorconfigTitle,
1173-
editorconfigDescription,
1174+
title,
1175+
description,
1176+
warnAsError,
11741177
analysisMode,
11751178
category,
11761179
sortedRulesById,
11771180
releaseTrackingDataAndVersion);
11781181
var directory = Directory.CreateDirectory(folder);
11791182
#pragma warning disable CA1308 // Normalize strings to uppercase - Need to use 'ToLowerInvariant' for file names in non-Windows platforms
1180-
var editorconfigFilePath = Path.Combine(directory.FullName, editorconfigFileName.ToLowerInvariant());
1183+
var configFilePath = Path.Combine(directory.FullName, fileName.ToLowerInvariant());
11811184
#pragma warning restore CA1308 // Normalize strings to uppercase
1182-
File.WriteAllText(editorconfigFilePath, text);
1185+
File.WriteAllText(configFilePath, text);
11831186
return;
11841187

11851188
// Local functions
11861189
static string GetGlobalconfigText(
1187-
string editorconfigTitle,
1188-
string editorconfigDescription,
1190+
string title,
1191+
string description,
1192+
bool warnAsError,
11891193
AnalysisMode analysisMode,
11901194
string? category,
11911195
SortedList<string, DiagnosticDescriptor> sortedRulesById,
@@ -1200,8 +1204,8 @@ void StartGlobalconfig()
12001204
{
12011205
result.AppendLine(@"# NOTE: Requires **VS2019 16.7** or later");
12021206
result.AppendLine();
1203-
result.AppendLine($@"# {editorconfigTitle}");
1204-
result.AppendLine($@"# Description: {editorconfigDescription}");
1207+
result.AppendLine($@"# {title}");
1208+
result.AppendLine($@"# Description: {description}");
12051209
result.AppendLine();
12061210
result.AppendLine($@"is_global = true");
12071211
result.AppendLine();
@@ -1240,6 +1244,11 @@ bool AddRule(DiagnosticDescriptor rule, string? category)
12401244
}
12411245

12421246
var (isEnabledByDefault, severity) = GetEnabledByDefaultAndSeverity(rule, analysisMode);
1247+
if (warnAsError && severity == DiagnosticSeverity.Warning && isEnabledByDefault)
1248+
{
1249+
severity = DiagnosticSeverity.Error;
1250+
}
1251+
12431252
if (rule.IsEnabledByDefault == isEnabledByDefault &&
12441253
severity == rule.DefaultSeverity)
12451254
{
@@ -1395,7 +1404,6 @@ static string GetCommonContents(string packageName, IOrderedEnumerable<string> c
13951404
}
13961405

13971406
stringBuilder.Append(GetMSBuildContentForPropertyAndItemOptions());
1398-
stringBuilder.Append(GetCodeAnalysisTreatWarningsAsErrorsTargetContents());
13991407
return stringBuilder.ToString();
14001408
}
14011409

@@ -1443,8 +1451,14 @@ static string GetGlobalAnalyzerConfigTargetContents(string packageName, string?
14431451
<_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition=""'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == 'AllDisabledByDefault'"">{nameof(AnalysisMode.None)}</_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName}>
14441452
<_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition=""'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == ''"">{nameof(AnalysisMode.Default)}</_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName}>
14451453
1454+
<!-- Default 'EffectiveCodeAnalysisTreatWarningsAsErrors' to 'CodeAnalysisTreatWarningsAsErrors' for escalating relevant code analysis warnings to errors. -->
1455+
<!-- We use a separate property to allow users to override 'CodeAnalysisTreatWarningsAsErrors' implementation from .NET7 or older SDK, which had a known issue: https://github.com/dotnet/roslyn-analyzers/issues/6281 -->
1456+
<EffectiveCodeAnalysisTreatWarningsAsErrors Condition=""'$(EffectiveCodeAnalysisTreatWarningsAsErrors)' == ''"">$(CodeAnalysisTreatWarningsAsErrors)</EffectiveCodeAnalysisTreatWarningsAsErrors>
1457+
<!-- Choose GlobalAnalyzerConfig file with '_warnaserror' suffix if 'EffectiveCodeAnalysisTreatWarningsAsErrors' is 'true'. -->
1458+
<_GlobalAnalyzerConfigFileName_{trimmedPackageName}_WarnAsErrorSuffix Condition=""'$(EffectiveCodeAnalysisTreatWarningsAsErrors)' == 'true'"">_warnaserror</_GlobalAnalyzerConfigFileName_{trimmedPackageName}_WarnAsErrorSuffix>
1459+
14461460
<!-- GlobalAnalyzerConfig file name based on user specified package version '{packageVersionPropName}', if any. We replace '.' with '_' to map the version string to file name suffix. -->
1447-
<_GlobalAnalyzerConfigFileName_{trimmedPackageName} Condition=""'$({packageVersionPropName})' != ''"">{analysisLevelPropName}_$({packageVersionPropName}.Replace(""."",""_""))_$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName}).editorconfig</_GlobalAnalyzerConfigFileName_{trimmedPackageName}>
1461+
<_GlobalAnalyzerConfigFileName_{trimmedPackageName} Condition=""'$({packageVersionPropName})' != ''"">{analysisLevelPropName}_$({packageVersionPropName}.Replace(""."",""_""))_$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})$(_GlobalAnalyzerConfigFileName_{trimmedPackageName}_WarnAsErrorSuffix).globalconfig</_GlobalAnalyzerConfigFileName_{trimmedPackageName}>
14481462
<_GlobalAnalyzerConfigFileName_{trimmedPackageName}>$(_GlobalAnalyzerConfigFileName_{trimmedPackageName}.ToLowerInvariant())</_GlobalAnalyzerConfigFileName_{trimmedPackageName}>
14491463
14501464
<_GlobalAnalyzerConfigDir_{trimmedPackageName} Condition=""'$(_GlobalAnalyzerConfigDir_{trimmedPackageName})' == ''"">$(MSBuildThisFileDirectory)config</_GlobalAnalyzerConfigDir_{trimmedPackageName}>
@@ -1580,23 +1594,6 @@ static void AddMSBuildContentForItemOptions(StringBuilder builder)
15801594
}
15811595
}
15821596

1583-
static string GetCodeAnalysisTreatWarningsAsErrorsTargetContents()
1584-
{
1585-
return $@"
1586-
<!--
1587-
Design-time target to handle 'CodeAnalysisTreatWarningsAsErrors' for the CA rule ids implemented in this package.
1588-
Note that similar 'WarningsNotAsErrors' and 'WarningsAsErrors'
1589-
property groups are present in the generated props file to ensure this functionality on command line builds.
1590-
-->
1591-
<Target Name=""_CodeAnalysisTreatWarningsAsErrors"" BeforeTargets=""CoreCompile"" Condition=""'$(DesignTimeBuild)' == 'true' OR '$(BuildingProject)' != 'true'"">
1592-
<PropertyGroup>
1593-
<WarningsNotAsErrors Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'false'"">$(WarningsNotAsErrors);$(CodeAnalysisRuleIds)</WarningsNotAsErrors>
1594-
<WarningsAsErrors Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'true' and '$(TreatWarningsAsErrors)' != 'true'"">$(WarningsAsErrors);$(CodeAnalysisRuleIds)</WarningsAsErrors>
1595-
</PropertyGroup>
1596-
</Target>
1597-
";
1598-
}
1599-
16001597
static string GetPackageSpecificContents(string packageName)
16011598
=> packageName switch
16021599
{

src/Tools/GenerateDocumentationAndConfigFiles/README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,5 @@ Following are the precedence rules as per the values of these properties:
2121

2222
2. For CAxxxx rules:
2323

24-
1. If `CodeAnalysisTreatWarningsAsErrors` and `TreatWarningsAsErrors` both are not set, no bulk settings to escalate or de-escalate warnings to errors is done.
25-
2. If `CodeAnalysisTreatWarningsAsErrors` is set, it overrides `TreatWarningsAsErrors` to determine if CA warnings are bulk escalated to errors or not.
26-
3. If `CodeAnalysisTreatWarningsAsErrors` is not set, it defaults to `TreatWarningsAsErrors`.
27-
4. If final value of `CodeAnalysisTreatWarningsAsErrors = false`, we append all CA rule IDs to `WarningsNotAsErrors` to ensure they are not escalated to errors. Users can still bump individual rule IDs to errors by editorconfig/ruleset entry, etc.
28-
5. If final value of `CodeAnalysisTreatWarningsAsErrors = true`, we append all CA rule IDs to `WarningsAsErrors` to ensure they are escalated to errors. We optimize it a bit more by avoiding this append if `TreatWarningsAsErrors` is also true, because then the compiler itself will take care of bumping all warnings to errors, and we don't need to pollute the command line with large number of CA rules IDs in a `/warnaserror+` switch. We expect this to be the most common case as well (`TreatWarningsAsErrors` is set by user to true, `CodeAnalysisTreatWarningsAsErrors` is never set and hence defaults to `true`), and we want to ensure we don't end up polluting the entire command line with CA rules IDs unless `TreatWarningsAsErrors` and `CodeAnalysisTreatWarningsAsErrors` have different settings.
24+
1. If `CodeAnalysisTreatWarningsAsErrors` is set to true, enabled CA warnings are bulk escalated to errors by choosing the appropriate globalconfig file with the error severity settings.
25+
2. Otherwise, if `TreatWarningsAsErrors` is set to true, this property translates to `/warnaserror` command line switch and the compiler bumps all warnings, including enabled CA warnings, to errors.

0 commit comments

Comments
 (0)