Skip to content

Commit 7c3ad9e

Browse files
[-Wunsafe-buffer-usage] Fix fixits for span initialized from const size array (#81927)
Example: int arr[10]; int * ptr = arr; If ptr is unsafe and we transform it to std::span then the fixit we'd currently provide transforms the code to: std::span<int> ptr{arr, 10}; That's suboptimal as that repeats the size of the array in the code. The idiomatic transformation should rely on the span constructor that takes just the array argument and relies on template parameter autodeduction to set the span size. The transformed code should look like: std::span<int> ptr = arr; Note that it just should not change the initializer at all and that also works for other forms of initialization like: int * ptr {arr}; becoming: std::span<int> ptr{arr}; This patch changes the initializer handling to the desired (empty) fixit.
1 parent 4265ad1 commit 7c3ad9e

File tree

3 files changed

+36
-22
lines changed

3 files changed

+36
-22
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

+17-14
Original file line numberDiff line numberDiff line change
@@ -2152,15 +2152,18 @@ UPCPreIncrementGadget::getFixits(const FixitStrategy &S) const {
21522152
// In many cases, this function cannot figure out the actual extent `S`. It
21532153
// then will use a place holder to replace `S` to ask users to fill `S` in. The
21542154
// initializer shall be used to initialize a variable of type `std::span<T>`.
2155+
// In some cases (e. g. constant size array) the initializer should remain
2156+
// unchanged and the function returns empty list. In case the function can't
2157+
// provide the right fixit it will return nullopt.
21552158
//
21562159
// FIXME: Support multi-level pointers
21572160
//
21582161
// Parameters:
21592162
// `Init` a pointer to the initializer expression
21602163
// `Ctx` a reference to the ASTContext
2161-
static FixItList
2164+
static std::optional<FixItList>
21622165
FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
2163-
const StringRef UserFillPlaceHolder) {
2166+
const StringRef UserFillPlaceHolder) {
21642167
const SourceManager &SM = Ctx.getSourceManager();
21652168
const LangOptions &LangOpts = Ctx.getLangOpts();
21662169

@@ -2176,11 +2179,11 @@ FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
21762179
std::optional<SourceLocation> InitLocation =
21772180
getEndCharLoc(Init, SM, LangOpts);
21782181
if (!InitLocation)
2179-
return {};
2182+
return std::nullopt;
21802183

21812184
SourceRange SR(Init->getBeginLoc(), *InitLocation);
21822185

2183-
return {FixItHint::CreateRemoval(SR)};
2186+
return FixItList{FixItHint::CreateRemoval(SR)};
21842187
}
21852188

21862189
FixItList FixIts{};
@@ -2199,7 +2202,7 @@ FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
21992202
if (!Ext->HasSideEffects(Ctx)) {
22002203
std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
22012204
if (!ExtentString)
2202-
return {};
2205+
return std::nullopt;
22032206
ExtentText = *ExtentString;
22042207
}
22052208
} else if (!CxxNew->isArray())
@@ -2208,10 +2211,10 @@ FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
22082211
ExtentText = One;
22092212
} else if (const auto *CArrTy = Ctx.getAsConstantArrayType(
22102213
Init->IgnoreImpCasts()->getType())) {
2211-
// In cases `Init` is of an array type after stripping off implicit casts,
2212-
// the extent is the array size. Note that if the array size is not a
2213-
// constant, we cannot use it as the extent.
2214-
ExtentText = getAPIntText(CArrTy->getSize());
2214+
// std::span has a single parameter constructor for initialization with
2215+
// constant size array. The size is auto-deduced as the constructor is a
2216+
// function template. The correct fixit is empty - no changes should happen.
2217+
return FixItList{};
22152218
} else {
22162219
// In cases `Init` is of the form `&Var` after stripping of implicit
22172220
// casts, where `&` is the built-in operator, the extent is 1.
@@ -2227,7 +2230,7 @@ FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx,
22272230
std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
22282231

22292232
if (!LocPassInit)
2230-
return {};
2233+
return std::nullopt;
22312234

22322235
StrBuffer.append(", ");
22332236
StrBuffer.append(ExtentText);
@@ -2317,12 +2320,12 @@ static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
23172320
}
23182321
// Fix the initializer if it exists:
23192322
if (const Expr *Init = D->getInit()) {
2320-
FixItList InitFixIts =
2323+
std::optional<FixItList> InitFixIts =
23212324
FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
2322-
if (InitFixIts.empty())
2325+
if (!InitFixIts)
23232326
return {};
2324-
FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts.begin()),
2325-
std::make_move_iterator(InitFixIts.end()));
2327+
FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2328+
std::make_move_iterator(InitFixIts->end()));
23262329
// If the declaration has the form `T *ident = init`, we want to replace
23272330
// `T *ident = ` with `std::span<T> ident`:
23282331
EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \
2+
// RUN: -fsafe-buffer-usage-suggestions \
3+
// RUN: -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
4+
5+
void safe_array_initing_safe_ptr(unsigned idx) {
6+
int buffer[10];
7+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
8+
int* ptr = buffer;
9+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
10+
}
11+
12+
void safe_array_initing_unsafe_ptr(unsigned idx) {
13+
int buffer[123321123];
14+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
15+
int* ptr = buffer;
16+
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:13}:"std::span<int> ptr"
17+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:{{.*}}123321123
18+
ptr[idx + 1] = 0;
19+
}

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

-8
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,8 @@ void local_variable_qualifiers_specifiers() {
5252
int a[10];
5353
const int * p = a;
5454
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:18}:"std::span<int const> p"
55-
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:19}:"{"
56-
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:20}:", 10}"
5755
const int * const q = a;
5856
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:24}:"std::span<int const> const q"
59-
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:25-[[@LINE-2]]:25}:"{"
60-
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
6157
int tmp;
6258
tmp = p[5];
6359
tmp = q[5];
@@ -88,12 +84,8 @@ void local_ptr_to_array() {
8884
int b[n]; // If the extent expression does not have a constant value, we cannot fill the extent for users...
8985
int *p = a;
9086
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> p"
91-
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
92-
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:13}:", 10}"
9387
int *q = b;
9488
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> q"
95-
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
96-
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:13}:", <# placeholder #>}"
9789
// No way to know if `n` is ever mutated since `int b[n];`, so no way to figure out the extent
9890
tmp = p[5];
9991
tmp = q[5];

0 commit comments

Comments
 (0)