diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -19,6 +19,8 @@ namespace clang { +using DefMapTy = llvm::DenseMap>; + /// The interface that lets the caller handle unsafe buffer usage analysis /// results by overriding this class's handle... methods. class UnsafeBufferUsageHandler { @@ -34,9 +36,12 @@ virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl) = 0; - /// Invoked when a fix is suggested against a variable. - virtual void handleFixableVariable(const VarDecl *Variable, - FixItList &&List) = 0; + /// Invoked when a fix is suggested against a variable. This function groups + /// all variables that must be fixed together (i.e their types must be changed to the + /// same target type to prevent type mismatches) into a single fixit. + virtual void handleUnsafeVariableGroup(const VarDecl *Variable, + const DefMapTy &VarGrpMap, + FixItList &&Fixes) = 0; /// Returns a reference to the `Preprocessor`: virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0; diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -36,6 +36,7 @@ FIXABLE_GADGET(UPCAddressofArraySubscript) // '&DRE[any]' in an Unspecified Pointer Context FIXABLE_GADGET(UPCStandalonePointer) FIXABLE_GADGET(UPCPreIncrement) // '++Ptr' in an Unspecified Pointer Context +FIXABLE_GADGET(PointerAssignment) #undef FIXABLE_GADGET #undef WARNING_GADGET diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11822,8 +11822,8 @@ InGroup, DefaultIgnore; def note_unsafe_buffer_operation : Note< "used%select{| in pointer arithmetic| in buffer access}0 here">; -def note_unsafe_buffer_variable_fixit : Note< - "change type of '%0' to '%select{std::span|std::array|std::span::iterator}1' to preserve bounds information">; +def note_unsafe_buffer_variable_fixit_group : Note< + "change type of %0 to '%select{std::span|std::array|std::span::iterator}1' to preserve bounds information%select{|, and change %2 to '%select{std::span|std::array|std::span::iterator}1' to propagate bounds information between them}3">; def note_safe_buffer_usage_suggestions_disabled : Note< "pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions">; def err_loongarch_builtin_requires_la32 : Error< diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -16,6 +16,7 @@ #include #include #include +#include using namespace llvm; using namespace clang; @@ -256,6 +257,29 @@ // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we // don't have to check that.) } + +// Returns a matcher that matches any expression 'e' such that `innerMatcher` +// matches 'e' and 'e' is in an unspecified untyped context (i.e the expression +// 'e' isn't evaluated to an RValue). For example, consider the following code: +// int *p = new int[4]; +// int *q = new int[4]; +// if ((p = q)) {} +// p = q; +// The expression `p = q` in the conditional of the `if` statement +// `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;` +// in the assignment statement is in an untyped context. +static internal::Matcher +isInUnspecifiedUntypedContext(internal::Matcher InnerMatcher) { + // An unspecified context can be + // 1. A compound statement, + // 2. The body of an if statement + // 3. Body of a loop + auto CompStmt = compoundStmt(forEach(InnerMatcher)); + auto IfStmtThen = ifStmt(hasThen(InnerMatcher)); + auto IfStmtElse = ifStmt(hasElse(InnerMatcher)); + // FIXME: Handle loop bodies. + return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse)); +} } // namespace clang::ast_matchers namespace { @@ -337,6 +361,16 @@ virtual std::optional getFixits(const Strategy &) const { return std::nullopt; } + + /// Returns a list of two elements where the first element is the LHS of a pointer assignment + /// statement and the second element is the RHS. This two-element list represents the fact that + /// the LHS buffer gets its bounds information from the RHS buffer. This information will be used + /// later to group all those variables whose types must be modified together to prevent type + /// mismatches. + virtual std::optional> + getStrategyImplications() const { + return std::nullopt; + } }; using FixableGadgetList = std::vector>; @@ -499,6 +533,58 @@ // FIXME: this gadge will need a fix-it }; +/// A pointer assignment expression of the form: +/// \code +/// p = q; +/// \endcode +class PointerAssignmentGadget : public FixableGadget { +private: + static constexpr const char *const PointerAssignmentTag = "ptrAssign"; + static constexpr const char *const PointerAssignLHSTag = "ptrLHS"; + static constexpr const char *const PointerAssignRHSTag = "ptrRHS"; + const BinaryOperator *PA; // pointer arithmetic expression + const DeclRefExpr * PtrLHS; // the LHS pointer expression in `PA` + const DeclRefExpr * PtrRHS; // the RHS pointer expression in `PA` + +public: + PointerAssignmentGadget(const MatchFinder::MatchResult &Result) + : FixableGadget(Kind::PointerAssignment), + PA(Result.Nodes.getNodeAs(PointerAssignmentTag)), + PtrLHS(Result.Nodes.getNodeAs(PointerAssignLHSTag)), + PtrRHS(Result.Nodes.getNodeAs(PointerAssignRHSTag)) {} + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::PointerAssignment; + } + + static Matcher matcher() { + auto PtrAssignExpr = binaryOperator(allOf(hasOperatorName("="), + hasRHS(ignoringParenImpCasts(declRefExpr(hasPointerType(), + to(varDecl())). + bind(PointerAssignRHSTag))), + hasLHS(declRefExpr(hasPointerType(), + to(varDecl())). + bind(PointerAssignLHSTag)))); + + //FIXME: Handle declarations at assignments + return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr)); + } + + virtual std::optional getFixits(const Strategy &S) const override; + + virtual const Stmt *getBaseStmt() const override { return PA; } + + virtual DeclUseList getClaimedVarUseSites() const override { + return DeclUseList{PtrLHS, PtrRHS}; + } + + virtual std::optional> + getStrategyImplications() const override { + return std::make_pair(cast(PtrLHS->getDecl()), + cast(PtrRHS->getDecl())); + } +}; + /// A call of a function or method that performs unchecked buffer operations /// over one of its pointer parameters. class UnsafeBufferUsageAttrGadget : public WarningGadget { @@ -986,17 +1072,17 @@ }; struct WarningGadgetSets { - std::map>, + std::map, // To keep keys sorted by their locations in the map so that the // order is deterministic: CompareNode> byVar; // These Gadgets are not related to pointer variables (e. g. temporaries). - llvm::SmallVector, 16> noVar; + llvm::SmallVector noVar; }; static WarningGadgetSets -groupWarningGadgetsByVar(WarningGadgetList &&AllUnsafeOperations) { +groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) { WarningGadgetSets result; // If some gadgets cover more than one // variable, they'll appear more than once in the map. @@ -1006,13 +1092,13 @@ bool AssociatedWithVarDecl = false; for (const DeclRefExpr *DRE : ClaimedVarUseSites) { if (const auto *VD = dyn_cast(DRE->getDecl())) { - result.byVar[VD].emplace(std::move(G)); + result.byVar[VD].insert(G.get()); AssociatedWithVarDecl = true; } } if (!AssociatedWithVarDecl) { - result.noVar.emplace_back(std::move(G)); + result.noVar.push_back(G.get()); continue; } } @@ -1020,7 +1106,7 @@ } struct FixableGadgetSets { - std::map>> byVar; + std::map> byVar; }; static FixableGadgetSets @@ -1031,7 +1117,7 @@ for (const DeclRefExpr *DRE : DREs) { if (const auto *VD = dyn_cast(DRE->getDecl())) { - FixablesForUnsafeVars.byVar[VD].emplace(std::move(F)); + FixablesForUnsafeVars.byVar[VD].insert(F.get()); } } } @@ -1069,6 +1155,26 @@ return false; } +std::optional +PointerAssignmentGadget::getFixits(const Strategy &S) const { + const auto *LeftVD = cast(PtrLHS->getDecl()); + const auto *RightVD = cast(PtrRHS->getDecl()); + switch (S.lookup(LeftVD)) { + case Strategy::Kind::Span: + if (S.lookup(RightVD) == Strategy::Kind::Span) + return FixItList{}; + return std::nullopt; + case Strategy::Kind::Wontfix: + return std::nullopt; + case Strategy::Kind::Iterator: + case Strategy::Kind::Array: + case Strategy::Kind::Vector: + llvm_unreachable("unsupported strategies for FixableGadgets"); + } + return std::nullopt; +} + + std::optional ULCArraySubscriptGadget::getFixits(const Strategy &S) const { if (const auto *DRE = @@ -1556,14 +1662,28 @@ }); } +static bool impossibleToFixForVar(const FixableGadgetSets &FixablesForUnsafeVars, + const Strategy &S, + const VarDecl * Var) { + for (const auto &F : FixablesForUnsafeVars.byVar.find(Var)->second) { + std::optional Fixits = F->getFixits(S); + if (!Fixits) { + return true; + } + } + return false; +} + static std::map getFixIts(FixableGadgetSets &FixablesForUnsafeVars, const Strategy &S, - const DeclUseTracker &Tracker, const ASTContext &Ctx, - UnsafeBufferUsageHandler &Handler) { + const DeclUseTracker &Tracker, const ASTContext &Ctx, + UnsafeBufferUsageHandler &Handler, + const DefMapTy &VarGrpMap) { std::map FixItsForVariable; for (const auto &[VD, Fixables] : FixablesForUnsafeVars.byVar) { + const Strategy::Kind ReplacementTypeForVD = S.lookup(VD); FixItsForVariable[VD] = - fixVariable(VD, S.lookup(VD), Tracker, Ctx, Handler); + fixVariable(VD, ReplacementTypeForVD, Tracker, Ctx, Handler); // If we fail to produce Fix-It for the declaration we have to skip the // variable entirely. if (FixItsForVariable[VD].empty()) { @@ -1583,23 +1703,71 @@ CorrectFixes.end()); } } - if (ImpossibleToFix) + + if (ImpossibleToFix) { FixItsForVariable.erase(VD); - else - FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), - FixItsForVD.begin(), FixItsForVD.end()); - // We conservatively discard fix-its of a variable if - // a fix-it overlaps with macros; or - // a fix-it conflicts with another one + continue; + } + + const auto VarGroupForVD = VarGrpMap.find(VD); + if (VarGroupForVD != VarGrpMap.end()) { + for (const VarDecl * V : VarGroupForVD->second) { + if (V == VD) { + continue; + } + if (impossibleToFixForVar(FixablesForUnsafeVars, S, V)) { + ImpossibleToFix = true; + break; + } + } + + if (ImpossibleToFix) { + FixItsForVariable.erase(VD); + for (const VarDecl * V : VarGroupForVD->second) { + FixItsForVariable.erase(V); + } + continue; + } + } + FixItsForVariable[VD].insert(FixItsForVariable[VD].end(), + FixItsForVD.begin(), FixItsForVD.end()); + + // Fix-it shall not overlap with macros or/and templates: if (overlapWithMacro(FixItsForVariable[VD]) || clang::internal::anyConflict(FixItsForVariable[VD], Ctx.getSourceManager())) { FixItsForVariable.erase(VD); + continue; + } + } + + for (auto VD : FixItsForVariable) { + const auto VarGroupForVD = VarGrpMap.find(VD.first); + const Strategy::Kind ReplacementTypeForVD = S.lookup(VD.first); + if (VarGroupForVD != VarGrpMap.end()) { + for (const VarDecl * Var : VarGroupForVD->second) { + if (Var == VD.first) { + continue; + } + + FixItList GroupFix; + if (FixItsForVariable.find(Var) == FixItsForVariable.end()) { + GroupFix = fixVariable(Var, ReplacementTypeForVD, Tracker, + Var->getASTContext(), Handler); + } else { + GroupFix = FixItsForVariable[Var]; + } + + for (auto Fix : GroupFix) { + FixItsForVariable[VD.first].push_back(Fix); + } + } } } return FixItsForVariable; } + static Strategy getNaiveStrategy(const llvm::SmallVectorImpl &UnsafeVars) { Strategy S; @@ -1614,54 +1782,135 @@ bool EmitSuggestions) { assert(D && D->getBody()); WarningGadgetSets UnsafeOps; - FixableGadgetSets FixablesForUnsafeVars; - DeclUseTracker Tracker; - - { - auto [FixableGadgets, WarningGadgets, TrackerRes] = - findGadgets(D, Handler, EmitSuggestions); - - if (!EmitSuggestions) { - // Our job is very easy without suggestions. Just warn about - // every problematic operation and consider it done. No need to deal - // with fixable gadgets, no need to group operations by variable. - for (const auto &G : WarningGadgets) { - Handler.handleUnsafeOperation(G->getBaseStmt(), - /*IsRelatedToDecl=*/false); - } + FixableGadgetSets FixablesForAllVars; + + auto [FixableGadgets, WarningGadgets, Tracker] = + findGadgets(D, Handler, EmitSuggestions); - // This return guarantees that most of the machine doesn't run when - // suggestions aren't requested. - assert(FixableGadgets.size() == 0 && - "Fixable gadgets found but suggestions not requested!"); - return; + if (!EmitSuggestions) { + // Our job is very easy without suggestions. Just warn about + // every problematic operation and consider it done. No need to deal + // with fixable gadgets, no need to group operations by variable. + for (const auto &G : WarningGadgets) { + Handler.handleUnsafeOperation(G->getBaseStmt(), + /*IsRelatedToDecl=*/false); } - UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); - FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets)); - Tracker = std::move(TrackerRes); + // This return guarantees that most of the machine doesn't run when + // suggestions aren't requested. + assert(FixableGadgets.size() == 0 && + "Fixable gadgets found but suggestions not requested!"); + return; } - std::map FixItsForVariable; + UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); + FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets)); + + std::map FixItsForVariableGroup; + DefMapTy VariableGroupsMap{}; // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. - for (auto it = FixablesForUnsafeVars.byVar.cbegin(); - it != FixablesForUnsafeVars.byVar.cend();) { + for (auto it = FixablesForAllVars.byVar.cbegin(); + it != FixablesForAllVars.byVar.cend();) { // FIXME: Support ParmVarDecl as well. if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) { - it = FixablesForUnsafeVars.byVar.erase(it); + it = FixablesForAllVars.byVar.erase(it); } else { ++it; } } llvm::SmallVector UnsafeVars; - for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar) + for (const auto &[VD, ignore] : FixablesForAllVars.byVar) UnsafeVars.push_back(VD); + + // Fixpoint iteration for pointer assignments + using DepMapTy = DenseMap>; + DepMapTy DependenciesMap{}; + DepMapTy PtrAssignmentGraph{}; + + for (auto it : FixablesForAllVars.byVar) { + for (const FixableGadget *fixable : it.second) { + std::optional> ImplPair = + fixable->getStrategyImplications(); + if (ImplPair) { + std::pair Impl = ImplPair.value(); + PtrAssignmentGraph[Impl.first].insert(Impl.second); + } + } + } + + /* + The following code does a BFS traversal of the `PtrAssignmentGraph` + considering all unsafe vars as starting nodes and constructs an undirected + graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner + elimiates all variables that are unreachable from any unsafe var. In other + words, this removes all dependencies that don't include any unsafe variable + and consequently don't need any fixit generation. + Note: A careful reader would observe that the code traverses + `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and + `Adj` and not between `CurrentVar` and `Adj`. Both approaches would + achieve the same result but the one used here dramatically cuts the + amount of hoops the second part of the algorithm needs to jump, given that + a lot of these connections become "direct". The reader is advised not to + imagine how the graph is transformed because of using `Var` instead of + `CurrentVar`. The reader can continue reading as if `CurrentVar` was used, + and think about why it's equivalent later. + */ + std::set VisitedVarsDirected{}; + for (const auto &[Var, ignore] : UnsafeOps.byVar) { + if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) { + + std::queue QueueDirected{}; + QueueDirected.push(Var); + while(!QueueDirected.empty()) { + const VarDecl* CurrentVar = QueueDirected.front(); + QueueDirected.pop(); + VisitedVarsDirected.insert(CurrentVar); + auto AdjacentNodes = PtrAssignmentGraph[CurrentVar]; + for (const VarDecl *Adj : AdjacentNodes) { + if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) { + QueueDirected.push(Adj); + } + DependenciesMap[Var].insert(Adj); + DependenciesMap[Adj].insert(Var); + } + } + } + } + + // Group Connected Components for Unsafe Vars + // (Dependencies based on pointer assignments) + std::set VisitedVars{}; + for (const auto &[Var, ignore] : UnsafeOps.byVar) { + if (VisitedVars.find(Var) == VisitedVars.end()) { + std::vector VarGroup{}; + + std::queue Queue{}; + Queue.push(Var); + while(!Queue.empty()) { + const VarDecl* CurrentVar = Queue.front(); + Queue.pop(); + VisitedVars.insert(CurrentVar); + VarGroup.push_back(CurrentVar); + auto AdjacentNodes = DependenciesMap[CurrentVar]; + for (const VarDecl *Adj : AdjacentNodes) { + if (VisitedVars.find(Adj) == VisitedVars.end()) { + Queue.push(Adj); + } + } + } + for (const VarDecl * V : VarGroup) { + if (UnsafeOps.byVar.find(V) != UnsafeOps.byVar.end()) { + VariableGroupsMap[V] = VarGroup; + } + } + } + } Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); - FixItsForVariable = getFixIts(FixablesForUnsafeVars, NaiveStrategy, Tracker, - D->getASTContext(), Handler); + FixItsForVariableGroup = getFixIts(FixablesForAllVars, NaiveStrategy, Tracker, + D->getASTContext(), Handler, VariableGroupsMap); // FIXME Detect overlapping FixIts. @@ -1670,10 +1919,11 @@ } for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { - auto FixItsIt = FixItsForVariable.find(VD); - Handler.handleFixableVariable(VD, FixItsIt != FixItsForVariable.end() - ? std::move(FixItsIt->second) - : FixItList{}); + auto FixItsIt = FixItsForVariableGroup.find(VD); + Handler.handleUnsafeVariableGroup(VD, VariableGroupsMap, + FixItsIt != FixItsForVariableGroup.end() + ? std::move(FixItsIt->second) + : FixItList{}); for (const auto &G : WarningGadgets) { Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true); } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2218,8 +2218,8 @@ } } - // FIXME: rename to handleUnsafeVariable - void handleFixableVariable(const VarDecl *Variable, + void handleUnsafeVariableGroup(const VarDecl *Variable, + const DefMapTy &VarGrpMap, FixItList &&Fixes) override { assert(!SuggestSuggestions && "Unsafe buffer usage fixits displayed without suggestions!"); @@ -2227,11 +2227,52 @@ << Variable << (Variable->getType()->isPointerType() ? 0 : 1) << Variable->getSourceRange(); if (!Fixes.empty()) { - unsigned FixItStrategy = 0; // For now we only has 'std::span' strategy + const auto VarGroupForVD = VarGrpMap.find(Variable)->second; + unsigned FixItStrategy = 0; // For now we only have 'std::span' strategy const auto &FD = S.Diag(Variable->getLocation(), - diag::note_unsafe_buffer_variable_fixit); + diag::note_unsafe_buffer_variable_fixit_group); + + FD << Variable << FixItStrategy; + std::string AllVars = ""; + if (VarGroupForVD.size() > 1) { + if (VarGroupForVD.size() == 2) { + if (VarGroupForVD[0] == Variable) { + AllVars.append("'" + VarGroupForVD[1]->getName().str() + "'"); + } else { + AllVars.append("'" + VarGroupForVD[0]->getName().str() + "'"); + } + } else { + bool first = false; + if (VarGroupForVD.size() == 3) { + for (const VarDecl * V : VarGroupForVD) { + if (V == Variable) { + continue; + } + if (!first) { + first = true; + AllVars.append("'" + V->getName().str() + "'" + " and "); + } else { + AllVars.append("'" + V->getName().str() + "'"); + } + } + } else { + for (const VarDecl * V : VarGroupForVD) { + if (V == Variable) { + continue; + } + if (VarGroupForVD.back() != V) { + AllVars.append("'" + V->getName().str() + "'" + ", "); + } else { + AllVars.append("and '" + V->getName().str() + "'"); + } + } + } + } + FD << AllVars << 1; + } else { + FD << "" << 0; + } - FD << Variable->getName() << FixItStrategy; for (const auto &F : Fixes) FD << F; } diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-fixits-test.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-fixits-test.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-fixits-test.cpp @@ -0,0 +1,138 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s + +void foo1a() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = r; + int tmp = p[9]; + int *q; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + q = r; +} + +void foo1b() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = r; + int tmp = p[9]; + int *q = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + q = r; + tmp = q[9]; +} + +void foo1c() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + p = r; + int tmp = r[9]; + int *q; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + q = r; + tmp = q[9]; +} + +void foo2a() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[5]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 5}" + int *q = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = q; + int tmp = p[8]; + q = r; +} + +void foo2b() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[5]; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 5}" + int *q = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = q; + int tmp = q[8]; + q = r; +} + +void foo2c() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[5]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 5}" + int *q = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = q; + int tmp = p[8]; + q = r; + tmp = q[8]; +} + +void foo3a() { + int *r = new int[7]; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + int *p = new int[5]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 5}" + int *q = new int[4]; + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + q = p; + int tmp = p[8]; + q = r; +} + +void foo3b() { + int *r = new int[10]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}" + int *p = new int[10]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}" + int *q = new int[10]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span q" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}" + q = p; + int tmp = q[8]; + q = r; +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc-fixits.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc-fixits.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc-fixits.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +void bar(int * param) {} + +void foo1a() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + p = r; + int tmp = p[9]; + int *q; + q = r; +} + +void uuc_if_body() { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + if (true) + p = r; + p[5] = 4; +} + +void uuc_if_body1(bool flag) { + int *r = new int[7]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span r" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 7}" + int *p = new int[4]; + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{" + // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", 4}" + if (flag) { + p = r; + } + p[5] = 4; +} + +void uuc_if_cond_no_unsafe_op() { + int *r = new int[7]; + int *p = new int[4]; + if ((p = r)) { + int x = 0; + } +} + +void uuc_if_cond_unsafe_op() { + int *r = new int[7]; + int *p = new int[4]; //expected-warning{{'p' is an unsafe pointer used for buffer access}} + if ((p = r)) { + p[3] = 2; // expected-note{{used in buffer access here}} + } +} + +void uuc_if_cond_unsafe_op1() { + int *r = new int[7]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *p = new int[4]; + if ((p = r)) { + r[3] = 2; // expected-note{{used in buffer access here}} + } +} + +void uuc_if_cond_unsafe_op2() { + int *r = new int[7]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} + if ((p = r)) { + r[3] = 2; // expected-note{{used in buffer access here}} + } + p[4] = 6; // expected-note{{used in buffer access here}} +} + +void uuc_call1() { + int *w = new int[4]; // expected-warning{{'w' is an unsafe pointer used for buffer access}} + int *y = new int[4]; + bar(w = y); + w[5] = 0; // expected-note{{used in buffer access here}} +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-uuc.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -verify %s +void bar(int * param) {} + +void foo1a() { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + p = r; + int tmp = p[9]; // expected-note{{used in buffer access here}} + int *q; + q = r; +} + +void uuc_if_body() { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} // expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + if (true) + p = r; + p[5] = 4; // expected-note{{used in buffer access here}} +} + +void uuc_if_body1(bool flag) { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} // expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + if (flag) { + p = r; + } + p[5] = 4; // expected-note{{used in buffer access here}} +} + +void uuc_if_body2(bool flag) { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} // expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + if (flag) { + } else { + p = r; + } + + p[5] = 4; // expected-note{{used in buffer access here}} +} + +void uuc_if_cond_no_unsafe_op() { + int *r = new int[7]; + int *p = new int[4]; + if ((p = r)) { + int x = 0; + } +} + +void uuc_if_cond_no_unsafe_op1() { + int *r = new int[7]; + int *p = new int[4]; + if (true) { + int x = 0; + } else if ((p = r)) + int y = 10; +} + +void uuc_if_cond_unsafe_op() { + int *r = new int[7]; + int *p = new int[4]; //expected-warning{{'p' is an unsafe pointer used for buffer access}} + if ((p = r)) { + p[3] = 2; // expected-note{{used in buffer access here}} + } +} + +void uuc_if_cond_unsafe_op1() { + int *r = new int[7]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *p = new int[4]; + if ((p = r)) { + r[3] = 2; // expected-note{{used in buffer access here}} + } +} + +void uuc_if_cond_unsafe_op2() { + int *r = new int[7]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} + if ((p = r)) { + r[3] = 2; // expected-note{{used in buffer access here}} + } + p[4] = 6; // expected-note{{used in buffer access here}} +} + +void uuc_call1() { + int *w = new int[4]; // expected-warning{{'w' is an unsafe pointer used for buffer access}} + int *y = new int[4]; + bar(w = y); + w[5] = 0; // expected-note{{used in buffer access here}} +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-multi-decl-warnings.cpp @@ -0,0 +1,347 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -verify %s + +namespace std { + class type_info { }; +} + +void local_assign_both_span() { + int tmp; + int* p = new int[10]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them$}}}} + tmp = p[4]; // expected-note{{used in buffer access here}} + + int* q = new int[10]; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'p' to 'std::span' to propagate bounds information between them$}}}} + tmp = q[4]; // expected-note{{used in buffer access here}} + + q = p; +} + +void local_assign_rhs_span() { + int tmp; + int* p = new int[10]; + int* q = new int[10]; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information$}}}} + tmp = q[4]; // expected-note{{used in buffer access here}} + p = q; +} + +void local_assign_no_span() { + int tmp; + int* p = new int[10]; + int* q = new int[10]; + p = q; +} + +void local_assign_lhs_span() { + int tmp; + int* p = new int[10]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them$}}}} + tmp = p[4]; // expected-note{{used in buffer access here}} + int* q = new int[10]; + + p = q; +} + + +// FIXME: Support initializations at declarations. +void lhs_span_multi_assign() { + int *a = new int[2]; + int *b = a; + int *c = b; + int *d = c; // expected-warning{{'d' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'd' to 'std::span' to preserve bounds information$}}}} + int tmp = d[2]; // expected-note{{used in buffer access here}} +} + +void rhs_span() { + int *x = new int[3]; + int *y; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} + y[5] = 10; // expected-note{{used in buffer access here}} + + x = y; +} + +void rhs_span1() { + int *q = new int[12]; + int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} + p[5] = 10; // expected-note{{used in buffer access here}} + int *r = q; // expected-warning{{'r' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information$}}}} + r[10] = 5; // expected-note{{used in buffer access here}} +} + +void rhs_span2() { + int *q = new int[6]; + int *p = q; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} + p[5] = 10; // expected-note{{used in buffer access here}} + int *r = q; +} + +void test_grouping() { + int *z = new int[8]; + int tmp; + int *y = new int[10]; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} + tmp = y[5]; // expected-note{{used in buffer access here}} + + int *x = new int[10]; + x = y; + + int *w = z; +} + +void test_grouping1() { + int tmp; + int *y = new int[10]; // expected-warning{{'y' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'y' to 'std::span' to preserve bounds information$}}}} + tmp = y[5]; // expected-note{{used in buffer access here}} + int *x = new int[10]; + x = y; + + int *w = new int[10]; // expected-warning{{'w' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'w' to 'std::span' to preserve bounds information$}}}} + tmp = w[5]; // expected-note{{used in buffer access here}} + int *z = new int[10]; + z = w; +} + +void foo1a() { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + p = r; + int tmp = p[9]; // expected-note{{used in buffer access here}} + int *q; + q = r; +} + +void foo1b() { + int *r = new int[7]; + int *p = new int[4]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' and 'q' to 'std::span' to propagate bounds information between them$}}}} + p = r; + int tmp = p[9]; // expected-note{{used in buffer access here}} + int *q; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'p' and 'r' to 'std::span' to propagate bounds information between them$}}}} + q = r; + tmp = q[9]; // expected-note{{used in buffer access here}} +} + +void foo1c() { + int *r = new int[7]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them$}}}} + int *p = new int[4]; + p = r; + int tmp = r[9]; // expected-note{{used in buffer access here}} + int *q; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + q = r; + tmp = q[9]; // expected-note{{used in buffer access here}} +} + +void foo2a() { + int *r = new int[7]; + int *p = new int[5]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' and 'q' to 'std::span' to propagate bounds information between them$}}}} + int *q = new int[4]; + p = q; + int tmp = p[8]; // expected-note{{used in buffer access here}} + q = r; +} + +void foo2b() { + int *r = new int[7]; + int *p = new int[5]; + int *q = new int[4]; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' to 'std::span' to propagate bounds information between them$}}}} + p = q; + int tmp = q[8]; // expected-note{{used in buffer access here}} + q = r; +} + +void foo2c() { + int *r = new int[7]; + int *p = new int[5]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' and 'q' to 'std::span' to propagate bounds information between them$}}}} + int *q = new int[4]; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'p' and 'r' to 'std::span' to propagate bounds information between them$}}}} + p = q; + int tmp = p[8]; // expected-note{{used in buffer access here}} + q = r; + tmp = q[8]; // expected-note{{used in buffer access here}} +} + +void foo3a() { + int *r = new int[7]; + int *p = new int[5]; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information$}}}} + int *q = new int[4]; + q = p; + int tmp = p[8]; // expected-note{{used in buffer access here}} + q = r; +} + +void foo3b() { + int *r = new int[7]; + int *p = new int[5]; + int *q = new int[4]; // expected-warning{{'q' is an unsafe pointer used for buffer access}} //expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' and 'p' to 'std::span' to propagate bounds information between them$}}}} + q = p; + int tmp = q[8]; // expected-note{{used in buffer access here}} + q = r; +} + +void test_crash() { + int *r = new int[8]; + int *q = r; + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'q' to 'std::span' to propagate bounds information between them$}}}} + p = q; + int tmp = p[9]; // expected-note{{used in buffer access here}} +} + +void foo_uuc() { + int *ptr; + int *local; // expected-warning{{'local' is an unsafe pointer used for buffer access}} + local = ptr; + local++; // expected-note{{used in pointer arithmetic here}} + + (local = ptr) += 5; // expected-warning{{unsafe pointer arithmetic}} +} + +void check_rhs_fix() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} // expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information, and change 'x' to 'std::span' to propagate bounds information between them$}}}} + int *x; + r[7] = 9; // expected-note{{used in buffer access here}} + r = x; +} + +void check_rhs_nofix() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + r = x; + x++; // expected-note{{used in pointer arithmetic here}} +} + +void check_rhs_nofix_order() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + x++; // expected-note{{used in pointer arithmetic here}} + r[7] = 9; // expected-note{{used in buffer access here}} + r = x; +} + +void check_rhs_nofix_order1() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + x++; // expected-note{{used in pointer arithmetic here}} + r = x; +} + +void check_rhs_nofix_order2() { + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + x++; // expected-note{{used in pointer arithmetic here}} + r = x; +} + +void check_rhs_nofix_order3() { + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r = x; + r[7] = 9; // expected-note{{used in buffer access here}} + x++; // expected-note{{used in pointer arithmetic here}} +} + +void check_rhs_nofix_order4() { + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + r = x; + x++; // expected-note{{used in pointer arithmetic here}} +} + +void no_unhandled_lhs() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} // expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information, and change 'x' to 'std::span' to propagate bounds information between them$}}}} + r[7] = 9; // expected-note{{used in buffer access here}} + int *x; + r = x; +} + +const std::type_info unhandled_lhs() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + int *x; + r = x; + return typeid(*r); +} + +const std::type_info unhandled_rhs() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + r[7] = 9; // expected-note{{used in buffer access here}} + int *x; + r = x; + return typeid(*x); +} + +void test_negative_index() { + int *x = new int[4]; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} + p = &x[1]; // expected-note{{used in buffer access here}} + p[-1] = 9; // expected-note{{used in buffer access here}} +} + +void test_unfixable() { + int *r = new int[8]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *x; // expected-warning{{'x' is an unsafe pointer used for buffer access}} + x[7] = 9; // expected-note{{used in buffer access here}} + r = x; + r++; // expected-note{{used in pointer arithmetic here}} +} + +void test_cyclic_deps() { + int *r = new int[10]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information, and change 'q' and 'p' to 'std::span' to propagate bounds information between them$}}}} + int *q; + q = r; + int *p; + p = q; + r[3] = 9; // expected-note{{used in buffer access here}} + r = p; +} + +void test_cyclic_deps_a() { + int *r = new int[10]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} + int *q; + q = r; + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} + p = q; + r[3] = 9; // expected-note{{used in buffer access here}} + r = p; + p++; // expected-note{{used in pointer arithmetic here}} +} + +void test_cyclic_deps1() { + int *r = new int[10]; + int *q; + q = r; + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' and 'q' to 'std::span' to propagate bounds information between them$}}}} + p = q; + p[3] = 9; // expected-note{{used in buffer access here}} + r = p; +} + +void test_cyclic_deps2() { + int *r = new int[10]; + int *q; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' and 'p' to 'std::span' to propagate bounds information between them$}}}} + q = r; + int *p; + p = q; + q[3] = 9; // expected-note{{used in buffer access here}} + r = p; +} + +void test_cyclic_deps3() { + int *r = new int[10]; + int *q; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' and 'p' to 'std::span' to propagate bounds information between them$}}}} + q = r; + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'q' and 'r' to 'std::span' to propagate bounds information between them$}}}} + p = q; + q[3] = 9; // expected-note{{used in buffer access here}} + p[4] = 7; // expected-note{{used in buffer access here}} + r = p; +} + +void test_cyclic_deps4() { + int *r = new int[10]; // expected-warning{{'r' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'r' to 'std::span' to preserve bounds information, and change 'q' and 'p' to 'std::span' to propagate bounds information between them$}}}} + int *q; // expected-warning{{'q' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'q' to 'std::span' to preserve bounds information, and change 'r' and 'p' to 'std::span' to propagate bounds information between them$}}}} + q = r; + int *p; // expected-warning{{'p' is an unsafe pointer used for buffer access}} expected-note-re{{{{^change type of 'p' to 'std::span' to preserve bounds information, and change 'r' and 'q' to 'std::span' to propagate bounds information between them$}}}} + p = q; + q[3] = 9; // expected-note{{used in buffer access here}} + p[4] = 7; // expected-note{{used in buffer access here}} + r[1] = 5; // expected-note{{used in buffer access here}} + r = p; +}