From cafb8b4ff2c38f81e65f97193eb1d8d16c581522 Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 29 Jun 2022 19:19:58 +0000 Subject: [PATCH] [clang][dataflow] Use diagnosis API in optional checker Followup to D127898. This patch updates `bugprone-unchecked-optional-access` to use the new `diagnoseCFG` function instead of just looking at the exit block. A followup to this will update the optional model itself to use a noop lattice rather than redundantly computing the diagnostics in both phases of the analysis. Reviewed By: ymandel, sgatev, gribozavr2, xazax.hun Differential Revision: https://reviews.llvm.org/D128352 --- .../bugprone/UncheckedOptionalAccessCheck.cpp | 33 +++++++++++----------- .../Analysis/FlowSensitive/DataflowAnalysis.h | 29 +++++++++++++++---- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp index 07fb19f..9766450 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UncheckedOptionalAccessCheck.cpp @@ -23,6 +23,7 @@ #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" #include #include @@ -32,12 +33,13 @@ namespace tidy { namespace bugprone { using ast_matchers::MatchFinder; using dataflow::SourceLocationsLattice; +using dataflow::UncheckedOptionalAccessDiagnoser; using dataflow::UncheckedOptionalAccessModel; using llvm::Optional; static constexpr llvm::StringLiteral FuncID("fun"); -static Optional +static Optional> analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { using dataflow::ControlFlowContext; using dataflow::DataflowAnalysisState; @@ -52,23 +54,22 @@ analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { std::make_unique()); dataflow::Environment Env(AnalysisContext, FuncDecl); UncheckedOptionalAccessModel Analysis(ASTCtx); + UncheckedOptionalAccessDiagnoser Diagnoser; + std::vector Diagnostics; Expected>>> - BlockToOutputState = - dataflow::runDataflowAnalysis(*Context, Analysis, Env); + BlockToOutputState = dataflow::runDataflowAnalysis( + *Context, Analysis, Env, + [&ASTCtx, &Diagnoser, + &Diagnostics](const Stmt *Stmt, + const DataflowAnalysisState + &State) mutable { + auto StmtDiagnostics = Diagnoser.diagnose(ASTCtx, Stmt, State.Env); + llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics)); + }); if (!BlockToOutputState) return llvm::None; - assert(Context->getCFG().getExit().getBlockID() < BlockToOutputState->size()); - const Optional> - &ExitBlockState = - (*BlockToOutputState)[Context->getCFG().getExit().getBlockID()]; - // `runDataflowAnalysis` doesn't guarantee that the exit block is visited; - // for example, when it is unreachable. - // FIXME: Diagnose violations even when the exit block is unreachable. - if (!ExitBlockState) - return llvm::None; - - return std::move(ExitBlockState->Lattice); + return Diagnostics; } void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) { @@ -97,9 +98,9 @@ void UncheckedOptionalAccessCheck::check( if (FuncDecl->isTemplated()) return; - if (Optional Errors = + if (Optional> Errors = analyzeFunction(*FuncDecl, *Result.Context)) - for (const SourceLocation &Loc : Errors->getSourceLocations()) + for (const SourceLocation &Loc : *Errors) diag(Loc, "unchecked access to optional value"); } diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index f4d6a22..40ac95b 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -109,14 +109,33 @@ template struct DataflowAnalysisState { /// dataflow analysis states that model the respective basic blocks. The /// returned vector, if any, will have the same size as the number of CFG /// blocks, with indices corresponding to basic block IDs. Returns an error if -/// the dataflow analysis cannot be performed successfully. +/// the dataflow analysis cannot be performed successfully. Otherwise, calls +/// `PostVisitStmt` on each statement with the final analysis results at that +/// program point. template llvm::Expected>>> -runDataflowAnalysis(const ControlFlowContext &CFCtx, AnalysisT &Analysis, - const Environment &InitEnv) { - auto TypeErasedBlockStates = - runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv); +runDataflowAnalysis( + const ControlFlowContext &CFCtx, AnalysisT &Analysis, + const Environment &InitEnv, + std::function &)> + PostVisitStmt = nullptr) { + std::function + PostVisitStmtClosure = nullptr; + if (PostVisitStmt != nullptr) { + PostVisitStmtClosure = [&PostVisitStmt]( + const Stmt *Stmt, + const TypeErasedDataflowAnalysisState &State) { + auto *Lattice = + llvm::any_cast(&State.Lattice.Value); + PostVisitStmt(Stmt, DataflowAnalysisState{ + *Lattice, State.Env}); + }; + } + + auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis( + CFCtx, Analysis, InitEnv, PostVisitStmtClosure); if (!TypeErasedBlockStates) return TypeErasedBlockStates.takeError(); -- 2.7.4