forked from OSchip/llvm-project
[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:
parent
2a33d12642
commit
2adaca532d
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue