diff --git a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h --- a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.h @@ -11,6 +11,7 @@ #include "../ClangTidyCheck.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" namespace clang { namespace tidy { @@ -24,12 +25,21 @@ class UncheckedOptionalAccessCheck : public ClangTidyCheck { public: UncheckedOptionalAccessCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + : ClangTidyCheck(Name, Context), + ModelOptions{ + Options.getLocalOrGlobal("IgnoreSmartPointerDereference", false)} {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override { + Options.store(Opts, "IgnoreSmartPointerDereference", + ModelOptions.IgnoreSmartPointerDereference); + } + +private: + dataflow::UncheckedOptionalAccessModelOptions ModelOptions; }; } // namespace bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp @@ -9,18 +9,15 @@ #include "UncheckedOptionalAccessCheck.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" #include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/Any.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" @@ -33,12 +30,14 @@ using ast_matchers::MatchFinder; using dataflow::UncheckedOptionalAccessDiagnoser; using dataflow::UncheckedOptionalAccessModel; +using dataflow::UncheckedOptionalAccessModelOptions; using llvm::Optional; static constexpr llvm::StringLiteral FuncID("fun"); static Optional> -analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { +analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, + UncheckedOptionalAccessModelOptions ModelOptions) { using dataflow::ControlFlowContext; using dataflow::DataflowAnalysisState; using llvm::Expected; @@ -52,7 +51,7 @@ std::make_unique()); dataflow::Environment Env(AnalysisContext, FuncDecl); UncheckedOptionalAccessModel Analysis(ASTCtx); - UncheckedOptionalAccessDiagnoser Diagnoser; + UncheckedOptionalAccessDiagnoser Diagnoser(ModelOptions); std::vector Diagnostics; Expected>>> @@ -98,7 +97,7 @@ return; if (Optional> Errors = - analyzeFunction(*FuncDecl, *Result.Context)) + analyzeFunction(*FuncDecl, *Result.Context, ModelOptions)) for (const SourceLocation &Loc : *Errors) diag(Loc, "unchecked access to optional value"); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access-ignore-smart.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access-ignore-smart.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access-ignore-smart.cpp @@ -0,0 +1,53 @@ +// RUN: %check_clang_tidy %s bugprone-unchecked-optional-access %t -- \ +// RUN: -config="{CheckOptions: [ \ +// RUN: {key: bugprone-unchecked-optional-access.IgnoreSmartPointerDereference, value: true}]}" -- \ +// RUN: -I %S/Inputs/unchecked-optional-access + +#include "absl/types/optional.h" + +// Include some basic cases to ensure that IgnoreSmartPointerDereference doesn't +// disble everything. + +void unchecked_deref_operator_access(const absl::optional &opt) { + *opt; + // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: unchecked access to optional value +} + +void unchecked_value_access(const absl::optional &opt) { + opt.value(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value [bugprone-unchecked-optional-access] +} + +struct Foo { + void foo() const {} +}; + +void unchecked_arrow_operator_access(const absl::optional &opt) { + opt->foo(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional value +} + +// Now, check the two basic cases covered by the option: direct access through +// the smart pointer, and access through the smart pointer followed by a field. +template +struct SmartPtr { + T& operator*() &; + T* operator->(); +}; + +struct Bar { + absl::optional opt; +}; + + +void unchecked_value_access_through_smart_ptr(SmartPtr> s) { + s->value(); + (*s).value(); + +} + +void unchecked_value_access_through_smart_ptr_field(SmartPtr s) { + s->opt.value(); + (*s).opt.value(); + +}