From c0b9aa27c6ef613ae1a367ef1d26950e4f0cb626 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 17 Jul 2024 10:06:30 +0200 Subject: [PATCH] Handle `:get`/`:set` in `EditorRequired` checking (#10628) * Add utility for verifying razor diagnostics * Handle `:get`/`:set` in `EditorRequired` checking * Simplify code --- .../ComponentCodeGenerationTestBase.cs | 90 +++++++++++++++++++ .../Components/ComponentLoweringPass.cs | 9 +- .../DiagnosticExtensions.cs | 20 +++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs index 364e9b87341..84f21cea83b 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs @@ -1198,6 +1198,96 @@ public class ComponentWithEditorRequiredParameters : ComponentBase Assert.Empty(generated.RazorDiagnostics); } + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGetSet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private string myField = "Some Value"; + private void OnFieldChanged(string value) { } + } + """); + + CompileToAssembly(generated); + Assert.Empty(generated.RazorDiagnostics); + } + + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindGet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private string myField = "Some Value"; + } + """); + + CompileToAssembly(generated); + Assert.Empty(generated.RazorDiagnostics); + } + + [IntegrationTestFact, WorkItem("https://github.com/dotnet/razor/issues/10553")] + public void Component_WithEditorRequiredParameter_ValueSpecifiedUsingBindSet() + { + AdditionalSyntaxTrees.Add(Parse(""" + using System; + using Microsoft.AspNetCore.Components; + + namespace Test; + + public class ComponentWithEditorRequiredParameters : ComponentBase + { + [Parameter] + [EditorRequired] + public string Property1 { get; set; } + } + """)); + + var generated = CompileToCSharp(""" + + + @code { + private void OnFieldChanged(string value) { } + } + """); + + var compiled = CompileToAssembly(generated); + generated.RazorDiagnostics.Verify( + // x:\dir\subdir\Test\TestComponent.cshtml(1,61): error RZ10016: Attribute 'bind-Property1:set' was used but no attribute 'bind-Property1:get' was found. + Diagnostic("RZ10016").WithLocation(1, 61)); + } + [IntegrationTestFact] public void Component_WithEditorRequiredChildContent_NoValueSpecified() { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs index e01340e82cf..8d809696490 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs @@ -203,10 +203,17 @@ static bool IsPresentAsAttribute(string attributeName, ComponentIntermediateNode const string bindPrefix = "@bind-"; if (child is TagHelperDirectiveAttributeIntermediateNode { OriginalAttributeName: { } originalAttributeName } && originalAttributeName.StartsWith(bindPrefix, StringComparison.Ordinal) && - originalAttributeName.AsSpan()[bindPrefix.Length..].Equals(attributeName.AsSpan(), StringComparison.Ordinal)) + originalAttributeName.AsSpan(start: bindPrefix.Length).Equals(attributeName.AsSpan(), StringComparison.Ordinal)) { return true; } + if (child is TagHelperDirectiveAttributeParameterIntermediateNode { OriginalAttributeName: { } originalName, AttributeNameWithoutParameter: { } nameWithoutParameter } && + originalName.StartsWith(bindPrefix, StringComparison.Ordinal) && + nameWithoutParameter.AsSpan(start: bindPrefix.Length - 1).Equals(attributeName.AsSpan(), StringComparison.Ordinal)) + { + // `@bind-Value:get` or `@bind-Value:set` is specified. + return true; + } } return false; diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs new file mode 100644 index 00000000000..54f800e718d --- /dev/null +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/DiagnosticExtensions.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.NET.Sdk.Razor.SourceGenerators; + +namespace Microsoft.CodeAnalysis; + +public static class RazorDiagnosticExtensions +{ + public static void Verify( + this IEnumerable diagnostics, + params DiagnosticDescription[] expected) + { + diagnostics.Select(d => d.AsDiagnostic()).Verify(expected); + } +}