1 //===--- UncheckedOptionalAccessCheck.cpp - clang-tidy --------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "UncheckedOptionalAccessCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/AST/DeclTemplate.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/ASTMatchers/ASTMatchers.h" 15 #include "clang/Analysis/CFG.h" 16 #include "clang/Analysis/FlowSensitive/ControlFlowContext.h" 17 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 18 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 19 #include "clang/Analysis/FlowSensitive/DataflowLattice.h" 20 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 21 #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" 22 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" 23 #include "clang/Basic/SourceLocation.h" 24 #include "llvm/ADT/Any.h" 25 #include "llvm/ADT/Optional.h" 26 #include "llvm/Support/Error.h" 27 #include <memory> 28 #include <vector> 29 30 namespace clang { 31 namespace tidy { 32 namespace bugprone { 33 using ast_matchers::MatchFinder; 34 using dataflow::SourceLocationsLattice; 35 using dataflow::UncheckedOptionalAccessModel; 36 using llvm::Optional; 37 38 static constexpr llvm::StringLiteral FuncID("fun"); 39 40 static Optional<SourceLocationsLattice> 41 analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { 42 using dataflow::ControlFlowContext; 43 using dataflow::DataflowAnalysisState; 44 using llvm::Expected; 45 46 Expected<ControlFlowContext> Context = 47 ControlFlowContext::build(&FuncDecl, FuncDecl.getBody(), &ASTCtx); 48 if (!Context) 49 return llvm::None; 50 51 dataflow::DataflowAnalysisContext AnalysisContext( 52 std::make_unique<dataflow::WatchedLiteralsSolver>()); 53 dataflow::Environment Env(AnalysisContext, FuncDecl); 54 UncheckedOptionalAccessModel Analysis(ASTCtx); 55 Expected<std::vector<Optional<DataflowAnalysisState<SourceLocationsLattice>>>> 56 BlockToOutputState = 57 dataflow::runDataflowAnalysis(*Context, Analysis, Env); 58 if (!BlockToOutputState) 59 return llvm::None; 60 assert(Context->getCFG().getExit().getBlockID() < BlockToOutputState->size()); 61 62 const Optional<DataflowAnalysisState<SourceLocationsLattice>> 63 &ExitBlockState = 64 (*BlockToOutputState)[Context->getCFG().getExit().getBlockID()]; 65 // `runDataflowAnalysis` doesn't guarantee that the exit block is visited; 66 // for example, when it is unreachable. 67 // FIXME: Diagnose violations even when the exit block is unreachable. 68 if (!ExitBlockState.hasValue()) 69 return llvm::None; 70 71 return std::move(ExitBlockState->Lattice); 72 } 73 74 void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) { 75 using namespace ast_matchers; 76 77 auto HasOptionalCallDescendant = hasDescendant(callExpr(callee(cxxMethodDecl( 78 ofClass(UncheckedOptionalAccessModel::optionalClassDecl()))))); 79 Finder->addMatcher( 80 decl(anyOf(functionDecl(unless(isExpansionInSystemHeader()), 81 // FIXME: Remove the filter below when lambdas are 82 // well supported by the check. 83 unless(hasDeclContext(cxxRecordDecl(isLambda()))), 84 hasBody(HasOptionalCallDescendant)), 85 cxxConstructorDecl(hasAnyConstructorInitializer( 86 withInitializer(HasOptionalCallDescendant))))) 87 .bind(FuncID), 88 this); 89 } 90 91 void UncheckedOptionalAccessCheck::check( 92 const MatchFinder::MatchResult &Result) { 93 if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred()) 94 return; 95 96 const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>(FuncID); 97 if (FuncDecl->isTemplated()) 98 return; 99 100 if (Optional<SourceLocationsLattice> Errors = 101 analyzeFunction(*FuncDecl, *Result.Context)) 102 for (const SourceLocation &Loc : Errors->getSourceLocations()) 103 diag(Loc, "unchecked access to optional value"); 104 } 105 106 } // namespace bugprone 107 } // namespace tidy 108 } // namespace clang 109