1+ // Licensed to the .NET Foundation under one or more agreements.
2+ // The .NET Foundation licenses this file to you under the MIT license.
3+
4+ using System . Collections . Immutable ;
5+ using System . Linq ;
6+ using Microsoft . CodeAnalysis ;
7+ using Microsoft . CodeAnalysis . CSharp ;
8+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
9+ using Microsoft . CodeAnalysis . Diagnostics ;
10+
11+ #nullable enable
12+
13+ namespace Microsoft . AspNetCore . Components . Analyzers ;
14+
15+ [ DiagnosticAnalyzer ( LanguageNames . CSharp ) ]
16+ public sealed class SupplyParameterFromFormAnalyzer : DiagnosticAnalyzer
17+ {
18+ public SupplyParameterFromFormAnalyzer ( )
19+ {
20+ SupportedDiagnostics = ImmutableArray . Create (
21+ DiagnosticDescriptors . SupplyParameterFromFormShouldNotHavePropertyInitializer ) ;
22+ }
23+
24+ public override ImmutableArray < DiagnosticDescriptor > SupportedDiagnostics { get ; }
25+
26+ public override void Initialize ( AnalysisContext context )
27+ {
28+ context . EnableConcurrentExecution ( ) ;
29+ context . ConfigureGeneratedCodeAnalysis ( GeneratedCodeAnalysisFlags . Analyze | GeneratedCodeAnalysisFlags . ReportDiagnostics ) ;
30+ context . RegisterCompilationStartAction ( context =>
31+ {
32+ if ( ! ComponentSymbols . TryCreate ( context . Compilation , out var symbols ) )
33+ {
34+ // Types we need are not defined.
35+ return ;
36+ }
37+
38+ context . RegisterSyntaxNodeAction ( context =>
39+ {
40+ var propertyDeclaration = ( PropertyDeclarationSyntax ) context . Node ;
41+
42+ // Check if property has an initializer
43+ if ( propertyDeclaration . Initializer == null )
44+ {
45+ return ;
46+ }
47+
48+ // Ignore initializers that set to default values (null, default, etc.)
49+ if ( IsDefaultValueInitializer ( propertyDeclaration . Initializer . Value ) )
50+ {
51+ return ;
52+ }
53+
54+ var propertySymbol = context . SemanticModel . GetDeclaredSymbol ( propertyDeclaration ) ;
55+ if ( propertySymbol == null )
56+ {
57+ return ;
58+ }
59+
60+ // Check if property has [SupplyParameterFromForm] attribute
61+ if ( ! ComponentFacts . IsSupplyParameterFromForm ( symbols , propertySymbol ) )
62+ {
63+ return ;
64+ }
65+
66+ // Check if the containing type inherits from ComponentBase
67+ var containingType = propertySymbol . ContainingType ;
68+ if ( ! ComponentFacts . IsComponentBase ( symbols , containingType ) )
69+ {
70+ return ;
71+ }
72+
73+ var propertyLocation = propertySymbol . Locations . FirstOrDefault ( ) ;
74+ if ( propertyLocation != null )
75+ {
76+ context . ReportDiagnostic ( Diagnostic . Create (
77+ DiagnosticDescriptors . SupplyParameterFromFormShouldNotHavePropertyInitializer ,
78+ propertyLocation ,
79+ propertySymbol . ToDisplayString ( SymbolDisplayFormat . CSharpErrorMessageFormat ) ) ) ;
80+ }
81+ } , SyntaxKind . PropertyDeclaration ) ;
82+ } ) ;
83+ }
84+
85+ private static bool IsDefaultValueInitializer ( ExpressionSyntax expression )
86+ {
87+ return expression switch
88+ {
89+ // null
90+ LiteralExpressionSyntax { Token . ValueText : "null" } => true ,
91+ // null!
92+ PostfixUnaryExpressionSyntax { Operand : LiteralExpressionSyntax { Token . ValueText : "null" } , OperatorToken . ValueText : "!" } => true ,
93+ // default
94+ LiteralExpressionSyntax literal when literal . Token . IsKind ( SyntaxKind . DefaultKeyword ) => true ,
95+ // default!
96+ PostfixUnaryExpressionSyntax { Operand : LiteralExpressionSyntax literal , OperatorToken . ValueText : "!" }
97+ when literal . Token . IsKind ( SyntaxKind . DefaultKeyword ) => true ,
98+ _ => false
99+ } ;
100+ }
101+ }
0 commit comments