Skip to content

Commit da47129

Browse files
committed
[-Wunsafe-buffer-usage] Preserve spelling of array size in std::array fixits
Fixits related to arrays generally change type from T [N] to std::array<T, N>. N has to be constant but doesn't have to be an integer literal. It can be for example a constant. The fixits need to preserve the spelling of the array size. The assumption is that not specifying the numerical value directly was a conscious decision of the original author and it'll very likely still apply to the transformed code. E. g. for int buffer[SIZE]; if `buffer` is unsafe it should become: std::array<int, SIZE> buffer; rather than: std::array<int, 42> buffer;
1 parent 83e31ad commit da47129

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,10 +2475,6 @@ static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
24752475
if (auto CAT = dyn_cast<clang::ConstantArrayType>(D->getType())) {
24762476
const QualType &ArrayEltT = CAT->getElementType();
24772477
assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
2478-
// Producing fix-it for variable declaration---make `D` to be of std::array
2479-
// type:
2480-
SmallString<32> Replacement;
2481-
raw_svector_ostream OS(Replacement);
24822478

24832479
// For most types the transformation is simple:
24842480
// T foo[10]; => std::array<T, 10> foo;
@@ -2496,6 +2492,21 @@ static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
24962492
return {};
24972493
}
24982494

2495+
// Find the '[' token.
2496+
std::optional<Token> NextTok = Lexer::findNextToken(getVarDeclIdentifierLoc(D), Ctx.getSourceManager(), Ctx.getLangOpts());
2497+
while (NextTok && !NextTok->is(tok::l_square))
2498+
NextTok = Lexer::findNextToken(NextTok->getLocation(), Ctx.getSourceManager(), Ctx.getLangOpts());
2499+
if (!NextTok)
2500+
return {};
2501+
const SourceLocation LSqBracketLoc = NextTok->getLocation();
2502+
2503+
// Get the spelling of the array size as written in the source file (including macros, etc.).
2504+
auto MaybeArraySizeTxt = getRangeText({LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()}, Ctx.getSourceManager(), Ctx.getLangOpts());
2505+
if (!MaybeArraySizeTxt)
2506+
return {};
2507+
2508+
const std::string ArraySizeTxt = MaybeArraySizeTxt->str();
2509+
24992510
std::optional<StringRef> IdentText =
25002511
getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
25012512

@@ -2504,8 +2515,10 @@ static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
25042515
return {};
25052516
}
25062517

2518+
SmallString<32> Replacement;
2519+
raw_svector_ostream OS(Replacement);
25072520
OS << "std::array<" << ArrayEltT.getAsString() << ", "
2508-
<< getAPIntText(CAT->getSize()) << "> " << IdentText->str();
2521+
<< ArraySizeTxt << "> " << IdentText->str();
25092522

25102523
FixIts.push_back(FixItHint::CreateReplacement(
25112524
SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-array.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ void local_array(unsigned idx) {
1010
buffer[idx] = 0;
1111
}
1212

13+
void weird_whitespace_in_declaration(unsigned idx) {
14+
int buffer_w [ 10 ] ;
15+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:35}:"std::array<int, 10 > buffer_w"
16+
buffer_w[idx] = 0;
17+
}
18+
19+
void weird_comments_in_declaration(unsigned idx) {
20+
int /* [ ] */ buffer_w /* [ ] */ [ /* [ ] */ 10 /* [ ] */ ] ;
21+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:67}:"std::array<int, /* [ ] */ 10 /* [ ] */ > buffer_w"
22+
buffer_w[idx] = 0;
23+
}
24+
1325
void unsupported_multi_decl1(unsigned idx) {
1426
int a, buffer[10];
1527
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
@@ -55,23 +67,25 @@ template void local_array_in_template<int>(unsigned); // FIXME: expected note {{
5567

5668
void macro_as_identifier(unsigned idx) {
5769
#define MY_BUFFER buffer
58-
// Although fix-its include macros, the macros do not overlap with
59-
// the bounds of the source range of these fix-its. So these fix-its
60-
// are valid.
61-
6270
int MY_BUFFER[10];
6371
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:20}:"std::array<int, 10> MY_BUFFER"
6472
MY_BUFFER[idx] = 0;
65-
6673
#undef MY_BUFFER
6774
}
6875

69-
void unsupported_fixit_overlapping_macro(unsigned idx) {
70-
#define MY_INT int
71-
MY_INT buffer[10];
76+
void macro_as_size(unsigned idx) {
77+
#define MY_TEN 10
78+
int buffer[MY_TEN];
7279
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
7380
buffer[idx] = 0;
74-
#undef MY_INT
81+
#undef MY_TEN
82+
}
83+
84+
void constant_as_size(unsigned idx) {
85+
const unsigned my_const = 10;
86+
int buffer[my_const];
87+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:23}:"std::array<int, my_const> buffer"
88+
buffer[idx] = 0;
7589
}
7690

7791
void subscript_negative() {

0 commit comments

Comments
 (0)