Skip to content

Commit c6d3214

Browse files
committed
Merge in 'release/6.0-rc2' changes
2 parents 421d23a + cc2f397 commit c6d3214

10 files changed

+189
-17
lines changed

src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public DefaultRazorCodeGenerationOptions(
1515
bool suppressPrimaryMethodBody,
1616
bool suppressNullabilityEnforcement,
1717
bool omitMinimizedComponentAttributeValues,
18+
bool supportLocalizedComponentNames,
1819
bool useEnhancedLinePragma)
1920
{
2021
IndentWithTabs = indentWithTabs;
@@ -26,6 +27,7 @@ public DefaultRazorCodeGenerationOptions(
2627
SuppressPrimaryMethodBody = suppressPrimaryMethodBody;
2728
SuppressNullabilityEnforcement = suppressNullabilityEnforcement;
2829
OmitMinimizedComponentAttributeValues = omitMinimizedComponentAttributeValues;
30+
SupportLocalizedComponentNames = supportLocalizedComponentNames;
2931
UseEnhancedLinePragma = useEnhancedLinePragma;
3032
}
3133

src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public DefaultRazorCodeGenerationOptionsBuilder(bool designTime)
4141

4242
public override bool OmitMinimizedComponentAttributeValues { get; set; }
4343

44+
public override bool SupportLocalizedComponentNames { get; set; }
45+
4446
public override bool UseEnhancedLinePragma { get; set; }
4547

4648
public override RazorCodeGenerationOptions Build()
@@ -55,6 +57,7 @@ public override RazorCodeGenerationOptions Build()
5557
SuppressPrimaryMethodBody,
5658
SuppressNullabilityEnforcement,
5759
OmitMinimizedComponentAttributeValues,
60+
SupportLocalizedComponentNames,
5861
UseEnhancedLinePragma)
5962
{
6063
SuppressMetadataSourceChecksumAttributes = SuppressMetadataSourceChecksumAttributes,

src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorIntermediateNodeLoweringPhase.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.Globalization;
78
using System.Linq;
89
using Microsoft.AspNetCore.Razor.Language.Components;
910
using Microsoft.AspNetCore.Razor.Language.Extensions;
@@ -1200,10 +1201,8 @@ public override void VisitMarkupElement(MarkupElementSyntax node)
12001201
{
12011202
// We only want this error during the second phase of the two phase compilation.
12021203
var startTagName = node.StartTag.GetTagNameWithOptionalBang();
1203-
if (startTagName != null && startTagName.Length > 0 && char.IsUpper(startTagName, 0))
1204+
if (!string.IsNullOrEmpty(startTagName) && LooksLikeAComponentName(_document, startTagName))
12041205
{
1205-
// A markup element that starts with an uppercase character.
1206-
// It is most likely intended to be a component. Add a warning.
12071206
element.Diagnostics.Add(
12081207
ComponentDiagnosticFactory.Create_UnexpectedMarkupElement(startTagName, BuildSourceSpanFromNode(node.StartTag)));
12091208
}
@@ -1214,6 +1213,24 @@ public override void VisitMarkupElement(MarkupElementSyntax node)
12141213
base.VisitMarkupElement(node);
12151214

12161215
_builder.Pop();
1216+
1217+
static bool LooksLikeAComponentName(DocumentIntermediateNode document, string startTagName)
1218+
{
1219+
var category = char.GetUnicodeCategory(startTagName, 0);
1220+
1221+
// A markup element which starts with an uppercase character is likely a component.
1222+
//
1223+
// In certain cultures, characters are not explicitly Uppercase/Lowercase, hence we must check
1224+
// the specific UnicodeCategory to see if we may still be able to treat it as a component.
1225+
//
1226+
// The goal here is to avoid clashing with any future standard-HTML elements.
1227+
//
1228+
// To avoid a breaking change, the support of localized component names (without explicit
1229+
// Uppercase classification) is behind a `SupportLocalizedComponentNames` feature flag.
1230+
return category is UnicodeCategory.UppercaseLetter ||
1231+
(document.Options.SupportLocalizedComponentNames &&
1232+
(category is UnicodeCategory.TitlecaseLetter || category is UnicodeCategory.OtherLetter));
1233+
}
12171234
}
12181235

12191236
public override void VisitMarkupStartTag(MarkupStartTagSyntax node)

src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ Microsoft.AspNetCore.Razor.Language.SourceSpan.EndCharacterIndex.get -> int
3333
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptionsBuilder.UseEnhancedLinePragma.get -> bool
3434
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptionsBuilder.UseEnhancedLinePragma.set -> void
3535
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptions.UseEnhancedLinePragma.get -> bool
36+
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptionsBuilder.SupportLocalizedComponentNames.get -> bool
37+
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptionsBuilder.SupportLocalizedComponentNames.set -> void
38+
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptions.SupportLocalizedComponentNames.get -> bool
39+
virtual Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptions.SupportLocalizedComponentNames.set -> void
40+
~static Microsoft.AspNetCore.Razor.Language.RazorProjectEngineBuilderExtensions.SetSupportLocalizedComponentNames(this Microsoft.AspNetCore.Razor.Language.RazorProjectEngineBuilder builder) -> Microsoft.AspNetCore.Razor.Language.RazorProjectEngineBuilder

src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static RazorCodeGenerationOptions CreateDefault()
1919
suppressPrimaryMethodBody: false,
2020
suppressNullabilityEnforcement: false,
2121
omitMinimizedComponentAttributeValues: false,
22+
supportLocalizedComponentNames: false,
2223
useEnhancedLinePragma: true);
2324
}
2425

