Exemplo n.º 1
0
        private async Task <ActualApiResponseMetadata?> RunInspectReturnStatementSyntax(string source, string test)
        {
            var project     = DiagnosticProject.Create(GetType().Assembly, new[] { source });
            var compilation = await project.GetCompilationAsync();

            var symbolCache = new ApiControllerSymbolCache(compilation);

            var returnType = compilation.GetTypeByMetadataName($"{Namespace}.{test}");
            var syntaxTree = returnType.DeclaringSyntaxReferences[0].SyntaxTree;

            var method          = (IMethodSymbol)returnType.GetMembers().First();
            var methodSyntax    = syntaxTree.GetRoot().FindNode(method.Locations[0].SourceSpan);
            var returnStatement = methodSyntax.DescendantNodes().OfType <ReturnStatementSyntax>().First();

            return(SymbolApiResponseMetadataProvider.InspectReturnStatementSyntax(
                       symbolCache,
                       compilation.GetSemanticModel(syntaxTree),
                       returnStatement,
                       CancellationToken.None));
        }
        private void InitializeWorker(CompilationStartAnalysisContext context, ApiControllerSymbolCache symbolCache)
        {
            context.RegisterOperationAction(operationAnalysisContext =>
            {
                var ifOperation = (IConditionalOperation)operationAnalysisContext.Operation;
                if (!(ifOperation.Syntax is IfStatementSyntax ifStatement))
                {
                    return;
                }

                if (ifOperation.WhenTrue == null || ifOperation.WhenFalse != null)
                {
                    // We only support expressions of the format
                    // if (!ModelState.IsValid)
                    // or
                    // if (ModelState.IsValid == false)
                    // If the conditional is misisng a true condition or has an else expression, skip this operation.
                    return;
                }

                var parent = ifOperation.Parent;
                if (parent?.Kind == OperationKind.Block)
                {
                    parent = parent?.Parent;
                }

                if (parent?.Kind != OperationKind.MethodBodyOperation)
                {
                    // Only support top-level ModelState IsValid checks.
                    return;
                }

                var trueStatement = UnwrapSingleStatementBlock(ifOperation.WhenTrue);
                if (trueStatement.Kind != OperationKind.Return)
                {
                    // We need to verify that the if statement does a ModelState.IsValid check and that the block inside contains
                    // a single return statement returning a 400. We'l get to it in just a bit
                    return;
                }

                if (!(parent.Syntax is MethodDeclarationSyntax methodSyntax))
                {
                    return;
                }

                var semanticModel = operationAnalysisContext.Compilation.GetSemanticModel(methodSyntax.SyntaxTree);
                var methodSymbol  = semanticModel.GetDeclaredSymbol(methodSyntax, operationAnalysisContext.CancellationToken);

                if (!ApiControllerFacts.IsApiControllerAction(symbolCache, methodSymbol))
                {
                    // Not a ApiController. Nothing to do here.
                    return;
                }

                if (!IsModelStateIsValidCheck(symbolCache, ifOperation.Condition))
                {
                    return;
                }

                var returnOperation = (IReturnOperation)trueStatement;

                var returnValue = returnOperation.ReturnedValue;
                if (returnValue == null ||
                    !symbolCache.IActionResult.IsAssignableFrom(returnValue.Type))
                {
                    return;
                }

                var returnStatementSyntax = (ReturnStatementSyntax)returnOperation.Syntax;
                var actualMetadata        = SymbolApiResponseMetadataProvider.InspectReturnStatementSyntax(
                    symbolCache,
                    semanticModel,
                    returnStatementSyntax,
                    operationAnalysisContext.CancellationToken);

                if (actualMetadata == null || actualMetadata.Value.StatusCode != 400)
                {
                    return;
                }

                var additionalLocations = new[]
                {
                    ifStatement.GetLocation(),
                    returnStatementSyntax.GetLocation(),
                };

                operationAnalysisContext.ReportDiagnostic(
                    Diagnostic.Create(
                        ApiDiagnosticDescriptors.API1003_ApiActionsDoNotRequireExplicitModelValidationCheck,
                        ifStatement.GetLocation(),
                        additionalLocations: additionalLocations));
            }, OperationKind.Conditional);
        }