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