private static void AnalyzeType(SymbolAnalysisContext context, SymbolCache symbols) { var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; if (!WebApiFacts.IsWebApiController(namedTypeSymbol, symbols.IHttpControllerAttribute)) { return; } context.ReportDiagnostic(Diagnostic.Create(Rule, namedTypeSymbol.Locations.FirstOrDefault())); }
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); SymbolCache?symbolCache = null; context.RegisterCompilationStartAction(compilationStartAction => { if (symbolCache is null) { if (!SymbolCache.TryCreate(compilationStartAction.Compilation, out var createdSymbolCache)) { // No-op if we can't find types we care about. return; } symbolCache = createdSymbolCache; } compilationStartAction.RegisterOperationBlockAction(operationBlockContext => { if (operationBlockContext.OwningSymbol is not IMethodSymbol method || method.ContainingType is not INamedTypeSymbol type || !WebApiFacts.IsWebApiController(type, symbolCache.IHttpControllerAttribute) || !WebApiFacts.IsWebApiAction(method, symbolCache.NonActionAttribute)) { return; } if (method.HasAttribute(symbolCache.IActionHttpMethodProvider)) { // Method has already been annotated. We're cool. return; } var verb = SupportedHttpMethodConventions.FirstOrDefault(v => method.Name.StartsWith(v, StringComparison.OrdinalIgnoreCase)); // If no verb is inferred, POST is implied. Either way, we need to tell the user to annotate it to get the correct Mvc behavior. verb ??= "POST"; var properties = ImmutableDictionary.Create <string, string>() .Add("HttpMethodAttribute", $"Http{verb}Attribute"); operationBlockContext.ReportDiagnostic(Diagnostic.Create( Rule, method.Locations.First(), properties, $"Annotate method with HTTP{verb}Attribute.")); }); }); }
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); SymbolCache?symbolCache = null; context.RegisterOperationBlockStartAction(operationBlockContext => { if (symbolCache is null) { if (!SymbolCache.TryCreate(operationBlockContext.Compilation, out var createdSymbolCache)) { // No-op if we can't find types we care about. return; } symbolCache = createdSymbolCache; } if (operationBlockContext.OwningSymbol is not IMethodSymbol method || method.ContainingType is not INamedTypeSymbol type || !WebApiFacts.IsWebApiController(type, symbolCache.IHttpControllerAttribute) || !WebApiFacts.IsWebApiAction(method, symbolCache.NonActionAttribute)) { return; } var returnType = method.ReturnType; if (!SymbolEqualityComparer.Default.Equals(returnType, symbolCache.HttpResponseMessage) && !(SymbolEqualityComparer.Default.Equals(returnType.OriginalDefinition, symbolCache.TaskOfT) && returnType is INamedTypeSymbol { TypeParameters: { Length: > 0 } } namedType&& SymbolEqualityComparer.Default.Equals(namedType.TypeArguments[0], symbolCache.HttpResponseMessage))) { // Method does not return HttpResponseMessage return; } operationBlockContext.RegisterOperationAction(operationContext => { operationContext.ReportDiagnostic(Diagnostic.Create( Rule, operationContext.Operation.Syntax.GetLocation())); }, OperationKind.Return); }); }
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); SymbolCache?symbolCache = null; context.RegisterCompilationStartAction(compilationStartAction => { if (symbolCache is null) { if (!SymbolCache.TryCreate(compilationStartAction.Compilation, out var createdSymbolCache)) { // No-op if we can't find types we care about. return; } symbolCache = createdSymbolCache; } compilationStartAction.RegisterSymbolAction(symbolAnalysisContext => { if (symbolAnalysisContext.Symbol is not INamedTypeSymbol { TypeKind: TypeKind.Class } type || !WebApiFacts.IsWebApiController(type, symbolCache.ApiController)) { return; } var groupedActions = type.GetMembers() .OfType <IMethodSymbol>() .Where(m => WebApiFacts.IsWebApiAction(m, symbolCache.NonActionAttribute) && !HasAttributeRoute(symbolCache, m)) .GroupBy(m => m.Name) // Oops, we're not looking at ActionName attribute .Where(g => g.Count() > 1); foreach (var group in groupedActions) { foreach (var item in group) { symbolAnalysisContext.ReportDiagnostic(Diagnostic.Create( Rule, item.Locations.First(), group.Select(m => m.Locations.First()))); } } static bool HasAttributeRoute(SymbolCache symbolCache, IMethodSymbol methodSymbol) { var routeAttributes = methodSymbol.GetAttributes(symbolCache.IRouteTemplateProvider); foreach (var attribute in routeAttributes) { var args = attribute.ConstructorArguments; // This is probably a RouteAttribute or a Http*Attribute. Both of these have exactly one ctor that accepts any value viz the literal template. // Therefore if any value is supplied, it's a value for the route template. if (args.Length > 0 && !args[0].IsNull) { return(true); } } return(false); } }, SymbolKind.NamedType);