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/WatchedLiteralsSolver.h"
22 #include "clang/Basic/SourceLocation.h"
23 #include "llvm/ADT/Any.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/STLExtras.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::UncheckedOptionalAccessDiagnoser;
35 using dataflow::UncheckedOptionalAccessModel;
36 using llvm::Optional;
37
38 static constexpr llvm::StringLiteral FuncID("fun");
39
40 static Optional<std::vector<SourceLocation>>
analyzeFunction(const FunctionDecl & FuncDecl,ASTContext & ASTCtx)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 UncheckedOptionalAccessDiagnoser Diagnoser;
56 std::vector<SourceLocation> Diagnostics;
57 Expected<std::vector<
58 Optional<DataflowAnalysisState<UncheckedOptionalAccessModel::Lattice>>>>
59 BlockToOutputState = dataflow::runDataflowAnalysis(
60 *Context, Analysis, Env,
61 [&ASTCtx, &Diagnoser, &Diagnostics](
62 const Stmt *Stmt,
63 const DataflowAnalysisState<UncheckedOptionalAccessModel::Lattice>
64 &State) mutable {
65 auto StmtDiagnostics = Diagnoser.diagnose(ASTCtx, Stmt, State.Env);
66 llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
67 });
68 if (!BlockToOutputState)
69 return llvm::None;
70
71 return Diagnostics;
72 }
73
registerMatchers(MatchFinder * Finder)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
check(const MatchFinder::MatchResult & Result)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<std::vector<SourceLocation>> Errors =
101 analyzeFunction(*FuncDecl, *Result.Context))
102 for (const SourceLocation &Loc : *Errors)
103 diag(Loc, "unchecked access to optional value");
104 }
105
106 } // namespace bugprone
107 } // namespace tidy
108 } // namespace clang
109