@@ -34,6 +35,7 @@ public static RazorCodeGenerationOptions CreateDesignTimeDefault()
3435
suppressPrimaryMethodBody: false,
3536
suppressNullabilityEnforcement: false,
3637
omitMinimizedComponentAttributeValues: false,
38+
supportLocalizedComponentNames: false,
3739
useEnhancedLinePragma: true);
3840
}
3941

@@ -133,6 +135,14 @@ public static RazorCodeGenerationOptions CreateDesignTime(Action<RazorCodeGenera
133135
/// </summary>
134136
public virtual bool OmitMinimizedComponentAttributeValues { get; }
135137

138+
/// <summary>
139+
/// Gets a value that determines if localized component names are to be supported.
140+
/// </summary>
141+
public virtual bool SupportLocalizedComponentNames { get; set; }
142+
143+
/// <summary>
144+
/// Gets a value that determines if enhanced line pragmas are to be utilized.
145+
/// </summary>
136146
public virtual bool UseEnhancedLinePragma { get; }
137147
}
138148
}

src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ public abstract class RazorCodeGenerationOptionsBuilder
7373
/// </summary>
7474
public virtual bool OmitMinimizedComponentAttributeValues { get; set; }
7575

76+
/// <summary>
77+
/// Gets or sets a value that determines if localized component names are to be supported.
78+
/// </summary>
79+
public virtual bool SupportLocalizedComponentNames { get; set; }
80+
81+
/// <summary>
82+
/// Gets a value that determines if enhanced line pragmas are to be utilized.
83+
/// </summary>
7684
public virtual bool UseEnhancedLinePragma { get; set; }
7785

7886
public abstract RazorCodeGenerationOptions Build();

src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngineBuilderExtensions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ public static RazorProjectEngineBuilder SetRootNamespace(this RazorProjectEngine
9292
return builder;
9393
}
9494

95+
/// <summary>
96+
/// Sets the SupportLocalizedComponentNames property to make localized component name diagnostics available.
97+
/// </summary>
98+
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
99+
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
100+
public static RazorProjectEngineBuilder SetSupportLocalizedComponentNames(this RazorProjectEngineBuilder builder)
101+
{
102+
if (builder == null)
103+
{
104+
throw new ArgumentNullException(nameof(builder));
105+
}
106+
107+
builder.Features.Add(new SetSupportLocalizedComponentNamesFeature());
108+
return builder;
109+
}
110+
95111
public static void SetImportFeature(this RazorProjectEngineBuilder builder, IImportProjectFeature feature)
96112
{
97113
if (builder == null)
@@ -302,6 +318,21 @@ public InMemoryProjectItem(string content)
302318
}
303319
}
304320

321+
private class SetSupportLocalizedComponentNamesFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature
322+
{
323+
public int Order { get; set; }
324+
325+
public void Configure(RazorCodeGenerationOptionsBuilder options)
326+
{
327+
if (options == null)
328+
{
329+
throw new ArgumentNullException(nameof(options));
330+
}
331+
332+
options.SupportLocalizedComponentNames = true;
333+
}
334+
}
335+
305336
private class ConfigureRootNamespaceFeature : IConfigureRazorCodeGenerationOptionsFeature
306337
{
307338
private readonly string _rootNamespace;

src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentChildContentIntegrationTest.cs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,10 @@ public void ChildContent_ExplicitChildContent_UnrecogizedContent_ProducesDiagnos
115115
diagnostic.GetMessage(CultureInfo.CurrentCulture));
116116
}
117117

118-
[Fact]
119-
public void ChildContent_ExplicitChildContent_UnrecogizedElement_ProducesDiagnostic()
118+
[Theory]
119+
[InlineData(true)]
120+
[InlineData(false)]
121+
public void ChildContent_ExplicitChildContent_UnrecogizedElement_ProducesDiagnostic(bool supportLocalizedComponentNames)
120122
{
121123
// Arrange
122124
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
@@ -127,7 +129,7 @@ public void ChildContent_ExplicitChildContent_UnrecogizedElement_ProducesDiagnos
127129
<ChildContent>
128130
</ChildContent>
129131
<UnrecognizedChildContent></UnrecognizedChildContent>
130-
</RenderChildContent>");
132+
</RenderChildContent>", supportLocalizedComponentNames: supportLocalizedComponentNames);
131133

132134
// Assert
133135
Assert.Collection(
@@ -136,6 +138,47 @@ public void ChildContent_ExplicitChildContent_UnrecogizedElement_ProducesDiagnos
136138
d => Assert.Equal("RZ9996", d.Id));
137139
}
138140

