public override void Initialize(AnalysisContext analysisContext) { analysisContext.EnableConcurrentExecution(); analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); analysisContext.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartContext) => { INamedTypeSymbol mvcControllerSymbol = WellKnownTypes.MvcController(compilationStartContext.Compilation); INamedTypeSymbol mvcControllerBaseSymbol = WellKnownTypes.MvcControllerBase(compilationStartContext.Compilation); INamedTypeSymbol actionResultSymbol = WellKnownTypes.ActionResult(compilationStartContext.Compilation); if ((mvcControllerSymbol == null && mvcControllerBaseSymbol == null) || actionResultSymbol == null) { // No MVC controllers that return an ActionResult here. return; } MvcAttributeSymbols mvcAttributeSymbols = new MvcAttributeSymbols(compilationStartContext.Compilation); compilationStartContext.RegisterSymbolAction( (SymbolAnalysisContext symbolContext) => { // TODO enhancements: Consider looking at non-ActionResult-derived return types as well. if (!(symbolContext.Symbol is IMethodSymbol methodSymbol) || methodSymbol.MethodKind != MethodKind.Ordinary || methodSymbol.IsStatic || !methodSymbol.IsPublic() || !methodSymbol.ReturnType.Inherits(actionResultSymbol) || // FxCop implementation only looks at ActionResult-derived return types. (!methodSymbol.ContainingType.Inherits(mvcControllerSymbol) && !methodSymbol.ContainingType.Inherits(mvcControllerBaseSymbol))) { return; } ImmutableArray <AttributeData> methodAttributes = methodSymbol.GetAttributes(); mvcAttributeSymbols.ComputeAttributeInfo(methodAttributes, out var verbs, out var isAntiforgeryTokenDefined, out var isAction); if (!isAction) { return; } if (verbs == MvcHttpVerbs.None) { // no verbs specified if (isAntiforgeryTokenDefined) { // antiforgery token attribute is set, but verbs are not specified symbolContext.ReportDiagnostic(Diagnostic.Create(NoVerbsRule, methodSymbol.Locations[0], methodSymbol.MetadataName)); } else { // no verbs, no antiforgery token attribute symbolContext.ReportDiagnostic(Diagnostic.Create(NoVerbsNoTokenRule, methodSymbol.Locations[0], methodSymbol.MetadataName)); } } else { // verbs are defined if (isAntiforgeryTokenDefined) { if (verbs.HasFlag(MvcHttpVerbs.Get)) { symbolContext.ReportDiagnostic(Diagnostic.Create(GetAndTokenRule, methodSymbol.Locations[0], methodSymbol.MetadataName)); if ((verbs & (MvcHttpVerbs.Post | MvcHttpVerbs.Put | MvcHttpVerbs.Delete | MvcHttpVerbs.Patch)) != MvcHttpVerbs.None) { // both verbs, antiforgery token attribute symbolContext.ReportDiagnostic(Diagnostic.Create(GetAndOtherAndTokenRule, methodSymbol.Locations[0], methodSymbol.MetadataName)); } } } else { if ((verbs & (MvcHttpVerbs.Post | MvcHttpVerbs.Put | MvcHttpVerbs.Delete | MvcHttpVerbs.Patch)) != MvcHttpVerbs.None) { // HttpPost, no antiforgery token attribute symbolContext.ReportDiagnostic(Diagnostic.Create(VerbsAndNoTokenRule, methodSymbol.Locations[0], methodSymbol.MetadataName)); } } } }, SymbolKind.Method); } ); }