@@ -2,8 +2,6 @@ namespace AspNetCoreAnalyzers
22{
33 using System . Collections . Immutable ;
44 using System . Linq ;
5- using System . Text . RegularExpressions ;
6- using AspNetCoreAnalyzers . Helpers ;
75 using Gu . Roslyn . AnalyzerExtensions ;
86 using Microsoft . CodeAnalysis ;
97 using Microsoft . CodeAnalysis . CSharp ;
@@ -24,58 +22,84 @@ public override void Initialize(AnalysisContext context)
2422 private static void Handle ( SyntaxNodeAnalysisContext context )
2523 {
2624 if ( ! context . IsExcludedFromAnalysis ( ) &&
27- context . Node is AttributeSyntax attribute )
25+ context . Node is AttributeSyntax attribute &&
26+ context . ContainingSymbol is IMethodSymbol method &&
27+ TryGetTemplate ( attribute , context , out var template ) )
2828 {
29- if ( context . ContainingSymbol is IMethodSymbol method &&
30- TryGetTemplate ( attribute , context , out var template ) &&
31- TryGetRouteParameter ( template , out var name ) &&
32- method . Parameters . TrySingle ( x => IsFromRoute ( x ) , out var parameter ) &&
33- parameter . Name != name )
29+ foreach ( var component in template . Path )
3430 {
35- context . ReportDiagnostic (
36- Diagnostic . Create (
37- ASP001ParameterName . Descriptor ,
38- parameter . Locations . Single ( ) ,
39- ImmutableDictionary < string , string > . Empty . Add ( nameof ( NameSyntax ) , name ) ) ) ;
31+ if ( TryGetParameterName ( component . Text , out var name ) )
32+ {
33+ if ( method . Parameters . TrySingle ( x => IsFromRoute ( x ) , out var single ) &&
34+ single . Name != name )
35+ {
36+ context . ReportDiagnostic (
37+ Diagnostic . Create (
38+ ASP001ParameterName . Descriptor ,
39+ single . Locations . Single ( ) ,
40+ ImmutableDictionary < string , string > . Empty . Add ( nameof ( NameSyntax ) , name ) ) ) ;
41+ }
42+ }
4043 }
4144 }
4245 }
4346
44- private static bool TryGetTemplate ( AttributeSyntax attribute , SyntaxNodeAnalysisContext context , out LiteralExpressionSyntax literal )
47+ private static bool TryGetParameterName ( string text , out string name )
4548 {
46- if ( attribute . ArgumentList is AttributeArgumentListSyntax argumentList &&
47- argumentList . Arguments . TrySingle ( out var argument ) &&
48- argument . Expression is LiteralExpressionSyntax candidate &&
49- candidate . IsKind ( SyntaxKind . StringLiteralExpression ) &&
50- ( Attribute . IsType ( attribute , KnownSymbol . HttpGetAttribute , context . SemanticModel , context . CancellationToken ) ||
51- Attribute . IsType ( attribute , KnownSymbol . HttpPostAttribute , context . SemanticModel , context . CancellationToken ) ||
52- Attribute . IsType ( attribute , KnownSymbol . HttpPutAttribute , context . SemanticModel , context . CancellationToken ) ||
53- Attribute . IsType ( attribute , KnownSymbol . HttpDeleteAttribute , context . SemanticModel , context . CancellationToken ) ) )
49+ var start = text . IndexOf ( '{' ) ;
50+ if ( start < 0 ||
51+ text . IndexOf ( '}' ) < start ||
52+ text . IndexOf ( '{' , start ) > 0 )
5453 {
55- literal = candidate ;
56- return true ;
54+ name = null ;
55+ return false ;
5756 }
5857
59- literal = null ;
60- return false ;
58+ start ++ ;
59+ while ( start < text . Length &&
60+ text [ start ] == ' ' )
61+ {
62+ start ++ ;
63+ }
64+
65+ var end = text . IndexOf ( '}' , start ) ;
66+ if ( end < 0 )
67+ {
68+ name = null ;
69+ return false ;
70+ }
71+
72+ while ( text [ end ] == ' ' )
73+ {
74+ end -- ;
75+ }
76+
77+ name = text . Substring ( start , end - start ) ;
78+ return true ;
6179 }
6280
63- private static bool TryGetRouteParameter ( LiteralExpressionSyntax literal , out string name )
81+ private static bool TryGetTemplate ( AttributeSyntax attribute , SyntaxNodeAnalysisContext context , out UrlTemplate template )
6482 {
65- var match = Regex . Match ( literal . Token . ValueText , @"\{(?<name>\w+)}" ) ;
66- if ( match . Success )
83+ if ( attribute . ArgumentList is AttributeArgumentListSyntax argumentList &&
84+ argumentList . Arguments . TrySingle ( out var argument ) &&
85+ argument . Expression is LiteralExpressionSyntax literal &&
86+ literal . IsKind ( SyntaxKind . StringLiteralExpression ) &&
87+ ( Attribute . IsType ( attribute , KnownSymbol . HttpGetAttribute , context . SemanticModel , context . CancellationToken ) ||
88+ Attribute . IsType ( attribute , KnownSymbol . HttpPostAttribute , context . SemanticModel , context . CancellationToken ) ||
89+ Attribute . IsType ( attribute , KnownSymbol . HttpPutAttribute , context . SemanticModel , context . CancellationToken ) ||
90+ Attribute . IsType ( attribute , KnownSymbol . HttpDeleteAttribute , context . SemanticModel , context . CancellationToken ) ) &&
91+ UrlTemplate . TryParse ( literal , out template ) )
6792 {
68- name = match . Groups [ "name" ] . Value ;
6993 return true ;
7094 }
7195
72- name = null ;
96+ template = default ( UrlTemplate ) ;
7397 return false ;
7498 }
7599
76- private static bool IsFromRoute ( IParameterSymbol parameter )
100+ private static bool IsFromRoute ( IParameterSymbol p )
77101 {
78- foreach ( var attributeData in parameter . GetAttributes ( ) )
102+ foreach ( var attributeData in p . GetAttributes ( ) )
79103 {
80104 if ( attributeData . AttributeClass == KnownSymbol . FromRouteAttribute )
81105 {
0 commit comments