Skip to content

Commit 41279e8

Browse files
committed
[-Wunsafe-buffer-usage] Refactor and improve for parameter fix-its
- Factor out the code that will be shared by both parameter and local variable fix-its - Add a check to ensure that a TypeLoc::isNull is false before using the TypeLoc - Remove the special check for whether a fixing variable involves unnamed types. This check is unnecessary now. - Move tests for cv-qualified parameters and unnamed types out of the "...-unsupported.cpp" test file. Reviewed by: NoQ (Artem Dergachev) Differential revision: https://reviews.llvm.org/D156188
1 parent b20a385 commit 41279e8

File tree

3 files changed

+182
-65
lines changed

3 files changed

+182
-65
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 92 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,13 +1380,35 @@ static std::optional<StringRef> getRangeText(SourceRange SR,
13801380
return std::nullopt;
13811381
}
13821382

1383+
// Returns the begin location of the identifier of the given variable
1384+
// declaration.
1385+
static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD) {
1386+
// According to the implementation of `VarDecl`, `VD->getLocation()` actually
1387+
// returns the begin location of the identifier of the declaration:
1388+
return VD->getLocation();
1389+
}
1390+
1391+
// Returns the literal text of the identifier of the given variable declaration.
1392+
static std::optional<StringRef>
1393+
getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM,
1394+
const LangOptions &LangOpts) {
1395+
SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
1396+
SourceLocation ParmIdentEndLoc =
1397+
Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
1398+
1399+
if (ParmIdentEndLoc.isMacroID() &&
1400+
!Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
1401+
return std::nullopt;
1402+
return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
1403+
}
1404+
13831405
// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
13841406
// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
13851407
// have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
13861408
// :( ), `Qualifiers` of the pointee type is returned separately through the
13871409
// output parameter `QualifiersToAppend`.
13881410
static std::optional<std::string>
1389-
getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM,
1411+
getPointeeTypeText(const VarDecl *VD, const SourceManager &SM,
13901412
const LangOptions &LangOpts,
13911413
std::optional<Qualifiers> *QualifiersToAppend) {
13921414
QualType Ty = VD->getType();
@@ -1395,15 +1417,36 @@ getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM,
13951417
assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
13961418
"Expecting a VarDecl of type of pointer to object type");
13971419
PteTy = Ty->getPointeeType();
1398-
if (PteTy->hasUnnamedOrLocalType())
1399-
// If the pointee type is unnamed, we can't refer to it
1420+
1421+
TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
1422+
TypeLoc PteTyLoc;
1423+
1424+
// We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
1425+
// the `TypeLoc` of the pointee type:
1426+
switch (TyLoc.getTypeLocClass()) {
1427+
case TypeLoc::ConstantArray:
1428+
case TypeLoc::IncompleteArray:
1429+
case TypeLoc::VariableArray:
1430+
case TypeLoc::DependentSizedArray:
1431+
case TypeLoc::Decayed:
1432+
assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
1433+
"pointer type unless it decays.");
1434+
PteTyLoc = TyLoc.getNextTypeLoc();
1435+
break;
1436+
case TypeLoc::Pointer:
1437+
PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
1438+
break;
1439+
default:
1440+
return std::nullopt;
1441+
}
1442+
if (PteTyLoc.isNull())
1443+
// Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
1444+
// when the pointer type is `auto`.
14001445
return std::nullopt;
14011446

1402-
TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc();
1403-
TypeLoc PteTyLoc = TyLoc.getUnqualifiedLoc().getNextTypeLoc();
1404-
SourceLocation VDNameStartLoc = VD->getLocation();
1447+
SourceLocation IdentLoc = getVarDeclIdentifierLoc(VD);
14051448

