xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp (revision 004a7cea705d63441307a5e0429b6786d49042c2)
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/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
14 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15 #include "clang/Basic/SourceLocation.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/Support/Error.h"
18 
19 namespace clang::tidy::bugprone {
20 using ast_matchers::MatchFinder;
21 using dataflow::UncheckedOptionalAccessDiagnoser;
22 using dataflow::UncheckedOptionalAccessModel;
23 
24 static constexpr llvm::StringLiteral FuncID("fun");
25 
registerMatchers(MatchFinder * Finder)26 void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) {
27   using namespace ast_matchers;
28 
29   auto HasOptionalCallDescendant = hasDescendant(callExpr(callee(cxxMethodDecl(
30       ofClass(UncheckedOptionalAccessModel::optionalClassDecl())))));
31   Finder->addMatcher(
32       decl(anyOf(functionDecl(unless(isExpansionInSystemHeader()),
33                               // FIXME: Remove the filter below when lambdas are
34                               // well supported by the check.
35                               unless(hasDeclContext(cxxRecordDecl(isLambda()))),
36                               hasBody(HasOptionalCallDescendant)),
37                  cxxConstructorDecl(hasAnyConstructorInitializer(
38                      withInitializer(HasOptionalCallDescendant)))))
39           .bind(FuncID),
40       this);
41 }
42 
check(const MatchFinder::MatchResult & Result)43 void UncheckedOptionalAccessCheck::check(
44     const MatchFinder::MatchResult &Result) {
45   if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred())
46     return;
47 
48   const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>(FuncID);
49   if (FuncDecl->isTemplated())
50     return;
51 
52   UncheckedOptionalAccessDiagnoser Diagnoser(ModelOptions);
53   // FIXME: Allow user to set the (defaulted) SAT iterations max for
54   // `diagnoseFunction` with config options.
55   if (llvm::Expected<llvm::SmallVector<SourceLocation>> Locs =
56           dataflow::diagnoseFunction<UncheckedOptionalAccessModel,
57                                      SourceLocation>(*FuncDecl, *Result.Context,
58                                                      Diagnoser))
59     for (const SourceLocation &Loc : *Locs)
60       diag(Loc, "unchecked access to optional value");
61   else
62     llvm::consumeError(Locs.takeError());
63 }
64 
65 } // namespace clang::tidy::bugprone
66