Skip to content

Commit 96baca0

Browse files
committed
Add RazorWarningLevel support
1 parent fa4d3e6 commit 96baca0

File tree

10 files changed

+131
-30
lines changed

10 files changed

+131
-30
lines changed

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Components;
1212

1313
internal class ComponentLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
1414
{
15+
private readonly int? _razorWarningLevel;
16+
1517
// This pass runs earlier than our other passes that 'lower' specific kinds of attributes.
1618
public override int Order => 0;
1719

20+
public ComponentLoweringPass(RazorConfiguration configuration)
21+
{
22+
_razorWarningLevel = configuration.RazorWarningLevel;
23+
}
24+
1825
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
1926
{
2027
if (!IsComponentDocument(documentNode))
@@ -137,7 +144,7 @@ static TagHelperDescriptor GetTagHelperOrAddDiagnostic(TagHelperIntermediateNode
137144
}
138145
}
139146

140-
private static ComponentIntermediateNode RewriteAsComponent(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper)
147+
private ComponentIntermediateNode RewriteAsComponent(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper)
141148
{
142149
var component = new ComponentIntermediateNode()
143150
{
@@ -214,8 +221,13 @@ static bool IsPresentAsAttribute(string attributeName, ComponentIntermediateNode
214221
}
215222
}
216223

217-
private static void WarnForUnnecessaryAt(ComponentIntermediateNode component)
224+
private void WarnForUnnecessaryAt(ComponentIntermediateNode component)
218225
{
226+
if (_razorWarningLevel is not >= 9)
227+
{
228+
return;
229+
}
230+
219231
foreach (var attribute in component.Attributes)
220232
{
221233
// IntParam="@x" has unnecessary `@`, can just use IntParam="x" -> warn

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorConfiguration.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ public sealed record class RazorConfiguration(
1212
string ConfigurationName,
1313
ImmutableArray<RazorExtension> Extensions,
1414
LanguageServerFlags? LanguageServerFlags = null,
15-
bool UseConsolidatedMvcViews = false)
15+
bool UseConsolidatedMvcViews = false,
16+
int? RazorWarningLevel = null)
1617
{
1718
public static readonly RazorConfiguration Default = new(
1819
RazorLanguageVersion.Latest,
@@ -27,6 +28,7 @@ public bool Equals(RazorConfiguration? other)
2728
ConfigurationName == other.ConfigurationName &&
2829
LanguageServerFlags == other.LanguageServerFlags &&
2930
UseConsolidatedMvcViews == other.UseConsolidatedMvcViews &&
31+
RazorWarningLevel == other.RazorWarningLevel &&
3032
Extensions.SequenceEqual(other.Extensions);
3133

3234
public override int GetHashCode()
@@ -37,6 +39,7 @@ public override int GetHashCode()
3739
hash.Add(Extensions);
3840
hash.Add(UseConsolidatedMvcViews);
3941
hash.Add(LanguageServerFlags);
42+
hash.Add(RazorWarningLevel);
4043
return hash;
4144
}
4245
}

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorProjectEngine.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ public static RazorProjectEngine Create(
363363
NamespaceDirective.Register(builder);
364364
AttributeDirective.Register(builder);
365365

366-
AddComponentFeatures(builder, configuration.LanguageVersion);
366+
AddComponentFeatures(builder, configuration);
367367
}
368368

369369
configure?.Invoke(builder);
@@ -448,8 +448,10 @@ private static void AddDefaultFeatures(ImmutableArray<IRazorFeature>.Builder fea
448448
});
449449
}
450450

451-
private static void AddComponentFeatures(RazorProjectEngineBuilder builder, RazorLanguageVersion razorLanguageVersion)
451+
private static void AddComponentFeatures(RazorProjectEngineBuilder builder, RazorConfiguration configuration)
452452
{
453+
RazorLanguageVersion razorLanguageVersion = configuration.LanguageVersion;
454+
453455
// Project Engine Features
454456
builder.Features.Add(new ComponentImportProjectFeature());
455457

@@ -486,7 +488,7 @@ private static void AddComponentFeatures(RazorProjectEngineBuilder builder, Razo
486488

487489
// Optimization
488490
builder.Features.Add(new ComponentComplexAttributeContentPass());
489-
builder.Features.Add(new ComponentLoweringPass());
491+
builder.Features.Add(new ComponentLoweringPass(configuration));
490492
builder.Features.Add(new ComponentEventHandlerLoweringPass());
491493
builder.Features.Add(new ComponentKeyLoweringPass());
492494
builder.Features.Add(new ComponentReferenceCaptureLoweringPass());

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/Diagnostics/DiagnosticIds.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators
66
internal static class DiagnosticIds
77
{
88
public const string InvalidRazorLangVersionRuleId = "RZ3600";
9+
public const string InvalidRazorWarningLevelRuleId = "RZ3601";
910
public const string ReComputingTagHelpersRuleId = "RSG001";
1011
public const string TargetPathNotProvidedRuleId = "RSG002";
1112
public const string GeneratedOutputFullPathNotProvidedRuleId = "RSG003";

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/Diagnostics/RazorDiagnostics.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ internal static class RazorDiagnostics
2020
DiagnosticSeverity.Error,
2121
isEnabledByDefault: true);
2222

23+
public static readonly DiagnosticDescriptor InvalidRazorWarningLevelDescriptor = new DiagnosticDescriptor(
24+
DiagnosticIds.InvalidRazorWarningLevelRuleId,
25+
new LocalizableResourceString(nameof(RazorSourceGeneratorResources.InvalidRazorWarningLevelTitle), RazorSourceGeneratorResources.ResourceManager, typeof(RazorSourceGeneratorResources)),
26+
new LocalizableResourceString(nameof(RazorSourceGeneratorResources.InvalidRazorWarningLevelMessage), RazorSourceGeneratorResources.ResourceManager, typeof(RazorSourceGeneratorResources)),
27+
"RazorSourceGenerator",
28+
DiagnosticSeverity.Error,
29+
isEnabledByDefault: true);
30+
2331
public static readonly DiagnosticDescriptor ReComputingTagHelpersDescriptor = new DiagnosticDescriptor(
2432
DiagnosticIds.ReComputingTagHelpersRuleId,
2533
new LocalizableResourceString(nameof(RazorSourceGeneratorResources.RecomputingTagHelpersTitle), RazorSourceGeneratorResources.ResourceManager, typeof(RazorSourceGeneratorResources)),

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/Diagnostics/RazorSourceGeneratorResources.resx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,13 @@
121121
<value>Invalid RazorLangVersion</value>
122122
</data>
123123
<data name="InvalidRazorLangMessage" xml:space="preserve">
124-
<value>Invalid value '{0}'' for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 8.0.</value>
124+
<value>Invalid value '{0}' for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 8.0.</value>
125+
</data>
126+
<data name="InvalidRazorWarningLevelTitle" xml:space="preserve">
127+
<value>Invalid RazorWarningLevel</value>
128+
</data>
129+
<data name="InvalidRazorWarningLevelMessage" xml:space="preserve">
130+
<value>Invalid value '{0}' for RazorWarningLevel. Must be empty or an integer.</value>
125131
</data>
126132
<data name="RecomputingTagHelpersTitle" xml:space="preserve">
127133
<value>Recomputing tag helpers</value>

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/IncrementalValueProviderExtensions.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using System.Collections.Immutable;
78
using System.Linq;
89
using Microsoft.AspNetCore.Razor;
910
using Microsoft.CodeAnalysis;
@@ -39,12 +40,12 @@ internal static IncrementalValuesProvider<TSource> ReportDiagnostics<TSource>(th
3940
return source.Where((pair) => pair.Item1 != null).Select((pair, ct) => pair.Item1!);
4041
}
4142

42-
internal static IncrementalValueProvider<TSource> ReportDiagnostics<TSource>(this IncrementalValueProvider<(TSource?, Diagnostic?)> source, IncrementalGeneratorInitializationContext context)
43+
internal static IncrementalValueProvider<TSource> ReportDiagnostics<TSource>(this IncrementalValueProvider<(TSource?, ImmutableArray<Diagnostic>)> source, IncrementalGeneratorInitializationContext context)
4344
{
4445
context.RegisterSourceOutput(source, (spc, source) =>
4546
{
46-
var (_, diagnostic) = source;
47-
if (diagnostic != null)
47+
var (_, diagnostics) = source;
48+
foreach (var diagnostic in diagnostics)
4849
{
4950
spc.ReportDiagnostic(diagnostic);
5051
}

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Immutable;
56
using System.IO;
67
using System.Text;
78
using System.Threading;
89
using Microsoft.AspNetCore.Razor.Language;
10+
using Microsoft.AspNetCore.Razor.PooledObjects;
911
using Microsoft.CodeAnalysis;
1012
using Microsoft.CodeAnalysis.CSharp;
1113
using Microsoft.CodeAnalysis.Diagnostics;
@@ -14,7 +16,7 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators
1416
{
1517
public partial class RazorSourceGenerator
1618
{
17-
private (RazorSourceGenerationOptions?, Diagnostic?) ComputeRazorSourceGeneratorOptions(((AnalyzerConfigOptionsProvider, ParseOptions), bool) pair, CancellationToken ct)
19+
private (RazorSourceGenerationOptions?, ImmutableArray<Diagnostic>) ComputeRazorSourceGeneratorOptions(((AnalyzerConfigOptionsProvider, ParseOptions), bool) pair, CancellationToken ct)
1820
{
1921
var ((options, parseOptions), isSuppressed) = pair;
2022
var globalOptions = options.GlobalOptions;
@@ -31,18 +33,42 @@ public partial class RazorSourceGenerator
3133
globalOptions.TryGetValue("build_property.SupportLocalizedComponentNames", out var supportLocalizedComponentNames);
3234
globalOptions.TryGetValue("build_property.GenerateRazorMetadataSourceChecksumAttributes", out var generateMetadataSourceChecksumAttributes);
3335

34-
Diagnostic? diagnostic = null;
36+
using var diagnostics = new PooledArrayBuilder<Diagnostic>();
3537
if (!globalOptions.TryGetValue("build_property.RazorLangVersion", out var razorLanguageVersionString) ||
3638
!RazorLanguageVersion.TryParse(razorLanguageVersionString, out var razorLanguageVersion))
3739
{
38-
diagnostic = Diagnostic.Create(
40+
diagnostics.Add(Diagnostic.Create(
3941
RazorDiagnostics.InvalidRazorLangVersionDescriptor,
4042
Location.None,
41-
razorLanguageVersionString);
43+
razorLanguageVersionString));
4244
razorLanguageVersion = RazorLanguageVersion.Latest;
4345
}
4446

45-
var razorConfiguration = new RazorConfiguration(razorLanguageVersion, configurationName ?? "default", Extensions: [], UseConsolidatedMvcViews: true);
47+
globalOptions.TryGetValue("build_property.RazorWarningLevel", out var razorWarningLevelString);
48+
int? razorWarningLevel;
49+
if (string.IsNullOrEmpty(razorWarningLevelString))
50+
{
51+
razorWarningLevel = null;
52+
}
53+
else if (!int.TryParse(razorWarningLevelString, out var razorWarningLevelInt))
54+
{
55+
diagnostics.Add(Diagnostic.Create(
56+
RazorDiagnostics.InvalidRazorWarningLevelDescriptor,
57+
Location.None,
58+
razorWarningLevelString));
59+
razorWarningLevel = null;
60+
}
61+
else
62+
{
63+
razorWarningLevel = razorWarningLevelInt;
64+
}
65+
66+
var razorConfiguration = new RazorConfiguration(
67+
razorLanguageVersion,
68+
configurationName ?? "default",
69+
Extensions: [],
70+
UseConsolidatedMvcViews: true,
71+
RazorWarningLevel: razorWarningLevel);
4672

4773
var razorSourceGenerationOptions = new RazorSourceGenerationOptions()
4874
{
@@ -54,7 +80,7 @@ public partial class RazorSourceGenerator
5480
TestSuppressUniqueIds = _testSuppressUniqueIds,
5581
};
5682

57-
return (razorSourceGenerationOptions, diagnostic);
83+
return (razorSourceGenerationOptions, diagnostics.DrainToImmutable());
5884
}
5985

6086
private static (SourceGeneratorProjectItem?, Diagnostic?) ComputeProjectItems((AdditionalText, AnalyzerConfigOptionsProvider) pair, CancellationToken ct)

src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Linq;
67
using System.Threading.Tasks;
78
using Microsoft.CodeAnalysis;
@@ -274,8 +275,14 @@ public static class RuntimeHelpers
274275
Assert.DoesNotContain("AddComponentParameter", source.SourceText.ToString());
275276
}
276277

277-
[Fact]
278-
public async Task ComponentParameter_UnnecessaryAt()
278+
[Theory]
279+
[InlineData(null, false)]
280+
[InlineData("", false)]
281+
[InlineData("8", false)]
282+
[InlineData("9", true)]
283+
[InlineData("10", true)]
284+
[InlineData("999", true)]
285+
public async Task ComponentParameter_UnnecessaryAt(string warningLevel, bool hasWarnings)
279286
{
280287
// Arrange
281288
var project = CreateTestProject(new()
@@ -308,22 +315,35 @@ public async Task ComponentParameter_UnnecessaryAt()
308315
"""
309316
});
310317
var compilation = await project.GetCompilationAsync();
311-
var driver = await GetDriverAsync(project);
318+
var driver = await GetDriverAsync(project, options =>
319+
{
320+
if (warningLevel != null)
321+
{
322+
options.TestGlobalOptions["build_property.RazorWarningLevel"] = warningLevel;
323+
}
324+
});
312325

313326
// Act
314327
var result = RunGenerator(compilation!, ref driver, out compilation);
315328

316329
// Assert
317-
result.Diagnostics.VerifyRazor(project,
318-
// Shared/Component1.razor(7,15): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
319-
// IntParam="@(43)"
320-
Diagnostic("RZ2013", "@").WithLocation(7, 15),
321-
// Shared/Component1.razor(10,15): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
322-
// IntParam="@x"
323-
Diagnostic("RZ2013", "@").WithLocation(10, 15),
324-
// Shared/Component1.razor(13,22): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
325-
// <Component2 IntParam=@x />
326-
Diagnostic("RZ2013", "@").WithLocation(13, 22));
330+
if (hasWarnings)
331+
{
332+
result.Diagnostics.VerifyRazor(project,
333+
// Shared/Component1.razor(7,15): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
334+
// IntParam="@(43)"
335+
Diagnostic("RZ2013", "@").WithLocation(7, 15),
336+
// Shared/Component1.razor(10,15): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
337+
// IntParam="@x"
338+
Diagnostic("RZ2013", "@").WithLocation(10, 15),
339+
// Shared/Component1.razor(13,22): warning RZ2013: The '@' prefix is not necessary for component parameters whose type is not string.
340+
// <Component2 IntParam=@x />
341+
Diagnostic("RZ2013", "@").WithLocation(13, 22));
342+
}
343+
else
344+
{
345+
result.Diagnostics.VerifyRazor(project);
346+
}
327347
Assert.Equal(3, result.GeneratedSources.Length);
328348
await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index");
329349
}

src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3073,11 +3073,33 @@ public async Task RazorLangVersion_Incorrect([CombinatorialValues("incorrect", "
30733073
var result = RunGenerator(compilation!, ref driver);
30743074

30753075
result.Diagnostics.Verify(
3076-
// error RZ3600: Invalid value '{0}'' for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 8.0.
3076+
// error RZ3600: Invalid value '{0}' for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 8.0.
30773077
Diagnostic("RZ3600").WithArguments(langVersion).WithLocation(1, 1));
30783078
Assert.Single(result.GeneratedSources);
30793079
}
30803080

3081+
[Theory, CombinatorialData]
3082+
public async Task RazorWarningLevel_Incorrect(
3083+
[CombinatorialValues("incorrect", "1.2", "0x1")] string warningLevel)
3084+
{
3085+
var project = CreateTestProject(new()
3086+
{
3087+
["Pages/Index.razor"] = "<h1>Hello world</h1>",
3088+
});
3089+
var compilation = await project.GetCompilationAsync();
3090+
var driver = await GetDriverAsync(project, options =>
3091+
{
3092+
options.TestGlobalOptions["build_property.RazorWarningLevel"] = warningLevel;
3093+
});
3094+
3095+
var result = RunGenerator(compilation!, ref driver);
3096+
3097+
result.Diagnostics.Verify(
3098+
// error RZ3600: Invalid value '{0}' for RazorWarningLevel. Must be empty or an integer.
3099+
Diagnostic("RZ3601").WithArguments(warningLevel).WithLocation(1, 1));
3100+
Assert.Single(result.GeneratedSources);
3101+
}
3102+
30813103
[Fact]
30823104
public async Task Test_WhenEmptyOrCached()
30833105
{

0 commit comments

Comments
 (0)