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