1406-
if (!(VDNameStartLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
1449+
if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
14071450
// We are expecting these locations to be valid. But in some cases, they are
14081451
// not all valid. It is a Clang bug to me and we are not responsible for
14091452
// fixing it. So we will just give up for now when it happens.
@@ -1414,7 +1457,11 @@ getPointeeTypeText(const ParmVarDecl *VD, const SourceManager &SM,
14141457
SourceLocation PteEndOfTokenLoc =
14151458
Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
14161459

1417-
if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, VDNameStartLoc)) {
1460+
if (!PteEndOfTokenLoc.isValid())
1461+
// Sometimes we cannot get the end location of the pointee type, e.g., when
1462+
// there are macros involved.
1463+
return std::nullopt;
1464+
if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
14181465
// We only deal with the cases where the source text of the pointee type
14191466
// appears on the left-hand side of the variable identifier completely,
14201467
// including the following forms:
@@ -1739,6 +1786,32 @@ Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), "failed to produce fixit for
17391786
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
17401787
#endif
17411788

1789+
// For the given variable declaration with a pointer-to-T type, returns the text
1790+
// `std::span<T>`. If it is unable to generate the text, returns
1791+
// `std::nullopt`.
1792+
static std::optional<std::string> createSpanTypeForVarDecl(const VarDecl *VD,
1793+
const ASTContext &Ctx) {
1794+
assert(VD->getType()->isPointerType());
1795+
1796+
std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
1797+
std::optional<std::string> PteTyText = getPointeeTypeText(
1798+
VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
1799+
1800+
if (!PteTyText)
1801+
return std::nullopt;
1802+
1803+
std::string SpanTyText = "std::span<";
1804+
1805+
SpanTyText.append(*PteTyText);
1806+
// Append qualifiers to span element type if any:
1807+
if (PteTyQualifiers) {
1808+
SpanTyText.append(" ");
1809+
SpanTyText.append(PteTyQualifiers->getAsString());
1810+
}
1811+
SpanTyText.append(">");
1812+
return SpanTyText;
1813+
}
1814+
17421815
// For a `VarDecl` of the form `T * var (= Init)?`, this
17431816
// function generates a fix-it for the declaration, which re-declares `var` to
17441817
// be of `span<T>` type and transforms the initializer, if present, to a span
@@ -1995,39 +2068,22 @@ static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
19952068
return {};
19962069
}
19972070

1998-
std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
1999-
std::optional<std::string> PteTyText = getPointeeTypeText(
2000-
PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2001-
2002-
if (!PteTyText) {
2003-
DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
2004-
return {};
2005-
}
2006-
2007-
std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
2008-
2009-
if (!PVDNameText) {
2010-
DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
2011-
return {};
2012-
}
2013-
2014-
std::string SpanOpen = "std::span<";
2015-
std::string SpanClose = ">";
2016-
std::string SpanTyText;
20172071
std::stringstream SS;
2072+
std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
2073+
std::optional<StringRef> ParmIdentText;
20182074

2019-
SS << SpanOpen << *PteTyText;
2020-
// Append qualifiers to span element type:
2021-
if (PteTyQualifiers)
2022-
SS << " " << PteTyQualifiers->getAsString();
2023-
SS << SpanClose;
2024-
// Save the Span type text:
2025-
SpanTyText = SS.str();
2075+
if (!SpanTyText)
2076+
return {};
2077+
SS << *SpanTyText;
20262078
// Append qualifiers to the type of the parameter:
20272079
if (PVD->getType().hasQualifiers())
20282080
SS << " " << PVD->getType().getQualifiers().getAsString();
2081+
ParmIdentText =
2082+
getVarDeclIdentifierText(PVD, Ctx.getSourceManager(), Ctx.getLangOpts());
2083+
if (!ParmIdentText)
2084+
return {};
20292085
// Append parameter's name:
2030-
SS << " " << PVDNameText->str();
2086+
SS << " " << ParmIdentText->str();
20312087

20322088
FixItList Fixes;
20332089
unsigned ParmIdx = 0;
@@ -2040,7 +2096,7 @@ static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
20402096
ParmIdx++;
20412097
}
20422098
if (ParmIdx < FD->getNumParams())
2043-
if (auto OverloadFix = createOverloadsForFixedParams(ParmIdx, SpanTyText,
2099+
if (auto OverloadFix = createOverloadsForFixedParams(ParmIdx, *SpanTyText,
20442100
FD, Ctx, Handler)) {
20452101
Fixes.append(*OverloadFix);
20462102
return Fixes;

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-span.cpp

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,78 @@ void complexDeclarator(int * (*a[10])[10]) {
147147
if (++a){}
148148
}
149149

150-
// Make sure we do not generate fixes for the following cases:
150+
// Tests parameters with cv-qualifiers
151151

152-
#define MACRO_NAME MyName
152+
void const_ptr(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}}
153+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:29}:"std::span<int> const x"
154+
int tmp = x[5]; // expected-note{{used in buffer access here}}
155+
}
156+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_ptr(int * const x) {return const_ptr(std::span<int>(x, <# size #>));}\n"
153157

154-
void macroIdentifier(int *MACRO_NAME) { // The fix-it ends with a macro. It will be discarded due to overlap with macros.
155-
// CHECK-NOT: fix-it:{{.*}}:{[[@LINE-1]]
156-
if (++MyName){}
158+
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}}
159+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:44}:"std::span<int const> const x"
160+
int tmp = x[5]; // expected-note{{used in buffer access here}}
161+
}
162+
// CHECK: 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<int const>(x, <# size #>));}\n"
163+
164+
void const_volatile_ptr(int * const volatile 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}}
165+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:47}:"std::span<int> const volatile x"
166+
int tmp = x[5]; // expected-note{{used in buffer access here}}
167+
}
168+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_volatile_ptr(int * const volatile x) {return const_volatile_ptr(std::span<int>(x, <# size #>));}\n"
169+
170+
void volatile_const_ptr(int * volatile 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}}
171+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:47}:"std::span<int> const volatile x"
172+
int tmp = x[5]; // expected-note{{used in buffer access here}}
173+
}
174+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void volatile_const_ptr(int * volatile const x) {return volatile_const_ptr(std::span<int>(x, <# size #>));}\n"
175+
176+
void const_volatile_ptr_to_const_volatile(const volatile int * const volatile 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}}
177+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:43-[[@LINE-1]]:80}:"std::span<int const volatile> const volatile x"
178+
int tmp = x[5]; // expected-note{{used in buffer access here}}
179+
}
180+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void const_volatile_ptr_to_const_volatile(const volatile int * const volatile x) {return const_volatile_ptr_to_const_volatile(std::span<int const volatile>(x, <# size #>));}\n"
181+
182+
183+
// Test if function declaration specifiers are handled correctly:
184+
185+
static void static_f(int *p) {
186+
p[5] = 5;
157187
}
188+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} static void static_f(int *p) {return static_f(std::span<int>(p, <# size #>));}\n"
158189

159-
// CHECK-NOT: fix-it:{{.*}}:
160-
void parmHasNoName(int *p, int *) { // cannot fix the function because there is one parameter has no name.
190+
static inline void static_inline_f(int *p) {
161191
p[5] = 5;
162192
}
193+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} static inline void static_inline_f(int *p) {return static_inline_f(std::span<int>(p, <# size #>));}\n"
194+
195+
inline void static static_inline_f2(int *p) {
196+
p[5] = 5;
197+
}
198+
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} inline void static static_inline_f2(int *p) {return static_inline_f2(std::span<int>(p, <# size #>));}\n"
199+
200+
201+
// Test when unnamed types are involved:
202+
203+
typedef struct {int x;} UNNAMED_STRUCT;
204+
struct {int x;} VarOfUnnamedType;
205+
206+
void useUnnamedType(UNNAMED_STRUCT * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} \
207+
expected-note{{change type of 'p' to 'std::span' to preserve bounds information}}
208+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:21-[[@LINE-2]]:39}:"std::span<UNNAMED_STRUCT> p"
209+
if (++p) { // expected-note{{used in pointer arithmetic here}}
210+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()"
211+
}
212+
}
213+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void useUnnamedType(UNNAMED_STRUCT * p) {return useUnnamedType(std::span<UNNAMED_STRUCT>(p, <# size #>));}\n"
214+
215+
void useUnnamedType2(decltype(VarOfUnnamedType) * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} \
216+
expected-note{{change type of 'p' to 'std::span' to preserve bounds information}}
217+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:22-[[@LINE-2]]:52}:"std::span<decltype(VarOfUnnamedType)> p"
218+
if (++p) { // expected-note{{used in pointer arithmetic here}}
219+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()"
220+
}
221+
}
222+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void useUnnamedType2(decltype(VarOfUnnamedType) * p) {return useUnnamedType2(std::span<decltype(VarOfUnnamedType)>(p, <# size #>));}\n"
163223

164224
#endif

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-parm-unsupported.cpp

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
11
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fcxx-exceptions -fsafe-buffer-usage-suggestions -verify %s
22
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fcxx-exceptions -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s
33

4-
void const_ptr(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}}
5-
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:16-[[@LINE-1]]:29}:"std::span<int> const x"
6-
int tmp = x[5]; // expected-note{{used in buffer access here}}
7-
}
8-
// 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<int>(x, <# size #>));}\n"
9-
10-
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}}
11-
// CHECK-DAG: fix-it:{{.*}}:{[[@LINE-1]]:25-[[@LINE-1]]:44}:"std::span<int const> const x"
12-
int tmp = x[5]; // expected-note{{used in buffer access here}}
13-
}
14-
// 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<int const>(x, <# size #>));}\n"
4+
typedef int * TYPEDEF_PTR;
5+
#define MACRO_PTR int*
156

16-
typedef struct {int x;} NAMED_UNNAMED_STRUCT; // an unnamed struct type named by a typedef
17-
typedef struct {int x;} * PTR_TO_ANON; // pointer to an unnamed struct
18-
typedef NAMED_UNNAMED_STRUCT * PTR_TO_NAMED; // pointer to a named type
7+
// We CANNOT fix a pointer whose type is defined in a typedef or a
8+
// macro. Because if the typedef is changed after the fix, the fix
9+
// becomes incorrect and may not be noticed.
1910

20-
// We can fix a pointer to a named type
21-
void namedPointeeType(NAMED_UNNAMED_STRUCT * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}\ expected-note{{change type of 'p' to 'std::span' to preserve bounds information}}
22-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:23-[[@LINE-1]]:47}:"std::span<NAMED_UNNAMED_STRUCT> p"
11+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
12+
void typedefPointer(TYPEDEF_PTR p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
2313
if (++p) { // expected-note{{used in pointer arithmetic here}}
24-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:10}:"(p = p.subspan(1)).data()"
2514
}
2615
}
27-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void namedPointeeType(NAMED_UNNAMED_STRUCT * p) {return namedPointeeType(std::span<NAMED_UNNAMED_STRUCT>(p, <# size #>));}\n"
2816

29-
// We CANNOT fix a pointer to an unnamed type
30-
// CHECK-NOT: fix-it:
31-
void unnamedPointeeType(PTR_TO_ANON p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
17+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]]
18+
void macroPointer(MACRO_PTR p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
3219
if (++p) { // expected-note{{used in pointer arithmetic here}}
3320
}
3421
}
@@ -148,3 +135,17 @@ void parmWithDefaultValueDecl(int * x) {
148135
int tmp;
149136
tmp = x[5]; // expected-note{{used in buffer access here}}
150137
}
138+
139+
#define MACRO_NAME MyName
140+
141+
// The fix-it ends with a macro. It will be discarded due to overlap with macros.
142+
// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
143+
void macroIdentifier(int * MACRO_NAME) { // expected-warning{{'MyName' is an unsafe pointer used for buffer access}}
144+
if (++MyName){} // expected-note{{used in pointer arithmetic here}}
145+
}
146+
147+
// CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]]
148+
void parmHasNoName(int *p, int *) { // cannot fix the function because there is one parameter has no name. \
149+
expected-warning{{'p' is an unsafe pointer used for buffer access}}
150+
p[5] = 5; // expected-note{{used in buffer access here}}
151+
}

0 commit comments

Comments
 (0)