141+
[Fact]
142+
public void ChildContent_ExplicitChildContent_StartsWithCharThatIsOtherLetterCategory_WhenLocalizedComponentNamesIsAllowed()
143+
{
144+
// Arrange
145+
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
146+
147+
// Act
148+
var generated = CompileToCSharp(@$"
149+
<RenderChildContent>
150+
<ChildContent>
151+
</ChildContent>
152+
<繁体字></繁体字>
153+
</RenderChildContent>", supportLocalizedComponentNames: true);
154+
155+
// Assert
156+
Assert.Collection(
157+
generated.Diagnostics,
158+
d => Assert.Equal("RZ10012", d.Id),
159+
d => Assert.Equal("RZ9996", d.Id));
160+
}
161+
162+
[Fact]
163+
public void ChildContent_ExplicitChildContent_StartsWithCharThatIsOtherLetterCategory_WhenLocalizedComponentNamesIsDisallowed()
164+
{
165+
// Arrange
166+
AdditionalSyntaxTrees.Add(RenderChildContentComponent);
167+
168+
// Act
169+
var generated = CompileToCSharp(@$"
170+
<RenderChildContent>
171+
<ChildContent>
172+
</ChildContent>
173+
<繁体字></繁体字>
174+
</RenderChildContent>", supportLocalizedComponentNames: false);
175+
176+
// Assert
177+
Assert.Collection(
178+
generated.Diagnostics,
179+
d => Assert.Equal("RZ9996", d.Id));
180+
}
181+
139182
[Fact]
140183
public void ChildContent_ExplicitChildContent_UnrecogizedAttribute_ProducesDiagnostic()
141184
{

src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentDiagnosticRazorIntegrationTest.cs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public void OldCodeBlockAttributeSyntax_ReportsError()
4343
var diagnostic = Assert.Single(generated.Diagnostics);
4444
Assert.Equal("RZ9979", diagnostic.Id);
4545
Assert.NotNull(diagnostic.GetMessage(CultureInfo.CurrentCulture));
46-
4746
}
4847

4948
[Fact]
@@ -147,16 +146,18 @@ public void Component_StartsWithLowerCase_ReportsError()
147146
diagnostic.GetMessage(CultureInfo.CurrentCulture));
148147
}
149148

150-
[Fact]
151-
public void Element_DoesNotStartWithLowerCase_ReportsWarning()
149+
[Theory]
150+
[InlineData(true)]
151+
[InlineData(false)]
152+
public void Component_NotFound_ReportsWarning(bool supportLocalizedComponentNames)
152153
{
153154
// Arrange & Act
154155
var generated = CompileToCSharp(@"
155156
<PossibleComponent></PossibleComponent>
156157
157158
@functions {
158159
public string Text { get; set; } = ""text"";
159-
}");
160+
}", supportLocalizedComponentNames: supportLocalizedComponentNames);
160161

161162
// Assert
162163
var diagnostic = Assert.Single(generated.Diagnostics);
@@ -167,6 +168,41 @@ public void Element_DoesNotStartWithLowerCase_ReportsWarning()
167168
diagnostic.GetMessage(CultureInfo.CurrentCulture));
168169
}
169170

171+
[Fact]
172+
public void Component_NotFound_StartsWithOtherLetter_WhenLocalizedComponentNamesIsAllowed_ReportsWarning()
173+
{
174+
// Arrange & Act
175+
var generated = CompileToCSharp(@"
176+
<繁体字></繁体字>
177+
178+
@functions {
179+
public string Text { get; set; } = ""text"";
180+
}", supportLocalizedComponentNames: true);
181+
182+
// Assert
183+
var diagnostic = Assert.Single(generated.Diagnostics);
184+
Assert.Equal("RZ10012", diagnostic.Id);
185+
Assert.Equal(RazorDiagnosticSeverity.Warning, diagnostic.Severity);
186+
Assert.Equal(
187+
"Found markup element with unexpected name '繁体字'. If this is intended to be a component, add a @using directive for its namespace.",
188+
diagnostic.GetMessage(CultureInfo.CurrentCulture));
189+
}
190+
191+
[Fact]
192+
public void Component_NotFound_StartsWithOtherLetter_WhenLocalizedComponentNamesIsDisallowed()
193+
{
194+
// Arrange & Act
195+
var generated = CompileToCSharp(@"
196+
<繁体字></繁体字>
197+
198+
@functions {
199+
public string Text { get; set; } = ""text"";
200+
}", supportLocalizedComponentNames: false);
201+
202+
// Assert
203+
Assert.Empty(generated.Diagnostics);
204+
}
205+
170206
[Fact]
171207
public void Element_DoesNotStartWithLowerCase_OverrideWithBang_NoWarning()
172208
{

0 commit comments

Comments
 (0)