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 @@ -16,8 +16,10 @@ #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" +#include "clang/Sema/Sema.h" namespace clang { +class Sema; using DefMapTy = llvm::DenseMap>; @@ -45,21 +47,12 @@ /// Returns a reference to the `Preprocessor`: virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const = 0; - - /// Returns the text indicating that the user needs to provide input there: - virtual std::string - getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") const { - std::string s = std::string("<# "); - s += HintTextToUser; - s += " #>"; - return s; - } }; // 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 EmitSuggestions); + bool EmitSuggestions, Sema &Sema); namespace internal { // Tests if any two `FixItHint`s in `FixIts` conflict. Two `FixItHint`s 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 @@ -1290,6 +1290,14 @@ return EOL; } +// Returns the text indicating that the user needs to provide input there: +std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") { + std::string s = std::string("<# "); + s += HintTextToUser; + s += " #>"; + return s; +} + // Return the text representation of the given `APInt Val`: static std::string getAPIntText(APInt Val) { SmallVector Txt; @@ -1756,11 +1764,28 @@ } // Returns the text representation of clang::unsafe_buffer_usage attribute. -static std::string getUnsafeBufferUsageAttributeText() { - static const char *const RawAttr = "[[clang::unsafe_buffer_usage]]"; - std::stringstream SS; - SS << RawAttr << getEndOfLine().str(); - return SS.str(); +// `WSSuffix` holds customized "white-space"s, e.g., newline or whilespace +// characters. +static std::string +getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, Sema &S, + StringRef WSSuffix = "") { + Preprocessor &PP = S.getPreprocessor(); + TokenValue ClangUnsafeBufferUsageTokens[] = { + tok::l_square, + tok::l_square, + PP.getIdentifierInfo("clang"), + tok::coloncolon, + PP.getIdentifierInfo("unsafe_buffer_usage"), + tok::r_square, + tok::r_square}; + + StringRef MacroName; + + // The returned macro (it returns) is guaranteed not to be function-like: + MacroName = PP.getLastMacroWithSpelling(Loc, ClangUnsafeBufferUsageTokens); + if (MacroName.empty()) + MacroName = "[[clang::unsafe_buffer_usage]]"; + return MacroName.str() + WSSuffix.str(); } // For a `FunDecl`, one of whose `ParmVarDecl`s is being changed to have a new @@ -1805,11 +1830,11 @@ static std::optional createOverloadsForFixedParams(unsigned ParmIdx, StringRef NewTyText, const FunctionDecl *FD, const ASTContext &Ctx, - UnsafeBufferUsageHandler &Handler) { + UnsafeBufferUsageHandler &Handler, Sema &Sema) { // FIXME: need to make this conflict checking better: if (hasConflictingOverload(FD)) return std::nullopt; - + const SourceManager &SM = Ctx.getSourceManager(); const LangOptions &LangOpts = Ctx.getLangOpts(); // FIXME Respect indentation of the original code. @@ -1859,7 +1884,7 @@ // A lambda that creates the text representation of a function definition with // the original signature: const auto OldOverloadDefCreator = - [&Handler, &SM, + [&SM, &Sema, &LangOpts](const FunctionDecl *FD, unsigned ParmIdx, StringRef NewTypeText) -> std::optional { std::stringstream SS; @@ -1869,7 +1894,8 @@ if (auto FDPrefix = getRangeText( SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM, LangOpts)) - SS << getUnsafeBufferUsageAttributeText() << FDPrefix->str() << "{"; + SS << getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), Sema, " ") + << FDPrefix->str() << "{"; else return std::nullopt; // Append: "return" func-name "(" @@ -1890,7 +1916,7 @@ if (i == ParmIdx) // This is our spanified paramter! SS << NewTypeText.str() << "(" << Parm->getIdentifier()->getName().str() << ", " - << Handler.getUserFillPlaceHolder("size") << ")"; + << getUserFillPlaceHolder("size") << ")"; else SS << Parm->getIdentifier()->getName().str(); if (i != NumParms - 1) @@ -1921,7 +1947,8 @@ // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`: if (!FReDecl->hasAttr()) { FixIts.emplace_back(FixItHint::CreateInsertion( - FReDecl->getBeginLoc(), getUnsafeBufferUsageAttributeText())); + FReDecl->getBeginLoc(), getUnsafeBufferUsageAttributeTextAt( + FReDecl->getBeginLoc(), Sema, " "))); } // Inserts a declaration with the new signature to the end of `FReDecl`: if (auto NewOverloadDecl = @@ -1938,7 +1965,7 @@ // new overload of the function so that the change is self-contained (see // `createOverloadsForFixedParams`). static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, - UnsafeBufferUsageHandler &Handler) { + UnsafeBufferUsageHandler &Handler, Sema &Sema) { if (PVD->hasDefaultArg()) // FIXME: generate fix-its for default values: return {}; @@ -1990,7 +2017,7 @@ } if (ParmIdx < FD->getNumParams()) if (auto OverloadFix = createOverloadsForFixedParams(ParmIdx, SpanTyText, - FD, Ctx, Handler)) { + FD, Ctx, Handler, Sema)) { Fixes.append(*OverloadFix); return Fixes; } @@ -2013,7 +2040,7 @@ (void)DS; // FIXME: handle cases where DS has multiple declarations - return fixVarDeclWithSpan(VD, Ctx, Handler.getUserFillPlaceHolder()); + return fixVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder()); } // TODO: we should be consistent to use `std::nullopt` to represent no-fix due @@ -2022,7 +2049,7 @@ fixVariable(const VarDecl *VD, Strategy::Kind K, /* The function decl under analysis */ const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, - UnsafeBufferUsageHandler &Handler) { + UnsafeBufferUsageHandler &Handler, Sema &Sema) { if (const auto *PVD = dyn_cast(VD)) { auto *FD = dyn_cast(PVD->getDeclContext()); if (!FD || FD != D) @@ -2049,7 +2076,7 @@ case Strategy::Kind::Span: { if (VD->getType()->isPointerType()) { if (const auto *PVD = dyn_cast(VD)) - return fixParamWithSpan(PVD, Ctx, Handler); + return fixParamWithSpan(PVD, Ctx, Handler, Sema); if (VD->isLocalVarDecl()) return fixVariableWithSpan(VD, Tracker, Ctx, Handler); @@ -2097,11 +2124,11 @@ ASTContext &Ctx, /* The function decl under analysis */ const Decl *D, const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, - const DefMapTy &VarGrpMap) { + const DefMapTy &VarGrpMap, Sema &Sema) { std::map FixItsForVariable; for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) { FixItsForVariable[VD] = - fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler); + fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler, Sema); // If we fail to produce Fix-It for the declaration we have to skip the // variable entirely. if (FixItsForVariable[VD].empty()) { @@ -2171,7 +2198,7 @@ FixItList GroupFix; if (FixItsForVariable.find(Var) == FixItsForVariable.end()) { GroupFix = fixVariable(Var, ReplacementTypeForVD, D, - Tracker, Var->getASTContext(), Handler); + Tracker, Var->getASTContext(), Handler, Sema); } else { GroupFix = FixItsForVariable[Var]; } @@ -2197,7 +2224,7 @@ void clang::checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, - bool EmitSuggestions) { + bool EmitSuggestions, Sema &Sema) { assert(D && D->getBody()); // Do not emit fixit suggestions for functions declared in an @@ -2343,7 +2370,7 @@ FixItsForVariableGroup = getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D, - Tracker, Handler, VariableGroupsMap); + Tracker, Handler, VariableGroupsMap, Sema); // FIXME Detect overlapping FixIts. 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 @@ -2423,7 +2423,7 @@ !Diags.isIgnored(diag::warn_unsafe_buffer_variable, Node->getBeginLoc())) { clang::checkUnsafeBufferUsage(Node, R, - UnsafeBufferUsageShouldEmitSuggestions); + UnsafeBufferUsageShouldEmitSuggestions, S); } // More analysis ... diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-attributes-spelling.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-attributes-spelling.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-attributes-spelling.cpp @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions -DCMD_UNSAFE_ATTR=[[clang::unsafe_buffer_usage]] %s 2>&1 | FileCheck %s + + +// no need to check fix-its for definition in this test ... +void foo(int *p) { + int tmp = p[5]; +} + +// Will use the macro defined from the command line: +void foo(int *); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"CMD_UNSAFE_ATTR " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:";\nvoid foo(std::span)" + + +#undef CMD_UNSAFE_ATTR +void foo(int *); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:";\nvoid foo(std::span)" + + +#define UNSAFE_ATTR [[clang::unsafe_buffer_usage]] + +void foo(int *); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"UNSAFE_ATTR " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:";\nvoid foo(std::span)" + +#undef UNSAFE_ATTR + +#if __has_cpp_attribute(clang::unsafe_buffer_usage) +#define UNSAFE_ATTR [[clang::unsafe_buffer_usage]] +#endif + +void foo(int *); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"UNSAFE_ATTR " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:";\nvoid foo(std::span)" + +#undef UNSAFE_ATTR + +#if __has_cpp_attribute(clang::unsafe_buffer_usage) +// we don't know how to use this macro +#define UNSAFE_ATTR(x) [[clang::unsafe_buffer_usage]] +#endif + +void foo(int *); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:16-[[@LINE-2]]:16}:";\nvoid foo(std::span)" + +#undef UNSAFE_ATTR + +#define UNSAFE_ATTR_1 [[clang::unsafe_buffer_usage]] +#define UNSAFE_ATTR_2 [[clang::unsafe_buffer_usage]] +#define UNSAFE_ATTR_3 [[clang::unsafe_buffer_usage]] + +// Should use the last defined macro (i.e., UNSAFE_ATTR_3) for +// `[[clang::unsafe_buffer_usage]]` +void foo(int *p); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"UNSAFE_ATTR_3 " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:17-[[@LINE-2]]:17}:";\nvoid foo(std::span p)" + + +#define WRONG_ATTR_1 [clang::unsafe_buffer_usage]] +#define WRONG_ATTR_2 [[clang::unsafe_buffer_usage] +#define WRONG_ATTR_3 [[clang::unsafe_buffer_usag]] + +// The last defined macro for +// `[[clang::unsafe_buffer_usage]]` is still UNSAFE_ATTR_3 +void foo(int *p); +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"UNSAFE_ATTR_3 " +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:17-[[@LINE-2]]:17}:";\nvoid foo(std::span p)" diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-overload.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-overload.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-overload.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-overload.cpp @@ -46,11 +46,11 @@ int tmp; tmp = p[5]; } - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid foo(int *p) {return foo(std::span(p, <# size #>));}\n" + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void foo(int *p) {return foo(std::span(p, <# size #>));}\n" // Similarly, `NS::bar` is distinct from `bar`: void bar(int *p); - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:3}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:3}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:19}:";\nvoid bar(std::span p)" } // end of namespace NS @@ -60,7 +60,7 @@ int tmp; tmp = p[5]; } -// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid NS::bar(int *p) {return NS::bar(std::span(p, <# size #>));}\n" +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void NS::bar(int *p) {return NS::bar(std::span(p, <# size #>));}\n" namespace NESTED { void alpha(int); @@ -74,7 +74,7 @@ int tmp; tmp = p[5]; } - // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:6}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid alpha(int *p) {return alpha(std::span(p, <# size #>));}\n" + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:6}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void alpha(int *p) {return alpha(std::span(p, <# size #>));}\n" } } diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-qualified-names.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-qualified-names.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-qualified-names.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span-qualified-names.cpp @@ -2,15 +2,15 @@ namespace NS1 { void foo(int *); - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK-DAG: fix-it:"{{.*}}:{[[@LINE-2]]:18-[[@LINE-2]]:18}:";\nvoid foo(std::span)" namespace NS2 { void foo(int *); - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:5-[[@LINE-1]]:5}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:5-[[@LINE-1]]:5}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK-DAG: fix-it:"{{.*}}:{[[@LINE-2]]:20-[[@LINE-2]]:20}:";\nvoid foo(std::span)" namespace NS3 { void foo(int *); - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:7-[[@LINE-1]]:7}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:7-[[@LINE-1]]:7}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK-DAG: fix-it:"{{.*}}:{[[@LINE-2]]:22-[[@LINE-2]]:22}:";\nvoid foo(std::span)" } } @@ -23,21 +23,21 @@ int tmp; tmp = p[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid NS1::foo(int *p) {return NS1::foo(std::span(p, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void NS1::foo(int *p) {return NS1::foo(std::span(p, <# size #>));}\n" void NS1::NS2::foo(int *p) { // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:20-[[@LINE-1]]:26}:"std::span p" int tmp; tmp = p[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid NS1::NS2::foo(int *p) {return NS1::NS2::foo(std::span(p, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void NS1::NS2::foo(int *p) {return NS1::NS2::foo(std::span(p, <# size #>));}\n" void NS1::NS2::NS3::foo(int *p) { // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:31}:"std::span p" int tmp; tmp = p[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid NS1::NS2::NS3::foo(int *p) {return NS1::NS2::NS3::foo(std::span(p, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void NS1::NS2::NS3::foo(int *p) {return NS1::NS2::NS3::foo(std::span(p, <# size #>));}\n" void f(NS1::MyType * x) { @@ -45,4 +45,4 @@ NS1::MyType tmp; tmp = x[5]; } -// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid f(NS1::MyType * x) {return f(std::span(x, <# size #>));}\n" +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void f(NS1::MyType * x) {return f(std::span(x, <# size #>));}\n" diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp @@ -6,13 +6,13 @@ #define INCLUDE_ME void simple(int *p); -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:20-[[@LINE-2]]:20}:";\nvoid simple(std::span p)" #else void simple(int *); -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} " // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:19-[[@LINE-2]]:19}:";\nvoid simple(std::span)" void simple(int *p) { @@ -20,7 +20,7 @@ int tmp; tmp = p[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid simple(int *p) {return simple(std::span(p, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void simple(int *p) {return simple(std::span(p, <# size #>));}\n" void twoParms(int *p, int * q) { @@ -29,14 +29,14 @@ int tmp; tmp = p[5] + q[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid twoParms(int *p, int * q) {return twoParms(std::span(p, <# size #>), q);}\n" -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:2-[[@LINE-2]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid twoParms(int *p, int * q) {return twoParms(p, std::span(q, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void twoParms(int *p, int * q) {return twoParms(std::span(p, <# size #>), q);}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:2-[[@LINE-2]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void twoParms(int *p, int * q) {return twoParms(p, std::span(q, <# size #>));}\n" void ptrToConst(const int * x) { // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:17-[[@LINE-1]]:30}:"std::span x" int tmp = x[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid ptrToConst(const int * x) {return ptrToConst(std::span(x, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void ptrToConst(const int * x) {return ptrToConst(std::span(x, <# size #>));}\n" // The followings test cases where multiple FileIDs maybe involved // when the analyzer loads characters from source files. @@ -50,7 +50,7 @@ // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:35-[[@LINE-1]]:42}:"std::span x" int tmp = x[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid FUN_NAME(macro_defined_name)(int * x) {return FUN_NAME(macro_defined_name)(std::span(x, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void FUN_NAME(macro_defined_name)(int * x) {return FUN_NAME(macro_defined_name)(std::span(x, <# size #>));}\n" // The followings test various type specifiers @@ -59,13 +59,13 @@ // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:24-[[@LINE-1]]:49}:"std::span p" auto tmp = p[5]; } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid simpleSpecifier(unsigned long long int *p) {return simpleSpecifier(std::span(p, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void simpleSpecifier(unsigned long long int *p) {return simpleSpecifier(std::span(p, <# size #>));}\n" void attrParm([[maybe_unused]] int * p) { // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:34-[[@LINE-1]]:41}:"std::span p" int tmp = p[5]; } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid attrParm({{\[}}{{\[}}maybe_unused{{\]}}{{\]}} int * p) {return attrParm(std::span(p, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void attrParm({{\[}}{{\[}}maybe_unused{{\]}}{{\]}} int * p) {return attrParm(std::span(p, <# size #>));}\n" using T = unsigned long long int; @@ -73,7 +73,7 @@ // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:31-[[@LINE-1]]:36}:"std::span p" int tmp = p[5]; } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid usingTypenameSpecifier(T * p) {return usingTypenameSpecifier(std::span(p, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void usingTypenameSpecifier(T * p) {return usingTypenameSpecifier(std::span(p, <# size #>));}\n" typedef unsigned long long int T2; @@ -81,7 +81,7 @@ // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:31}:"std::span p" int tmp = p[5]; } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid typedefSpecifier(T2 * p) {return typedefSpecifier(std::span(p, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void typedefSpecifier(T2 * p) {return typedefSpecifier(std::span(p, <# size #>));}\n" class SomeClass { } C; @@ -90,7 +90,7 @@ // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:27-[[@LINE-1]]:52}:"std::span p" if (++p) {} } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid classTypeSpecifier(const class SomeClass * p) {return classTypeSpecifier(std::span(p, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void classTypeSpecifier(const class SomeClass * p) {return classTypeSpecifier(std::span(p, <# size #>));}\n" struct { // anon @@ -113,9 +113,9 @@ if (++r) {} if (++rr) {} } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(std::span(p, <# size #>), q, r, rr);}\n - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:4-[[@LINE-2]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(p, q, std::span(r, <# size #>), rr);}\n" - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-3]]:4-[[@LINE-3]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(p, q, r, std::span(rr, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(std::span(p, <# size #>), q, r, rr);}\n + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:4-[[@LINE-2]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(p, q, std::span(r, <# size #>), rr);}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-3]]:4-[[@LINE-3]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decltypeSpecifier(decltype(C) * p, decltype(ANON_S) * q, decltype(NAMED_S) * r,\n{{.*}}decltype(NAMED_S) ** rr) {return decltypeSpecifier(p, q, r, std::span(rr, <# size #>));}\n" #define MACRO_TYPE(T) long T @@ -125,8 +125,8 @@ int tmp = p[5]; tmp = q[5]; } - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid macroType(unsigned MACRO_TYPE(int) * p, unsigned MACRO_TYPE(long) * q) {return macroType(std::span(p, <# size #>), q);}\n" - // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:4-[[@LINE-2]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid macroType(unsigned MACRO_TYPE(int) * p, unsigned MACRO_TYPE(long) * q) {return macroType(p, std::span(q, <# size #>));}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:4-[[@LINE-1]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void macroType(unsigned MACRO_TYPE(int) * p, unsigned MACRO_TYPE(long) * q) {return macroType(std::span(p, <# size #>), q);}\n" + // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-2]]:4-[[@LINE-2]]:4}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void macroType(unsigned MACRO_TYPE(int) * p, unsigned MACRO_TYPE(long) * q) {return macroType(p, std::span(q, <# size #>));}\n" } // The followings test various declarators: @@ -135,7 +135,7 @@ int tmp; tmp = a[5]; } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid decayedArray(int a[]) {return decayedArray(std::span(a, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void decayedArray(int a[]) {return decayedArray(std::span(a, <# size #>));}\n" void decayedArrayOfArray(int a[10][10]) { // CHECK-NOT: fix-it:{{.*}}:{[[@LINE-1]] diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp @@ -5,13 +5,13 @@ // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:29}:"std::span const x" int tmp = x[5]; // expected-note{{used in buffer access here}} } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid const_ptr(int * const x) {return const_ptr(std::span(x, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr(int * const x) {return const_ptr(std::span(x, <# size #>));}\n" void const_ptr_to_const(const int * const x) {// expected-warning{{'x' is an unsafe pointer used for buffer access}} expected-note{{change type of 'x' to 'std::span' to preserve bounds information}} // CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:44}:"std::span const x" int tmp = x[5]; // expected-note{{used in buffer access here}} } -// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid const_ptr_to_const(const int * const x) {return const_ptr_to_const(std::span(x, <# size #>));}\n" +// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr_to_const(const int * const x) {return const_ptr_to_const(std::span(x, <# size #>));}\n" typedef struct {int x;} NAMED_UNNAMED_STRUCT; // an unnamed struct type named by a typedef typedef struct {int x;} * PTR_TO_ANON; // pointer to an unnamed struct @@ -24,7 +24,7 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()" } } -// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}}\nvoid namedPointeeType(NAMED_UNNAMED_STRUCT * p) {return namedPointeeType(std::span(p, <# size #>));}\n" +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {return namedPointeeType(std::span(p, <# size #>));}\n" // We CANNOT fix a pointer to an unnamed type // CHECK-NOT: fix-it: