private async Task <(bool result, IList <ActualApiResponseMetadata> responseMetadatas, TestSource testSource)> TryGetActualResponseMetadata(string typeName, string methodName) { var testSource = MvcTestSource.Read(GetType().Name, "TryGetActualResponseMetadataTests"); var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source }); var compilation = await GetCompilation("TryGetActualResponseMetadataTests"); var type = compilation.GetTypeByMetadataName(typeName); var method = (IMethodSymbol)type.GetMembers(methodName).First(); var symbolCache = new ApiControllerSymbolCache(compilation); var syntaxTree = method.DeclaringSyntaxReferences[0].SyntaxTree; var methodSyntax = (MethodDeclarationSyntax)syntaxTree.GetRoot().FindNode(method.Locations[0].SourceSpan); var semanticModel = compilation.GetSemanticModel(syntaxTree); var result = SymbolApiResponseMetadataProvider.TryGetActualResponseMetadata(symbolCache, semanticModel, methodSyntax, CancellationToken.None, out var responseMetadatas); return(result, responseMetadatas, testSource); }
private ICollection <int> CalculateStatusCodesToApply(CodeActionContext context, IList <DeclaredApiResponseMetadata> declaredResponseMetadata) { if (!SymbolApiResponseMetadataProvider.TryGetActualResponseMetadata(context.SymbolCache, context.SemanticModel, context.MethodSyntax, context.CancellationToken, out var actualResponseMetadata)) { // If we cannot parse metadata correctly, don't offer fixes. return(Array.Empty <int>()); } var statusCodes = new HashSet <int>(); foreach (var metadata in actualResponseMetadata) { if (DeclaredApiResponseMetadata.TryGetDeclaredMetadata(declaredResponseMetadata, metadata, result: out var declaredMetadata) && declaredMetadata.AttributeSource == context.Method) { // A ProducesResponseType attribute is declared on the method for the current status code. continue; } statusCodes.Add(metadata.IsDefaultResponse ? 200 : metadata.StatusCode); } return(statusCodes); }
private void InitializeWorker(CompilationStartAnalysisContext compilationStartAnalysisContext, ApiControllerSymbolCache symbolCache) { compilationStartAnalysisContext.RegisterSyntaxNodeAction(syntaxNodeContext => { var cancellationToken = syntaxNodeContext.CancellationToken; var methodSyntax = (MethodDeclarationSyntax)syntaxNodeContext.Node; var semanticModel = syntaxNodeContext.SemanticModel; var method = semanticModel.GetDeclaredSymbol(methodSyntax, syntaxNodeContext.CancellationToken); if (!ApiControllerFacts.IsApiControllerAction(symbolCache, method)) { return; } var declaredResponseMetadata = SymbolApiResponseMetadataProvider.GetDeclaredResponseMetadata(symbolCache, method); var hasUnreadableStatusCodes = !SymbolApiResponseMetadataProvider.TryGetActualResponseMetadata(symbolCache, semanticModel, methodSyntax, cancellationToken, out var actualResponseMetadata); var hasUndocumentedStatusCodes = false; foreach (var actualMetadata in actualResponseMetadata) { var location = actualMetadata.ReturnStatement.GetLocation(); if (!DeclaredApiResponseMetadata.Contains(declaredResponseMetadata, actualMetadata)) { hasUndocumentedStatusCodes = true; if (actualMetadata.IsDefaultResponse) { syntaxNodeContext.ReportDiagnostic(Diagnostic.Create( ApiDiagnosticDescriptors.API1001_ActionReturnsUndocumentedSuccessResult, location)); } else { syntaxNodeContext.ReportDiagnostic(Diagnostic.Create( ApiDiagnosticDescriptors.API1000_ActionReturnsUndocumentedStatusCode, location, actualMetadata.StatusCode)); } } } if (hasUndocumentedStatusCodes || hasUnreadableStatusCodes) { // If we produced analyzer warnings about undocumented status codes, don't attempt to determine // if there are documented status codes that are missing from the method body. return; } for (var i = 0; i < declaredResponseMetadata.Count; i++) { var declaredMetadata = declaredResponseMetadata[i]; if (!Contains(actualResponseMetadata, declaredMetadata)) { syntaxNodeContext.ReportDiagnostic(Diagnostic.Create( ApiDiagnosticDescriptors.API1002_ActionDoesNotReturnDocumentedStatusCode, methodSyntax.Identifier.GetLocation(), declaredMetadata.StatusCode)); } } }, SyntaxKind.MethodDeclaration); }