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 @@ -18,7 +18,8 @@ #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" +#include "clang/Analysis/FlowSensitive/NoopLattice.h" +#include "clang/Analysis/FlowSensitive/SourceLocations.h" #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/Any.h" @@ -31,14 +32,16 @@ namespace tidy { namespace bugprone { using ast_matchers::MatchFinder; -using dataflow::SourceLocationsLattice; +using dataflow::NoopLattice; +using dataflow::SourceLocations; +using dataflow::TypeErasedDataflowAnalysisState; using dataflow::UncheckedOptionalAccessModel; using llvm::Optional; static constexpr llvm::StringLiteral FuncID("fun"); -static Optional -analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) { +static Optional analyzeFunction(const FunctionDecl &FuncDecl, + ASTContext &ASTCtx) { using dataflow::ControlFlowContext; using dataflow::DataflowAnalysisState; using llvm::Expected; @@ -52,23 +55,29 @@ std::make_unique()); dataflow::Environment Env(AnalysisContext, FuncDecl); UncheckedOptionalAccessModel Analysis(ASTCtx); - Expected>>> + Expected>> BlockToOutputState = - dataflow::runDataflowAnalysis(*Context, Analysis, Env); + dataflow::runTypeErasedDataflowAnalysis(*Context, Analysis, Env); if (!BlockToOutputState) return llvm::None; - assert(Context->getCFG().getExit().getBlockID() < BlockToOutputState->size()); + auto &BlockStates = *BlockToOutputState; - 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.hasValue()) - return llvm::None; + SourceLocations Locs; + for (const CFGBlock *Block : Context->getCFG()) { + // Skip blocks that were not evaluated. + if (!BlockStates[Block->getBlockID()].hasValue()) + continue; + + transferBlock( + *Context, BlockStates, *Block, Env, Analysis, + [&Locs, &Analysis](const clang::CFGStmt &Stmt, + const TypeErasedDataflowAnalysisState &State) { + auto *Lattice = llvm::any_cast(&State.Lattice.Value); + Analysis.check(Stmt.getStmt(), Locs, *Lattice, State.Env); + }); + } - return std::move(ExitBlockState->Lattice); + return std::move(Locs); } void UncheckedOptionalAccessCheck::registerMatchers(MatchFinder *Finder) { @@ -97,9 +106,9 @@ 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/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -131,8 +131,9 @@ clang/include/clang/Analysis/FlowSensitive/DataflowWorklist.h clang/include/clang/Analysis/FlowSensitive/MapLattice.h clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h +clang/include/clang/Analysis/FlowSensitive/NoopLattice.h clang/include/clang/Analysis/FlowSensitive/Solver.h -clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h +clang/include/clang/Analysis/FlowSensitive/SourceLocations.h clang/include/clang/Analysis/FlowSensitive/StorageLocation.h clang/include/clang/Analysis/FlowSensitive/Transfer.h clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h @@ -309,7 +310,7 @@ clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp -clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp +clang/lib/Analysis/FlowSensitive/SourceLocations.cpp clang/lib/Analysis/FlowSensitive/Transfer.cpp clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp @@ -636,7 +637,6 @@ clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp clang/unittests/Analysis/FlowSensitive/SolverTest.cpp -clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.h clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -32,6 +32,8 @@ namespace clang { namespace dataflow { +using NoopDiags = std::tuple<>; + /// Base class template for dataflow analyses built on a single lattice type. /// /// Requirements: @@ -47,6 +49,8 @@ /// * `bool merge(QualType, const Value &, const Value &, Value &, /// Environment &)` - joins distinct values. This could be a strict /// lattice join or a more general widening operation. +/// * `void check(const Stmt *, DiagsT &, const LatticeT &, const Environment +/// &)` - collects diagnostics for a given statement and analysis result. /// /// `LatticeT` is a bounded join-semilattice that is used by `Derived` and must /// provide the following public members: @@ -56,11 +60,15 @@ /// made to it; /// * `bool operator==(const LatticeT &) const` - returns true if and only if /// the object is equal to the argument. -template +/// +/// `DiagsT` must have a default constructor. +template class DataflowAnalysis : public TypeErasedDataflowAnalysis { public: /// Bounded join-semilattice that is used in the analysis. using Lattice = LatticeT; + /// Diagnostics set that is used after the analysis. + using Diags = DiagsT; explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {} explicit DataflowAnalysis(ASTContext &Context, bool ApplyBuiltinTransfer) @@ -92,6 +100,9 @@ static_cast(this)->transfer(Stmt, L, Env); } + virtual void check(const Stmt *Stmt, Diags &E, const Lattice &L, + const Environment &Env) {} + private: ASTContext &Context; }; diff --git a/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h b/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h --- a/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h +++ b/clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h @@ -44,6 +44,15 @@ Environment &Env; }; +/// A common form of state shared between the cases of a check function. +template struct CheckState { + CheckState(DiagsT &Diags, const Environment &Env) : Diags(Diags), Env(Env) {} + + /// Current set of diagnostics. + DiagsT &Diags; + const Environment &Env; +}; + /// Matches against `Stmt` and, based on its structure, dispatches to an /// appropriate handler. template diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h +++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h @@ -19,7 +19,10 @@ #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" +#include "clang/Analysis/FlowSensitive/NoopLattice.h" +#include "clang/Analysis/FlowSensitive/SourceLocations.h" +#include "clang/Basic/SourceLocation.h" +#include namespace clang { namespace dataflow { @@ -36,15 +39,12 @@ bool IgnoreSmartPointerDereference = false; }; -/// Dataflow analysis that discovers unsafe accesses of optional values and -/// adds the respective source locations to the lattice. +/// Dataflow analysis that models whether optionals hold values or not. /// /// Models the `std::optional`, `absl::optional`, and `base::Optional` types. -/// -/// FIXME: Consider separating the models from the unchecked access analysis. class UncheckedOptionalAccessModel - : public DataflowAnalysis { + : public DataflowAnalysis { public: UncheckedOptionalAccessModel( ASTContext &AstContext, UncheckedOptionalAccessModelOptions Options = {}); @@ -52,12 +52,14 @@ /// Returns a matcher for the optional classes covered by this model. static ast_matchers::DeclarationMatcher optionalClassDecl(); - static SourceLocationsLattice initialElement() { - return SourceLocationsLattice(); - } + static NoopLattice initialElement() { return {}; } + + void transfer(const Stmt *Stmt, NoopLattice &State, Environment &Env); - void transfer(const Stmt *Stmt, SourceLocationsLattice &State, - Environment &Env); + /// Discovers unsafe accesses of optional values and adds the respective + /// source locations to a set. + void check(const Stmt *Stmt, SourceLocations &Locs, const NoopLattice &, + const Environment &Env) override; bool compareEquivalent(QualType Type, const Value &Val1, const Environment &Env1, const Value &Val2, @@ -68,7 +70,14 @@ Environment &MergedEnv) override; private: - MatchSwitch> TransferMatchSwitch; + UncheckedOptionalAccessModel( + ASTContext &AstContext, + std::pair>, + MatchSwitch>> + MatchSwitches); + + MatchSwitch> TransferMatchSwitch; + MatchSwitch> CheckMatchSwitch; }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h b/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h @@ -0,0 +1,41 @@ +//===-- NoopLattice.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the lattice with exactly one element. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H +#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H + +#include "clang/Analysis/FlowSensitive/DataflowLattice.h" +#include + +namespace clang { +namespace dataflow { + +/// Trivial lattice for dataflow analysis with exactly one element. +/// +/// Useful for analyses that only need the Environment and nothing more. +class NoopLattice { +public: + bool operator==(const NoopLattice &Other) const { return true; } + + LatticeJoinEffect join(const NoopLattice &Other) { + return LatticeJoinEffect::Unchanged; + } +}; + +inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) { + return OS << "noop"; +} + +} // namespace dataflow +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H diff --git a/clang/include/clang/Analysis/FlowSensitive/SourceLocations.h b/clang/include/clang/Analysis/FlowSensitive/SourceLocations.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Analysis/FlowSensitive/SourceLocations.h @@ -0,0 +1,35 @@ +//===-- SourceLocations.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a type alias for collecting source locations of interest. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H +#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H + +#include "clang/AST/ASTContext.h" +#include "clang/Analysis/FlowSensitive/DataflowLattice.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/DenseSet.h" +#include +#include + +namespace clang { +namespace dataflow { + +/// Set of source locations. +using SourceLocations = llvm::DenseSet; + +/// Returns a string that represents the source locations of the lattice. +std::string DebugString(const SourceLocations &Locs, const ASTContext &Context); + +} // namespace dataflow +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_H diff --git a/clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h b/clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h deleted file mode 100644 --- a/clang/include/clang/Analysis/FlowSensitive/SourceLocationsLattice.h +++ /dev/null @@ -1,65 +0,0 @@ -//===-- SourceLocationsLattice.h --------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines a lattice that collects source locations of interest. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H -#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H - -#include "clang/AST/ASTContext.h" -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/DenseSet.h" -#include -#include - -namespace clang { -namespace dataflow { - -/// Lattice for dataflow analysis that keeps track of a set of source locations. -/// -/// Bottom is the empty set, join is set union, and equality is set equality. -/// -/// FIXME: Generalize into a (templated) PowerSetLattice. -class SourceLocationsLattice { -public: - SourceLocationsLattice() = default; - - explicit SourceLocationsLattice(llvm::DenseSet Locs) - : Locs(std::move(Locs)) {} - - bool operator==(const SourceLocationsLattice &Other) const { - return Locs == Other.Locs; - } - - bool operator!=(const SourceLocationsLattice &Other) const { - return !(*this == Other); - } - - LatticeJoinEffect join(const SourceLocationsLattice &Other); - - llvm::DenseSet &getSourceLocations() { return Locs; } - - const llvm::DenseSet &getSourceLocations() const { - return Locs; - } - -private: - llvm::DenseSet Locs; -}; - -/// Returns a string that represents the source locations of the lattice. -std::string DebugString(const SourceLocationsLattice &Lattice, - const ASTContext &Context); - -} // namespace dataflow -} // namespace clang - -#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SOURCELOCATIONS_LATTICE_H diff --git a/clang/lib/Analysis/FlowSensitive/CMakeLists.txt b/clang/lib/Analysis/FlowSensitive/CMakeLists.txt --- a/clang/lib/Analysis/FlowSensitive/CMakeLists.txt +++ b/clang/lib/Analysis/FlowSensitive/CMakeLists.txt @@ -2,7 +2,7 @@ ControlFlowContext.cpp DataflowAnalysisContext.cpp DataflowEnvironment.cpp - SourceLocationsLattice.cpp + SourceLocations.cpp Transfer.cpp TypeErasedDataflowAnalysis.cpp WatchedLiteralsSolver.cpp diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -20,7 +20,8 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" +#include "clang/Analysis/FlowSensitive/NoopLattice.h" +#include "clang/Analysis/FlowSensitive/SourceLocations.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" @@ -33,7 +34,7 @@ namespace { using namespace ::clang::ast_matchers; -using LatticeTransferState = TransferState; +using LatticeTransferState = TransferState; DeclarationMatcher optionalClass() { return classTemplateSpecializationDecl( @@ -310,18 +311,7 @@ if (auto *Loc = maybeInitializeOptionalValueMember( UnwrapExpr->getType(), *OptionalVal, State.Env)) State.Env.setStorageLocation(*UnwrapExpr, *Loc); - - auto *Prop = OptionalVal->getProperty("has_value"); - if (auto *HasValueVal = cast_or_null(Prop)) { - if (State.Env.flowConditionImplies(*HasValueVal)) - return; - } } - - // Record that this unwrap is *not* provably safe. - // FIXME: include either the name of the optional (if applicable) or a source - // range of the access for easier interpretation of the result. - State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc()); } void transferMakeOptionalCall(const CallExpr *E, @@ -542,6 +532,23 @@ transferSwap(*OptionalLoc1, *OptionalLoc2, State); } +void checkUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, + CheckState &State) { + if (auto *OptionalVal = + State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { + auto *Prop = OptionalVal->getProperty("has_value"); + if (auto *HasValueVal = cast_or_null(Prop)) { + if (State.Env.flowConditionImplies(*HasValueVal)) + return; + } + } + + // Record that this unwrap is *not* provably safe. + // FIXME: include either the name of the optional (if applicable) or a source + // range of the access for easier interpretation of the result. + State.Diags.insert(ObjectExpr->getBeginLoc()); +} + llvm::Optional ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { if (Options.IgnoreSmartPointerDereference) @@ -552,12 +559,8 @@ return llvm::None; } -auto buildTransferMatchSwitch( - const UncheckedOptionalAccessModelOptions &Options) { - // FIXME: Evaluate the efficiency of matchers. If using matchers results in a - // lot of duplicated work (e.g. string comparisons), consider providing APIs - // that avoid it through memoization. - auto IgnorableOptional = ignorableOptional(Options); +auto buildTransferMatchSwitch(StatementMatcher &ValueCall, + StatementMatcher &ValueOperatorCall) { return MatchSwitchBuilder() // Attach a symbolic "has_value" state to optional values that we see for // the first time. @@ -593,20 +596,18 @@ // optional::value .CaseOf( - isOptionalMemberCallWithName("value", IgnorableOptional), + ValueCall, [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, LatticeTransferState &State) { transferUnwrapCall(E, E->getImplicitObjectArgument(), State); }) // optional::operator*, optional::operator-> - .CaseOf( - expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), - isOptionalOperatorCallWithName("->", IgnorableOptional))), - [](const CallExpr *E, const MatchFinder::MatchResult &, - LatticeTransferState &State) { - transferUnwrapCall(E, E->getArg(0), State); - }) + .CaseOf(ValueOperatorCall, + [](const CallExpr *E, const MatchFinder::MatchResult &, + LatticeTransferState &State) { + transferUnwrapCall(E, E->getArg(0), State); + }) // optional::has_value .CaseOf(isOptionalMemberCallWithName("has_value"), @@ -654,6 +655,39 @@ .Build(); } +auto buildCheckMatchSwitch(StatementMatcher &ValueCall, + StatementMatcher &ValueOperatorCall) { + return MatchSwitchBuilder>() + // optional::value + .CaseOf( + ValueCall, + [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, + CheckState &State) { + checkUnwrapCall(E, E->getImplicitObjectArgument(), State); + }) + + // optional::operator*, optional::operator-> + .CaseOf(ValueOperatorCall, + [](const CallExpr *E, const MatchFinder::MatchResult &, + CheckState &State) { + checkUnwrapCall(E, E->getArg(0), State); + }) + .Build(); +} + +auto buildMatchSwitches(const UncheckedOptionalAccessModelOptions &Options) { + // FIXME: Evaluate the efficiency of matchers. If using matchers results in a + // lot of duplicated work (e.g. string comparisons), consider providing APIs + // that avoid it through memoization. + auto IgnorableOptional = ignorableOptional(Options); + auto ValueCall = isOptionalMemberCallWithName("value", IgnorableOptional); + auto ValueOperatorCall = + expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), + isOptionalOperatorCallWithName("->", IgnorableOptional))); + return std::make_pair(buildTransferMatchSwitch(ValueCall, ValueOperatorCall), + buildCheckMatchSwitch(ValueCall, ValueOperatorCall)); +} + } // namespace ast_matchers::DeclarationMatcher @@ -663,12 +697,20 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) - : DataflowAnalysis( - Ctx), - TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} + : UncheckedOptionalAccessModel(Ctx, buildMatchSwitches(Options)) {} -void UncheckedOptionalAccessModel::transfer(const Stmt *S, - SourceLocationsLattice &L, +// We use a delegating constructor to build both `MatchSwitch`es at once while +// still avoiding unnecessary work by using initializer lists. +UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( + ASTContext &Ctx, std::pair>, + MatchSwitch>> + MatchSwitches) + : DataflowAnalysis(Ctx), + TransferMatchSwitch(MatchSwitches.first), + CheckMatchSwitch(MatchSwitches.second) {} + +void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L, Environment &Env) { LatticeTransferState State(L, Env); TransferMatchSwitch(*S, getASTContext(), State); @@ -700,5 +742,12 @@ return true; } +void UncheckedOptionalAccessModel::check(const Stmt *S, SourceLocations &Locs, + const NoopLattice &, + const Environment &Env) { + CheckState State(Locs, Env); + CheckMatchSwitch(*S, getASTContext(), State); +} + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp b/clang/lib/Analysis/FlowSensitive/SourceLocations.cpp rename from clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp rename to clang/lib/Analysis/FlowSensitive/SourceLocations.cpp --- a/clang/lib/Analysis/FlowSensitive/SourceLocationsLattice.cpp +++ b/clang/lib/Analysis/FlowSensitive/SourceLocations.cpp @@ -1,4 +1,4 @@ -//===- SourceLocationsLattice.cpp -----------------------------------------===// +//===- SourceLocations.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +6,12 @@ // //===----------------------------------------------------------------------===// // -// This file implements a lattice that collects source locations of interest. +// This file implements a function to stringify source locations of interest. // //===----------------------------------------------------------------------===// -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" +#include "clang/Analysis/FlowSensitive/SourceLocations.h" #include "clang/AST/ASTContext.h" -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" #include @@ -22,22 +21,14 @@ namespace clang { namespace dataflow { -LatticeJoinEffect -SourceLocationsLattice::join(const SourceLocationsLattice &Other) { - auto SizeBefore = Locs.size(); - Locs.insert(Other.Locs.begin(), Other.Locs.end()); - return SizeBefore == Locs.size() ? LatticeJoinEffect::Unchanged - : LatticeJoinEffect::Changed; -} - -std::string DebugString(const SourceLocationsLattice &Lattice, +std::string DebugString(const SourceLocations &Locs, const ASTContext &Context) { - if (Lattice.getSourceLocations().empty()) + if (Locs.empty()) return ""; std::vector Locations; - Locations.reserve(Lattice.getSourceLocations().size()); - for (const clang::SourceLocation &Loc : Lattice.getSourceLocations()) { + Locations.reserve(Locs.size()); + for (const clang::SourceLocation &Loc : Locs) { Locations.push_back(Loc.printToString(Context.getSourceManager())); } std::sort(Locations.begin(), Locations.end()); diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt --- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt +++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt @@ -11,7 +11,6 @@ MatchSwitchTest.cpp MultiVarConstantPropagationTest.cpp SingleVarConstantPropagationTest.cpp - SourceLocationsLatticeTest.cpp TestingSupport.cpp TestingSupportTest.cpp TransferTest.cpp diff --git a/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp @@ -145,7 +145,7 @@ llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { Match(Results, ASTCtx); }, + NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, {"-fsyntax-only", "-fno-delayed-template-parsing", "-std=c++17"}, FileContents), llvm::Succeeded()); diff --git a/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp b/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp @@ -135,6 +135,7 @@ llvm::ArrayRef>> Results, + TestAnalysis::Diags &, ASTContext &) { EXPECT_THAT(Results, Expectations); }, {"-fsyntax-only", "-std=c++17"}), llvm::Succeeded()); diff --git a/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp b/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp @@ -223,6 +223,7 @@ std::string, DataflowAnalysisState< ConstantPropagationAnalysis::Lattice>>> Results, + ConstantPropagationAnalysis::Diags &, ASTContext &) { EXPECT_THAT(Results, Expectations); }, {"-fsyntax-only", "-std=c++17"}), llvm::Succeeded()); diff --git a/clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h b/clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h --- a/clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h +++ b/clang/unittests/Analysis/FlowSensitive/NoopAnalysis.h @@ -18,25 +18,11 @@ #include "clang/AST/Stmt.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include +#include "clang/Analysis/FlowSensitive/NoopLattice.h" namespace clang { namespace dataflow { -class NoopLattice { -public: - bool operator==(const NoopLattice &) const { return true; } - - LatticeJoinEffect join(const NoopLattice &) { - return LatticeJoinEffect::Unchanged; - } -}; - -inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) { - return OS << "noop"; -} - class NoopAnalysis : public DataflowAnalysis { public: /// `ApplyBuiltinTransfer` controls whether to run the built-in transfer diff --git a/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp b/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp @@ -201,6 +201,7 @@ std::string, DataflowAnalysisState< ConstantPropagationAnalysis::Lattice>>> Results, + ConstantPropagationAnalysis::Diags &, ASTContext &) { EXPECT_THAT(Results, Expectations); }, {"-fsyntax-only", "-std=c++17"}), llvm::Succeeded()); diff --git a/clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp b/clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp deleted file mode 100644 --- a/clang/unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//===- unittests/Analysis/FlowSensitive/SourceLocationsLatticeTest.cpp ----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" - -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include "clang/Basic/SourceLocation.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace clang { -namespace dataflow { -namespace { - -TEST(SourceLocationsLatticeTest, Comparison) { - const SourceLocationsLattice Bottom; - const SourceLocationsLattice NonBottom( - {SourceLocation::getFromRawEncoding(0)}); - - EXPECT_TRUE(Bottom == Bottom); - EXPECT_FALSE(Bottom == NonBottom); - EXPECT_FALSE(NonBottom == Bottom); - EXPECT_TRUE(NonBottom == NonBottom); - - EXPECT_FALSE(Bottom != Bottom); - EXPECT_TRUE(Bottom != NonBottom); - EXPECT_TRUE(NonBottom != Bottom); - EXPECT_FALSE(NonBottom != NonBottom); -} - -TEST(SourceLocationsLatticeTest, Join) { - const SourceLocationsLattice Bottom; - const SourceLocationsLattice NonBottom( - {SourceLocation::getFromRawEncoding(0)}); - { - SourceLocationsLattice LHS = Bottom; - const SourceLocationsLattice RHS = Bottom; - EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged); - EXPECT_EQ(LHS, Bottom); - } - { - SourceLocationsLattice LHS = NonBottom; - const SourceLocationsLattice RHS = Bottom; - EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged); - EXPECT_EQ(LHS, NonBottom); - } - { - SourceLocationsLattice LHS = Bottom; - const SourceLocationsLattice RHS = NonBottom; - EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Changed); - EXPECT_EQ(LHS, NonBottom); - } - { - SourceLocationsLattice LHS = NonBottom; - const SourceLocationsLattice RHS = NonBottom; - EXPECT_EQ(LHS.join(RHS), LatticeJoinEffect::Unchanged); - EXPECT_EQ(LHS, NonBottom); - } -} - -} // namespace -} // namespace dataflow -} // namespace clang diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -63,7 +63,7 @@ llvm::Annotations AnnotatedCode); // Runs dataflow on the body of the function that matches `func_matcher` in code -// snippet `code`. Requires: `Analysis` contains a type `Lattice`. +// snippet `code`. Requires: `Analysis` contains types `Lattice` and `Diags`. template llvm::Error checkDataflow( llvm::StringRef Code, @@ -72,7 +72,7 @@ std::function>>, - ASTContext &)> + typename AnalysisT::Diags &, ASTContext &)> Expectations, ArrayRef Args, const tooling::FileContentMappings &VirtualMappedFiles = {}) { @@ -121,8 +121,10 @@ return MaybeBlockStates.takeError(); auto &BlockStates = *MaybeBlockStates; + typename AnalysisT::Diags Diags; + if (BlockStates.empty()) { - Expectations({}, Context); + Expectations({}, Diags, Context); return llvm::Error::success(); } @@ -136,17 +138,21 @@ transferBlock( *CFCtx, BlockStates, *Block, Env, Analysis, - [&Results, &Annotations](const clang::CFGStmt &Stmt, - const TypeErasedDataflowAnalysisState &State) { - auto It = Annotations.find(Stmt.getStmt()); - if (It == Annotations.end()) - return; + [&Results, &Diags, &Annotations, + &Analysis](const clang::CFGStmt &Stmt, + const TypeErasedDataflowAnalysisState &State) { + auto S = Stmt.getStmt(); auto *Lattice = llvm::any_cast(&State.Lattice.Value); + Analysis.check(S, Diags, *Lattice, State.Env); + + auto It = Annotations.find(S); + if (It == Annotations.end()) + return; Results.emplace_back(It->second, StateT{*Lattice, State.Env}); }); } - Expectations(Results, Context); + Expectations(Results, Diags, Context); return llvm::Error::success(); } @@ -159,7 +165,7 @@ std::function>>, - ASTContext &)> + typename AnalysisT::Diags &, ASTContext &)> Expectations, ArrayRef Args, const tooling::FileContentMappings &VirtualMappedFiles = {}) { diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp b/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp @@ -76,7 +76,7 @@ llvm::StringRef Code, llvm::StringRef Target, std::function>>, - ASTContext &)> + NoopDiags &, ASTContext &)> Expectations) { ASSERT_THAT_ERROR( test::checkDataflow( @@ -92,10 +92,10 @@ ::testing::MockFunction>>, - ASTContext &)> + NoopDiags &, ASTContext &)> Expectations; - EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1); + EXPECT_CALL(Expectations, Call(IsEmpty(), _, _)).Times(1); checkDataflow("void target() {}", "target", Expectations.AsStdFunction()); } @@ -104,10 +104,10 @@ ::testing::MockFunction>>, - ASTContext &)> + NoopDiags &, ASTContext &)> Expectations; - EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1); + EXPECT_CALL(Expectations, Call(IsEmpty(), _, _)).Times(1); checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction()); } @@ -116,11 +116,11 @@ ::testing::MockFunction>>, - ASTContext &)> + NoopDiags &, ASTContext &)> Expectations; EXPECT_CALL(Expectations, - Call(UnorderedElementsAre(Pair("program-point", _)), _)) + Call(UnorderedElementsAre(Pair("program-point", _)), _, _)) .Times(1); checkDataflow(R"cc(void target() { @@ -134,13 +134,13 @@ ::testing::MockFunction>>, - ASTContext &)> + NoopDiags &, ASTContext &)> Expectations; EXPECT_CALL(Expectations, Call(UnorderedElementsAre(Pair("program-point-1", _), Pair("program-point-2", _)), - _)) + _, _)) .Times(1); checkDataflow(R"cc(void target(bool b) { diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -54,7 +54,7 @@ llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { Match(Results, ASTCtx); }, + NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, {"-fsyntax-only", "-fno-delayed-template-parsing", "-std=" + std::string( @@ -1325,7 +1325,7 @@ [](llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { + NoopDiags &, ASTContext &ASTCtx) { // Regression test to verify that base-class initializers do not // trigger an assertion. If we add support for such initializers in // the future, we can expand this test to check more specific diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -203,6 +203,7 @@ llvm::ArrayRef>> Results, + NoopDiags &, ASTContext &) { EXPECT_THAT(Results, Expectations); }, {"-fsyntax-only", "-std=c++17"}, FilesContents), llvm::Succeeded()); @@ -394,7 +395,7 @@ llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { Match(Results, ASTCtx); }, + NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, {"-fsyntax-only", "-std=c++17"}), llvm::Succeeded()); } @@ -549,7 +550,7 @@ llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { Match(Results, ASTCtx); }, + NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, {"-fsyntax-only", "-std=c++17"}, FilesContents), llvm::Succeeded()); } @@ -725,7 +726,7 @@ llvm::ArrayRef< std::pair>> Results, - ASTContext &ASTCtx) { Match(Results, ASTCtx); }, + NoopDiags &, ASTContext &ASTCtx) { Match(Results, ASTCtx); }, {"-fsyntax-only", "-std=c++17"}), llvm::Succeeded()); } diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp @@ -11,7 +11,7 @@ #include "TestingSupport.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" +#include "clang/Analysis/FlowSensitive/SourceLocations.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" @@ -26,9 +26,6 @@ using namespace dataflow; using namespace test; -using ::testing::Pair; -using ::testing::UnorderedElementsAre; - // FIXME: Move header definitions in separate file(s). static constexpr char CSDtdDefHeader[] = R"( #ifndef CSTDDEF_H @@ -1181,10 +1178,9 @@ )"; /// Converts `L` to string. -static std::string ConvertToString(const SourceLocationsLattice &L, +static std::string ConvertToString(const SourceLocations &L, const ASTContext &Ctx) { - return L.getSourceLocations().empty() ? "safe" - : "unsafe: " + DebugString(L, Ctx); + return L.empty() ? "safe" : "unsafe: " + DebugString(L, Ctx); } /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. @@ -1253,19 +1249,11 @@ /*IgnoreSmartPointerDereference=*/true}); }, [&MatchesLatticeChecks]( - llvm::ArrayRef>> - CheckToLatticeMap, - ASTContext &Ctx) { - // FIXME: Consider using a matcher instead of translating - // `CheckToLatticeMap` to `CheckToStringifiedLatticeMap`. - std::vector> - CheckToStringifiedLatticeMap; - for (const auto &E : CheckToLatticeMap) { - CheckToStringifiedLatticeMap.emplace_back( - E.first, ConvertToString(E.second.Lattice, Ctx)); - } - EXPECT_THAT(CheckToStringifiedLatticeMap, MatchesLatticeChecks); + llvm::ArrayRef< + std::pair>>, + SourceLocations &Locs, ASTContext &Ctx) { + std::string StringifiedDiags = ConvertToString(Locs, Ctx); + EXPECT_THAT(StringifiedDiags, MatchesLatticeChecks); }, {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); if (Error) @@ -1289,7 +1277,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { @@ -1302,7 +1290,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); + "unsafe: input.cc:5:7"); ExpectLatticeChecksFor( R"( @@ -1313,7 +1301,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); + "unsafe: input.cc:5:7"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { @@ -1326,7 +1314,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); + "unsafe: input.cc:5:8"); ExpectLatticeChecksFor( R"( @@ -1337,7 +1325,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); + "unsafe: input.cc:5:8"); } TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { @@ -1354,7 +1342,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); + "unsafe: input.cc:9:7"); ExpectLatticeChecksFor( R"( @@ -1369,7 +1357,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); + "unsafe: input.cc:9:7"); } TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { @@ -1383,7 +1371,7 @@ } } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { @@ -1397,7 +1385,7 @@ } } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { @@ -1411,7 +1399,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); + "unsafe: input.cc:5:7"); ExpectLatticeChecksFor( R"( @@ -1422,7 +1410,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); + "unsafe: input.cc:5:7"); } TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { @@ -1436,7 +1424,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); + "unsafe: input.cc:6:7"); } TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { @@ -1450,7 +1438,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); + "unsafe: input.cc:6:7"); } TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { @@ -1463,7 +1451,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1476,7 +1464,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1491,7 +1479,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1506,7 +1494,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { @@ -1519,7 +1507,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1530,7 +1518,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1540,7 +1528,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1555,7 +1543,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1572,7 +1560,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1587,7 +1575,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { @@ -1607,7 +1595,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); + "unsafe: input.cc:12:7"); ExpectLatticeChecksFor( R"( @@ -1625,7 +1613,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); + "unsafe: input.cc:12:7"); ExpectLatticeChecksFor( R"( @@ -1644,7 +1632,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:13:7"))); + "unsafe: input.cc:13:7"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1662,7 +1650,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1680,7 +1668,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, MakeOptional) { @@ -1693,7 +1681,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1708,7 +1696,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1724,7 +1712,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, ValueOr) { @@ -1738,7 +1726,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { @@ -1757,8 +1745,7 @@ } } )code", - UnorderedElementsAre(Pair("check-ptrs-1", "safe"), - Pair("check-ptrs-2", "unsafe: input.cc:9:9"))); + "unsafe: input.cc:9:9"); // Integers. ExpectLatticeChecksFor( @@ -1775,8 +1762,7 @@ } } )code", - UnorderedElementsAre(Pair("check-ints-1", "safe"), - Pair("check-ints-2", "unsafe: input.cc:9:9"))); + "unsafe: input.cc:9:9"); // Strings. ExpectLatticeChecksFor( @@ -1793,8 +1779,7 @@ } } )code", - UnorderedElementsAre(Pair("check-strings-1", "safe"), - Pair("check-strings-2", "unsafe: input.cc:9:9"))); + "unsafe: input.cc:9:9"); ExpectLatticeChecksFor( R"code( @@ -1810,9 +1795,7 @@ } } )code", - UnorderedElementsAre( - Pair("check-strings-neq-1", "safe"), - Pair("check-strings-neq-2", "unsafe: input.cc:9:9"))); + "unsafe: input.cc:9:9"); // Pointer-to-optional. // @@ -1833,8 +1816,7 @@ } } )code", - UnorderedElementsAre(Pair("check-pto-1", "safe"), - Pair("check-pto-2", "unsafe: input.cc:10:9"))); + "unsafe: input.cc:10:9"); } TEST_P(UncheckedOptionalAccessTest, Emplace) { @@ -1848,7 +1830,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1859,7 +1841,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); // FIXME: Add tests that call `emplace` in conditional branches: // ExpectLatticeChecksFor( @@ -1879,8 +1861,7 @@ // } // } // )", - // UnorderedElementsAre(Pair("check-1", "safe"), - // Pair("check-2", "unsafe: input.cc:12:9"))); + // "unsafe: input.cc:12:9"); } TEST_P(UncheckedOptionalAccessTest, Reset) { @@ -1895,7 +1876,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); + "unsafe: input.cc:7:7"); ExpectLatticeChecksFor( R"( @@ -1909,7 +1890,7 @@ } } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:9"))); + "unsafe: input.cc:7:9"); // FIXME: Add tests that call `reset` in conditional branches: // ExpectLatticeChecksFor( @@ -1930,8 +1911,7 @@ // } // } // )", - // UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:10:9"), - // Pair("check-2", "safe"))); + // "safe"); } TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { @@ -1947,7 +1927,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1961,7 +1941,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1977,7 +1957,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -1992,7 +1972,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { @@ -2014,7 +1994,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); ExpectLatticeChecksFor( R"( @@ -2036,7 +2016,7 @@ } } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:15:9"))); + "unsafe: input.cc:15:9"); ExpectLatticeChecksFor( R"( @@ -2056,7 +2036,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { @@ -2071,7 +2051,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); + "unsafe: input.cc:7:7"); ExpectLatticeChecksFor( R"( @@ -2083,7 +2063,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); + "unsafe: input.cc:6:7"); } TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { @@ -2104,8 +2084,7 @@ /*[[check-2]]*/ } )", - UnorderedElementsAre(Pair("check-1", "safe"), - Pair("check-2", "unsafe: input.cc:13:7"))); + "unsafe: input.cc:13:7"); ExpectLatticeChecksFor( R"( @@ -2124,8 +2103,7 @@ /*[[check-4]]*/ } )", - UnorderedElementsAre(Pair("check-3", "safe"), - Pair("check-4", "unsafe: input.cc:13:7"))); + "unsafe: input.cc:13:7"); } TEST_P(UncheckedOptionalAccessTest, StdSwap) { @@ -2146,8 +2124,7 @@ /*[[check-2]]*/ } )", - UnorderedElementsAre(Pair("check-1", "safe"), - Pair("check-2", "unsafe: input.cc:13:7"))); + "unsafe: input.cc:13:7"); ExpectLatticeChecksFor( R"( @@ -2166,8 +2143,7 @@ /*[[check-4]]*/ } )", - UnorderedElementsAre(Pair("check-3", "safe"), - Pair("check-4", "unsafe: input.cc:13:7"))); + "unsafe: input.cc:13:7"); } TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { @@ -2195,7 +2171,7 @@ /*[[check-2]]*/ } )", - UnorderedElementsAre(Pair("check-1", "safe"), Pair("check-2", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { @@ -2212,7 +2188,7 @@ /*[[check-1]]*/ } )", - UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:9:7"))); + "unsafe: input.cc:9:7"); ExpectLatticeChecksFor( R"( #include "unchecked_optional_access_test.h" @@ -2226,7 +2202,7 @@ /*[[check-2]]*/ } )", - UnorderedElementsAre(Pair("check-2", "unsafe: input.cc:9:7"))); + "unsafe: input.cc:9:7"); ExpectLatticeChecksFor( R"( @@ -2242,7 +2218,7 @@ /*[[check-3]]*/ } )", - UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:10:7"))); + "unsafe: input.cc:10:7"); ExpectLatticeChecksFor( R"( @@ -2258,7 +2234,7 @@ /*[[check-4]]*/ } )", - UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:10:7"))); + "unsafe: input.cc:10:7"); } // Verifies that the model sees through aliases. @@ -2275,7 +2251,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:8:7"))); + "unsafe: input.cc:8:7"); } TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { @@ -2295,7 +2271,7 @@ } } )", - UnorderedElementsAre(Pair("access", "safe"))); + "safe"); // Mutation is supported for nested values. ExpectLatticeChecksFor( @@ -2312,7 +2288,7 @@ } } )", - UnorderedElementsAre(Pair("reset", "unsafe: input.cc:9:9"))); + "unsafe: input.cc:9:9"); } // Tests that structs can be nested. We use an optional field because its easy @@ -2333,7 +2309,7 @@ } } )", - UnorderedElementsAre(Pair("access", "safe"))); + "safe"); } TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { @@ -2363,7 +2339,7 @@ /*[[merge]]*/ } )", - UnorderedElementsAre(Pair("merge", "unsafe: input.cc:19:7"))); + "unsafe: input.cc:19:7"); } TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { @@ -2383,7 +2359,7 @@ /*[[check]]*/ } )", - UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); + "unsafe: input.cc:12:7"); } TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { @@ -2399,7 +2375,7 @@ } } )code", - UnorderedElementsAre(Pair("check-1", "safe"))); + "safe"); ExpectLatticeChecksFor(R"code( #include "unchecked_optional_access_test.h" @@ -2412,7 +2388,7 @@ } } )code", - UnorderedElementsAre(Pair("check-2", "safe"))); + "safe"); ExpectLatticeChecksFor( R"code( @@ -2426,7 +2402,7 @@ } } )code", - UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9"))); + "unsafe: input.cc:7:9"); ExpectLatticeChecksFor(R"code( #include "unchecked_optional_access_test.h" @@ -2440,7 +2416,7 @@ } } )code", - UnorderedElementsAre(Pair("check-4", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -2454,7 +2430,7 @@ } } )", - UnorderedElementsAre(Pair("check-5", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -2468,7 +2444,7 @@ } } )", - UnorderedElementsAre(Pair("check-6", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -2487,7 +2463,7 @@ } } )", - UnorderedElementsAre(Pair("check", "safe"))); + "safe"); // FIXME: Add support for operator==. // ExpectLatticeChecksFor(R"( @@ -2502,7 +2478,7 @@ // } // } // )", - // UnorderedElementsAre(Pair("check-7", "safe"))); + // "safe"); } TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { @@ -2526,8 +2502,7 @@ } } )code", - UnorderedElementsAre(Pair("check-1", "safe"), - Pair("check-2", "unsafe: input.cc:15:9"))); + "unsafe: input.cc:15:9"); ExpectLatticeChecksFor(R"code( #include "unchecked_optional_access_test.h" @@ -2545,7 +2520,7 @@ /*[[check-3]]*/ } )code", - UnorderedElementsAre(Pair("check-3", "safe"))); + "safe"); ExpectLatticeChecksFor( R"code( @@ -2563,7 +2538,7 @@ /*[[check-4]]*/ } )code", - UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:12:7"))); + "unsafe: input.cc:12:7"); ExpectLatticeChecksFor( R"code( @@ -2580,7 +2555,7 @@ /*[[check-5]]*/ } )code", - UnorderedElementsAre(Pair("check-5", "safe"))); + "safe"); ExpectLatticeChecksFor( R"code( @@ -2597,7 +2572,7 @@ /*[[check-6]]*/ } )code", - UnorderedElementsAre(Pair("check-6", "unsafe: input.cc:11:7"))); + "unsafe: input.cc:11:7"); } TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) { @@ -2612,7 +2587,7 @@ } } )", - UnorderedElementsAre(Pair("check-1", "safe"))); + "safe"); ExpectLatticeChecksFor(R"( #include "unchecked_optional_access_test.h" @@ -2628,7 +2603,7 @@ } } )", - UnorderedElementsAre(Pair("check-2", "safe"))); + "safe"); ExpectLatticeChecksFor( R"( @@ -2644,7 +2619,7 @@ } } )", - UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9"))); + "unsafe: input.cc:7:9"); ExpectLatticeChecksFor( R"( @@ -2661,7 +2636,7 @@ } } )", - UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:7:9"))); + "unsafe: input.cc:7:9"); } // FIXME: Add support for: diff --git a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn --- a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn @@ -9,7 +9,7 @@ "ControlFlowContext.cpp", "DataflowAnalysisContext.cpp", "DataflowEnvironment.cpp", - "SourceLocationsLattice.cpp", + "SourceLocations.cpp", "Transfer.cpp", "TypeErasedDataflowAnalysis.cpp", "WatchedLiteralsSolver.cpp", diff --git a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn --- a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn @@ -25,7 +25,6 @@ "MultiVarConstantPropagationTest.cpp", "SingleVarConstantPropagationTest.cpp", "SolverTest.cpp", - "SourceLocationsLatticeTest.cpp", "TestingSupport.cpp", "TestingSupportTest.cpp", "TransferTest.cpp",