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 @@ -54,7 +54,7 @@ // This function invokes the analysis and allows the caller to react to it // through the handler class. void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, - bool EmitFixits); + bool EmitSuggestions); namespace internal { // Tests if any two `FixItHint`s in `FixIts` conflict. Two `FixItHint`s diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -95,6 +95,8 @@ /// Column limit for formatting message diagnostics, or 0 if unused. VALUE_DIAGOPT(MessageLength, 32, 0) +DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0) + #undef DIAGOPT #undef ENUM_DIAGOPT #undef VALUE_DIAGOPT 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 @@ -11825,6 +11825,8 @@ "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_safe_buffer_usage_suggestions_disabled : Note< + "pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions">; def err_loongarch_builtin_requires_la32 : Error< "this builtin requires target: loongarch32">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1564,6 +1564,11 @@ Group, Flags<[CC1Option]>, HelpText<"Print a template comparison tree for differing templates">, MarshallingInfoFlag>; +defm safe_buffer_usage_suggestions : BoolFOption<"safe-buffer-usage-suggestions", + DiagnosticOpts<"ShowSafeBufferUsageSuggestions">, DefaultFalse, + PosFlag, + NegFlag>; def fdiscard_value_names : Flag<["-"], "fdiscard-value-names">, Group, HelpText<"Discard value names in LLVM IR">, Flags<[NoXarchOption]>; def fno_discard_value_names : Flag<["-"], "fno-discard-value-names">, Group, 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 @@ -870,7 +870,8 @@ /// Scan the function and return a list of gadgets found with provided kits. static std::tuple -findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler) { +findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, + bool EmitSuggestions) { struct GadgetFinderCallback : MatchFinder::MatchCallback { FixableGadgetList FixableGadgets; @@ -923,35 +924,44 @@ // clang-format off M.addMatcher( - stmt(eachOf( - // A `FixableGadget` matcher and a `WarningGadget` matcher should not disable - // each other (they could if they were put in the same `anyOf` group). - // We also should make sure no two `FixableGadget` (resp. `WarningGadget`) matchers - // match for the same node, so that we can group them - // in one `anyOf` group (for better performance via short-circuiting). - forEachDescendantStmt(stmt(eachOf( -#define FIXABLE_GADGET(x) \ - x ## Gadget::matcher().bind(#x), + stmt( + forEachDescendantEvaluatedStmt(stmt(anyOf( + // Add Gadget::matcher() for every gadget in the registry. +#define WARNING_GADGET(x) \ + allOf(x ## Gadget::matcher().bind(#x), \ + notInSafeBufferOptOut(&Handler)), #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" - // In parallel, match all DeclRefExprs so that to find out - // whether there are any uncovered by gadgets. - declRefExpr(anyOf(hasPointerType(), hasArrayType()), to(varDecl())).bind("any_dre") - ))), - forEachDescendantEvaluatedStmt(stmt(anyOf( - // Add Gadget::matcher() for every gadget in the registry. -#define WARNING_GADGET(x) \ - allOf(x ## Gadget::matcher().bind(#x), notInSafeBufferOptOut(&Handler)), -#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" - // Also match DeclStmts because we'll need them when fixing - // their underlying VarDecls that otherwise don't have - // any backreferences to DeclStmts. - declStmt().bind("any_ds") - )) - ))), + // Avoid a hanging comma. + unless(stmt()) + ))) + ), &CB ); // clang-format on + if (EmitSuggestions) { + // clang-format off + M.addMatcher( + stmt( + forEachDescendantStmt(stmt(eachOf( +#define FIXABLE_GADGET(x) \ + x ## Gadget::matcher().bind(#x), +#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" + // In parallel, match all DeclRefExprs so that to find out + // whether there are any uncovered by gadgets. + declRefExpr(anyOf(hasPointerType(), hasArrayType()), + to(varDecl())).bind("any_dre"), + // Also match DeclStmts because we'll need them when fixing + // their underlying VarDecls that otherwise don't have + // any backreferences to DeclStmts. + declStmt().bind("any_ds") + ))) + ), + &CB + ); + // clang-format on + } + M.match(*D->getBody(), D->getASTContext()); // Gadgets "claim" variables they're responsible for. Once this loop finishes, @@ -1601,15 +1611,32 @@ void clang::checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, - bool EmitFixits) { + bool EmitSuggestions) { assert(D && D->getBody()); - WarningGadgetSets UnsafeOps; FixableGadgetSets FixablesForUnsafeVars; DeclUseTracker Tracker; { - auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D, Handler); + 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); + } + + // 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; + } + UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets)); FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets)); Tracker = std::move(TrackerRes); @@ -1617,36 +1644,33 @@ std::map FixItsForVariable; - if (EmitFixits) { - // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. - for (auto it = FixablesForUnsafeVars.byVar.cbegin(); - it != FixablesForUnsafeVars.byVar.cend();) { - // FIXME: Support ParmVarDecl as well. - if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) { - it = FixablesForUnsafeVars.byVar.erase(it); - } else { - ++it; - } + // Filter out non-local vars and vars with unclaimed DeclRefExpr-s. + for (auto it = FixablesForUnsafeVars.byVar.cbegin(); + it != FixablesForUnsafeVars.byVar.cend();) { + // FIXME: Support ParmVarDecl as well. + if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) { + it = FixablesForUnsafeVars.byVar.erase(it); + } else { + ++it; } + } - llvm::SmallVector UnsafeVars; - for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar) - UnsafeVars.push_back(VD); + llvm::SmallVector UnsafeVars; + for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar) + UnsafeVars.push_back(VD); - Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); - FixItsForVariable = getFixIts(FixablesForUnsafeVars, NaiveStrategy, Tracker, - D->getASTContext(), Handler); + Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars); + FixItsForVariable = getFixIts(FixablesForUnsafeVars, NaiveStrategy, Tracker, + D->getASTContext(), Handler); - // FIXME Detect overlapping FixIts. - } + // FIXME Detect overlapping FixIts. for (const auto &G : UnsafeOps.noVar) { Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false); } for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) { - auto FixItsIt = - EmitFixits ? FixItsForVariable.find(VD) : FixItsForVariable.end(); + auto FixItsIt = FixItsForVariable.find(VD); Handler.handleFixableVariable(VD, FixItsIt != FixItsForVariable.end() ? std::move(FixItsIt->second) : FixItList{}); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7066,6 +7066,9 @@ A->claim(); } + Args.addOptInFlag(CmdArgs, options::OPT_fsafe_buffer_usage_suggestions, + options::OPT_fno_safe_buffer_usage_suggestions); + // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) { 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 @@ -2161,9 +2161,11 @@ namespace { class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { Sema &S; + bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? public: - UnsafeBufferUsageReporter(Sema &S) : S(S) {} + UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions) + : S(S), SuggestSuggestions(SuggestSuggestions) {} void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl) override { @@ -2197,20 +2199,30 @@ } } else { if (isa(Operation)) { + // note_unsafe_buffer_operation doesn't have this mode yet. + assert(!IsRelatedToDecl && "Not implemented yet!"); MsgParam = 3; } Loc = Operation->getBeginLoc(); Range = Operation->getSourceRange(); } - if (IsRelatedToDecl) + if (IsRelatedToDecl) { + assert(!SuggestSuggestions && + "Variables blamed for unsafe buffer usage without suggestions!"); S.Diag(Loc, diag::note_unsafe_buffer_operation) << MsgParam << Range; - else + } else { S.Diag(Loc, diag::warn_unsafe_buffer_operation) << MsgParam << Range; + if (SuggestSuggestions) { + S.Diag(Loc, diag::note_safe_buffer_usage_suggestions_disabled); + } + } } // FIXME: rename to handleUnsafeVariable void handleFixableVariable(const VarDecl *Variable, FixItList &&Fixes) override { + assert(!SuggestSuggestions && + "Unsafe buffer usage fixits displayed without suggestions!"); S.Diag(Variable->getLocation(), diag::warn_unsafe_buffer_variable) << Variable << (Variable->getType()->isPointerType() ? 0 : 1) << Variable->getSourceRange(); @@ -2350,19 +2362,28 @@ // exit if having uncompilable errors or ignoring all warnings: return; - // Whether -Wunsafe-buffer-usage should emit fix-its: - const bool UnsafeBufferEmitFixits = - Diags.getDiagnosticOptions().ShowFixits && S.getLangOpts().CPlusPlus20; - UnsafeBufferUsageReporter R(S); + DiagnosticOptions &DiagOpts = Diags.getDiagnosticOptions(); + + // UnsafeBufferUsage analysis settings. + bool UnsafeBufferUsageCanEmitSuggestions = S.getLangOpts().CPlusPlus20; + bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. + UnsafeBufferUsageCanEmitSuggestions && + DiagOpts.ShowSafeBufferUsageSuggestions; + bool UnsafeBufferUsageShouldSuggestSuggestions = + UnsafeBufferUsageCanEmitSuggestions && + !DiagOpts.ShowSafeBufferUsageSuggestions; + UnsafeBufferUsageReporter R(S, UnsafeBufferUsageShouldSuggestSuggestions); // The Callback function that performs analyses: auto CallAnalyzers = [&](const Decl *Node) -> void { - // Perform unsafe buffer analysis: + // Perform unsafe buffer usage analysis: if (!Diags.isIgnored(diag::warn_unsafe_buffer_operation, Node->getBeginLoc()) || !Diags.isIgnored(diag::warn_unsafe_buffer_variable, - Node->getBeginLoc())) - clang::checkUnsafeBufferUsage(Node, R, UnsafeBufferEmitFixits); + Node->getBeginLoc())) { + clang::checkUnsafeBufferUsage(Node, R, + UnsafeBufferUsageShouldEmitSuggestions); + } // More analysis ... }; diff --git a/clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp b/clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp --- a/clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp +++ b/clang/test/SemaCXX/unsafe-buffer-usage-diag-type.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -verify %s +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions -verify %s namespace localVar { void testRefersPtrLocalVarDecl(int i) { diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-crashes.c b/clang/test/SemaCXX/warn-unsafe-buffer-usage-crashes.c --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-crashes.c +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-crashes.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -Wunsafe-buffer-usage %s -verify %s +// RUN: %clang_cc1 -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions \ +// RUN: %s -verify %s void gnu_stmtexpr_crash(void) { struct A {}; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -triple=arm-apple -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -triple=arm-apple \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s int f(unsigned long, void *); diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s // TODO cases where we don't want fixits diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-deref-simple-ptr-arith.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-deref-simple-ptr-arith.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-deref-simple-ptr-arith.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-deref-simple-ptr-arith.cpp @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits \ +// RUN: -fsyntax-only %s 2>&1 | FileCheck %s // TODO test we don't mess up vertical whitespace // TODO test different whitespaces diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s typedef int * Int_ptr_t; typedef int Int_t; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s void foo(int* v) { } diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-deref.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s void basic_dereference() { int tmp; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pre-increment.cpp @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -triple=arm-apple -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -triple=arm-apple \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s void foo(int * , int *); diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-unevaluated-context.cpp @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits \ +// RUN: -fsyntax-only %s 2>&1 | FileCheck %s namespace std { class type_info; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions -verify %s [[clang::unsafe_buffer_usage]] void deprecatedFunction3(); diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-no-fixits.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-no-fixits.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-no-fixits.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-no-fixits.cpp @@ -1,23 +1,55 @@ -// RUN: %clang_cc1 -x c -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=c89 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=gnu89 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=iso9899:1990 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=c89 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=gnu89 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=iso9899:1990 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=c17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=gnu17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=iso9899:2017 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c -std=c2x -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=c17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=gnu17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=iso9899:2017 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -std=c2x -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c++ -std=c++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c++ -std=gnu++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c++ -std=c++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x c++ -std=gnu++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -std=c++98 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -std=gnu++98 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -std=c++17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -std=gnu++17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x objective-c++ -std=c++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x objective-c++ -std=gnu++98 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x objective-c++ -std=c++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -x objective-c++ -std=gnu++17 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -std=c++98 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -std=gnu++98 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -std=c++17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -std=gnu++17 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s // CHECK-NOT: fix-it: diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-fixit.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s void basic(int * x) { int tmp; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma-misuse.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions -verify %s void beginUnclosed(int * x) { #pragma clang unsafe_buffer_usage begin diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-pragma.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -Wno-unused-value -verify %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -Wno-unused-value -verify %s void basic(int * x) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} int *p1 = new int[10]; // not to warn diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-source-ranges.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-source-ranges.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-source-ranges.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-source-ranges.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -Wno-everything -Wunsafe-buffer-usage -fdiagnostics-print-source-range-info %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -Wno-everything -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fdiagnostics-print-source-range-info %s 2>&1 | FileCheck %s void foo(int i) { int * ptr; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-suggestions-flag.cpp @@ -0,0 +1,66 @@ +// Test the -cc1 flag. There's no -fno-... option in -cc1 invocations, +// just the positive option. + +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify=OFF %s +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify=ON %s \ +// RUN: -fsafe-buffer-usage-suggestions + +// Test driver flags. Now there's both -f... and -fno-... to worry about. + +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -Xclang -verify=OFF %s +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=ON %s +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fno-safe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=OFF %s + +// In case of driver flags, last flag takes precedence. + +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fno-safe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=OFF %s +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fno-safe-buffer-usage-suggestions \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=ON %s + +// Passing through -Xclang. + +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -Xclang -fsafe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=ON %s + +// -Xclang flags take precedence over driver flags. + +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -Xclang -fsafe-buffer-usage-suggestions \ +// RUN: -fno-safe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=ON %s +// RUN: %clang -fsyntax-only -std=c++20 -Wunsafe-buffer-usage \ +// RUN: -fno-safe-buffer-usage-suggestions \ +// RUN: -Xclang -fsafe-buffer-usage-suggestions \ +// RUN: -Xclang -verify=ON %s + +[[clang::unsafe_buffer_usage]] void bar(int *); + +void foo(int *x) { // \ + // ON-warning{{'x' is an unsafe pointer used for buffer access}} + // FIXME: Better "OFF" warning? + x[5] = 10; // \ + // ON-note {{used in buffer access here}} \ + // OFF-warning{{unsafe buffer access}} \ + // OFF-note {{pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions}} + + x += 5; // \ + // ON-note {{used in pointer arithmetic here}} \ + // OFF-warning{{unsafe pointer arithmetic}} \ + // OFF-note {{pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions}} + + bar(x); // \ + // ON-warning{{function introduces unsafe buffer manipulation}} \ + // OFF-warning{{function introduces unsafe buffer manipulation}} \ + // OFF-note {{pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions}} +} diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-unevaluated-context.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-unevaluated-context.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-unevaluated-context.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-unevaluated-context.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fblocks -include %s -verify %s +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fblocks -include %s -verify %s // RUN: %clang -x c++ -frtti -fsyntax-only -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s // RUN: %clang_cc1 -std=c++11 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fblocks -include %s -verify %s +// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -fblocks -include %s -verify %s // RUN: %clang -x c++ -fsyntax-only -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s // RUN: %clang_cc1 -std=c++11 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s