UncheckedOptionalAccessCheck.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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. #include "UncheckedOptionalAccessCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/AST/DeclCXX.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/ASTMatchers/ASTMatchers.h"
  13. #include "clang/Analysis/CFG.h"
  14. #include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
  15. #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
  16. #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
  17. #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
  18. #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
  19. #include "clang/Basic/SourceLocation.h"
  20. #include "llvm/ADT/STLExtras.h"
  21. #include "llvm/Support/Error.h"
  22. #include <memory>
  23. #include <optional>
  24. #include <vector>
  25. namespace clang::tidy::bugprone {
  26. using ast_matchers::MatchFinder;
  27. using dataflow::UncheckedOptionalAccessDiagnoser;
  28. using dataflow::UncheckedOptionalAccessModel;
  29. using dataflow::UncheckedOptionalAccessModelOptions;
  30. static constexpr llvm::StringLiteral FuncID("fun");
  31. static std::optional<std::vector<SourceLocation>>
  32. analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx,
  33. UncheckedOptionalAccessModelOptions ModelOptions) {
  34. using dataflow::ControlFlowContext;
  35. using dataflow::DataflowAnalysisState;
  36. using llvm::Expected;
  37. Expected<ControlFlowContext> Context =
  38. ControlFlowContext::build(&FuncDecl, *FuncDecl.getBody(), ASTCtx);
  39. if (!Context)
  40. return std::nullopt;
  41. dataflow::DataflowAnalysisContext AnalysisContext(
  42. std::make_unique<dataflow::WatchedLiteralsSolver>());
  43. dataflow::Environment Env(AnalysisContext, FuncDecl);
  44. UncheckedOptionalAccessModel Analysis(ASTCtx);
  45. UncheckedOptionalAccessDiagnoser Diagnoser(ModelOptions);
  46. std::vector<SourceLocation> Diagnostics;
  47. Expected<std::vector<std::optional<
  48. DataflowAnalysisState<UncheckedOptionalAccessModel::Lattice>>>>
  49. BlockToOutputState = dataflow::runDataflowAnalysis(
  50. *Context, Analysis, Env,
  51. [&ASTCtx, &Diagnoser, &Diagnostics](
  52. const CFGElement &Elt,
  53. const DataflowAnalysisState<UncheckedOptionalAccessModel::Lattice>
  54. &State) mutable {
  55. auto EltDiagnostics = Diagnoser.diagnose(ASTCtx, &Elt, State.Env);
  56. llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
  57. });
  58. if (!BlockToOutputState)
  59. return std::nullopt;
  60. return Diagnostics;
  61. }
  62. void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) {
  63. using namespace ast_matchers;
  64. auto HasOptionalCallDescendant = hasDescendant(callExpr(callee(cxxMethodDecl(
  65. ofClass(UncheckedOptionalAccessModel::optionalClassDecl())))));
  66. Finder->addMatcher(
  67. decl(anyOf(functionDecl(unless(isExpansionInSystemHeader()),
  68. // FIXME: Remove the filter below when lambdas are
  69. // well supported by the check.
  70. unless(hasDeclContext(cxxRecordDecl(isLambda()))),
  71. hasBody(HasOptionalCallDescendant)),
  72. cxxConstructorDecl(hasAnyConstructorInitializer(
  73. withInitializer(HasOptionalCallDescendant)))))
  74. .bind(FuncID),
  75. this);
  76. }
  77. void UncheckedOptionalAccessCheck::check(
  78. const MatchFinder::MatchResult &Result) {
  79. if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred())
  80. return;
  81. const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>(FuncID);
  82. if (FuncDecl->isTemplated())
  83. return;
  84. if (std::optional<std::vector<SourceLocation>> Errors =
  85. analyzeFunction(*FuncDecl, *Result.Context, ModelOptions))
  86. for (const SourceLocation &Loc : *Errors)
  87. diag(Loc, "unchecked access to optional value");
  88. }
  89. } // namespace clang::tidy::bugprone