[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
This commit is contained in:
Sam Estep 2022-06-29 19:19:58 +00:00
parent 2a33d12642
commit 2adaca532d
2 changed files with 41 additions and 21 deletions

View File

@ -23,6 +23,7 @@
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/Any.h" #include "llvm/ADT/Any.h"
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -32,12 +33,13 @@ namespace tidy {
namespace bugprone { namespace bugprone {
using ast_matchers::MatchFinder; using ast_matchers::MatchFinder;
using dataflow::SourceLocationsLattice; using dataflow::SourceLocationsLattice;
using dataflow::UncheckedOptionalAccessDiagnoser;
using dataflow::UncheckedOptionalAccessModel; using dataflow::UncheckedOptionalAccessModel;
using llvm::Optional; using llvm::Optional;
static constexpr llvm::StringLiteral FuncID("fun"); static constexpr llvm::StringLiteral FuncID("fun");
static Optional<SourceLocationsLattice> static Optional<std::vector<SourceLocation>>
analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) {
using dataflow::ControlFlowContext; using dataflow::ControlFlowContext;
using dataflow::DataflowAnalysisState; using dataflow::DataflowAnalysisState;
@ -52,23 +54,22 @@ analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) {
std::make_unique<dataflow::WatchedLiteralsSolver>()); std::make_unique<dataflow::WatchedLiteralsSolver>());
dataflow::Environment Env(AnalysisContext, FuncDecl); dataflow::Environment Env(AnalysisContext, FuncDecl);
UncheckedOptionalAccessModel Analysis(ASTCtx); UncheckedOptionalAccessModel Analysis(ASTCtx);
UncheckedOptionalAccessDiagnoser Diagnoser;
std::vector<SourceLocation> Diagnostics;
Expected<std::vector<Optional<DataflowAnalysisState<SourceLocationsLattice>>>> Expected<std::vector<Optional<DataflowAnalysisState<SourceLocationsLattice>>>>
BlockToOutputState = BlockToOutputState = dataflow::runDataflowAnalysis(
dataflow::runDataflowAnalysis(*Context, Analysis, Env); *Context, Analysis, Env,
[&ASTCtx, &Diagnoser,
&Diagnostics](const Stmt *Stmt,
const DataflowAnalysisState<SourceLocationsLattice>
&State) mutable {
auto StmtDiagnostics = Diagnoser.diagnose(ASTCtx, Stmt, State.Env);
llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
});
if (!BlockToOutputState) if (!BlockToOutputState)
return llvm::None; return llvm::None;
assert(Context->getCFG().getExit().getBlockID() < BlockToOutputState->size());
const Optional<DataflowAnalysisState<SourceLocationsLattice>> return Diagnostics;
&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);
} }
void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) { void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) {
@ -97,9 +98,9 @@ void UncheckedOptionalAccessCheck::check(
if (FuncDecl->isTemplated()) if (FuncDecl->isTemplated())
return; return;
if (Optional<SourceLocationsLattice> Errors = if (Optional<std::vector<SourceLocation>> Errors =
analyzeFunction(*FuncDecl, *Result.Context)) analyzeFunction(*FuncDecl, *Result.Context))
for (const SourceLocation &Loc : Errors->getSourceLocations()) for (const SourceLocation &Loc : *Errors)
diag(Loc, "unchecked access to optional value"); diag(Loc, "unchecked access to optional value");
} }

View File

@ -109,14 +109,33 @@ template <typename LatticeT> struct DataflowAnalysisState {
/// dataflow analysis states that model the respective basic blocks. The /// dataflow analysis states that model the respective basic blocks. The
/// returned vector, if any, will have the same size as the number of CFG /// 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 /// 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 <typename AnalysisT> template <typename AnalysisT>
llvm::Expected<std::vector< llvm::Expected<std::vector<
llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>> llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(const ControlFlowContext &CFCtx, AnalysisT &Analysis, runDataflowAnalysis(
const Environment &InitEnv) { const ControlFlowContext &CFCtx, AnalysisT &Analysis,
auto TypeErasedBlockStates = const Environment &InitEnv,
runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv); std::function<void(const Stmt *, const DataflowAnalysisState<
typename AnalysisT::Lattice> &)>
PostVisitStmt = nullptr) {
std::function<void(const Stmt *, const TypeErasedDataflowAnalysisState &)>
PostVisitStmtClosure = nullptr;
if (PostVisitStmt != nullptr) {
PostVisitStmtClosure = [&PostVisitStmt](
const Stmt *Stmt,
const TypeErasedDataflowAnalysisState &State) {
auto *Lattice =
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
PostVisitStmt(Stmt, DataflowAnalysisState<typename AnalysisT::Lattice>{
*Lattice, State.Env});
};
}
auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis(
CFCtx, Analysis, InitEnv, PostVisitStmtClosure);
if (!TypeErasedBlockStates) if (!TypeErasedBlockStates)
return TypeErasedBlockStates.takeError(); return TypeErasedBlockStates.takeError();