private static bool HasWrongName(PathSegment segment, UrlAttribute urlAttribute, SyntaxNodeAnalysisContext context, out Replacement <Location> nameReplacement, out Replacement <Span> spanReplacement) { if (context.ContainingSymbol is IMethodSymbol method && segment.Parameter is { } templateParameter) { if (!TryFindParameter(templateParameter, method, out _)) { if (method.Parameters.TrySingle(x => IsOrphan(x), out var symbol) && symbol.TrySingleDeclaration(context.CancellationToken, out var parameterSyntax)) { nameReplacement = new Replacement <Location>(parameterSyntax.Identifier.GetLocation(), templateParameter.Name.ToString()); spanReplacement = new Replacement <Span>(templateParameter.Name, symbol.Name); return(true); } // Using TryFirst instead of Count() here as a silly optimization // As it is called after TrySingle it means Count() > 1 if (method.Parameters.TryFirst(x => IsOrphan(x), out _) && method.TrySingleDeclaration(context.CancellationToken, out MethodDeclarationSyntax? methodDeclaration)) { nameReplacement = new Replacement <Location>(methodDeclaration.ParameterList.GetLocation(), null); spanReplacement = new Replacement <Span>(templateParameter.Name, null); return(true); } } } nameReplacement = default; spanReplacement = default; return(false); bool IsOrphan(IParameterSymbol p) { if (IsFromRoute(p) && urlAttribute.UrlTemplate is { } template) { foreach (var candidateSegment in template.Path) { if (candidateSegment.Parameter is { } candidateParameter&& candidateParameter.Name.Equals(p.Name, StringComparison.Ordinal)) { return(false); } } return(true); } return(false); } }
private static void Handle(SyntaxNodeAnalysisContext context) { if (!context.IsExcludedFromAnalysis() && context.Node is AttributeSyntax attribute && UrlAttribute.TryCreate(attribute, context, out var urlAttribute) && urlAttribute.UrlTemplate is { } template) { foreach (var segment in template.Path) { if (HasWrongName(segment, urlAttribute, context, out var nameReplacement, out var spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP001ParameterSymbolName, nameReplacement.Node, nameReplacement.Property(nameof(NameSyntax)))); context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP002RouteParameterName, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (HasWrongType(segment, context, out var typeReplacement, out spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP003ParameterSymbolType, typeReplacement.Node.GetLocation(), typeReplacement.Property(nameof(TypeSyntax)))); context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP004RouteParameterType, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (HasWrongSyntax(segment, out spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP005ParameterSyntax, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (HasWrongRegexSyntax(segment, out spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP006ParameterRegex, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (HasMissingMethodParameter(segment, context, out var location, out var name)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP007MissingParameter, location, name)); } if (HasInvalidName(segment, out spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP008ValidRouteParameterName, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (ShouldKebabCase(segment, out var kebabCase)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP009KebabCaseUrl, segment.Span.GetLocation(), ImmutableDictionary <string, string> .Empty.Add(nameof(UrlTemplate), kebabCase))); } if (HasSyntaxError(segment, out location)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP010UrlSyntax, location, segment.Span.ToString(location))); } if (IsMultipleOccurringParameter(segment, urlAttribute, context, out location)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP011RouteParameterNameMustBeUnique, location)); } if (ShouldUseExplicitRoute(segment, context, out spanReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP012UseExplicitRoute, spanReplacement.Node.GetLocation(), spanReplacement.Property(nameof(UrlTemplate)))); } if (ShouldRenameController(segment, urlAttribute, context, out nameReplacement)) { context.ReportDiagnostic( Diagnostic.Create( Descriptors.ASP013ControllerNameShouldMatchRoute, nameReplacement.Node, nameReplacement.Property(nameof(NameSyntax)), nameReplacement.NewText)); } } } }