public override void Initialize(AnalysisContext analysisContext) { analysisContext.EnableConcurrentExecution(); analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); analysisContext.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartContext) => { WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationStartContext.Compilation); INamedTypeSymbol?mvcControllerSymbol = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemWebMvcController); INamedTypeSymbol?mvcControllerBaseSymbol = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemWebMvcControllerBase); INamedTypeSymbol?actionResultSymbol = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemWebMvcActionResult); 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 IAsyncResult-based action methods. if (!(symbolContext.Symbol is IMethodSymbol methodSymbol) || methodSymbol.MethodKind != MethodKind.Ordinary || methodSymbol.IsStatic || !methodSymbol.IsPublic() || !(methodSymbol.ReturnType.Inherits(actionResultSymbol) || // FxCop implementation only looked at ActionResult-derived return types. wellKnownTypeProvider.IsTaskOfType( methodSymbol.ReturnType, (ITypeSymbol typeArgument) => typeArgument.Inherits(actionResultSymbol))) || (!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); } ); }