Skip to content

Commit fde4b80

Browse files
[-Wunsafe-buffer-usage] Minimize fixit range for pointer variables (#81935)
Example: int * const my_var = my_initializer; Currently when transforming my_var to std::span the fixits: - replace "int * const my_var = " with "std::span<int> const my_var {" - add ", SIZE}" after "my_initializer" where SIZE is either inferred or a placeholder This patch makes that behavior less intrusive by not modifying variable cv-qualifiers and initialization syntax. The new behavior is: - replace "int *" with "std::span<int>" - add "{" before "my_initializer" - add ", SIZE}" after "my_initializer" This is an improvement on its own - since we don't touch the identifier, we automatically can handle macros in them. It also simplifies future work on initializer fixits.
1 parent b3050f5 commit fde4b80

19 files changed

+222
-126
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2295,20 +2295,6 @@ static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
22952295
std::stringstream SS;
22962296

22972297
SS << *SpanTyText;
2298-
// Append qualifiers to the type of `D`, if any:
2299-
if (D->getType().hasQualifiers())
2300-
SS << " " << D->getType().getQualifiers().getAsString();
2301-
2302-
// The end of the range of the original source that will be replaced
2303-
// by `std::span<T> ident`:
2304-
SourceLocation EndLocForReplacement = D->getEndLoc();
2305-
std::optional<StringRef> IdentText =
2306-
getVarDeclIdentifierText(D, Ctx.getSourceManager(), Ctx.getLangOpts());
2307-
2308-
if (!IdentText) {
2309-
DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
2310-
return {};
2311-
}
23122298
// Fix the initializer if it exists:
23132299
if (const Expr *Init = D->getInit()) {
23142300
std::optional<FixItList> InitFixIts =
@@ -2317,15 +2303,22 @@ static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
23172303
return {};
23182304
FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
23192305
std::make_move_iterator(InitFixIts->end()));
2320-
// If the declaration has the form `T *ident = init`, we want to replace
2321-
// `T *ident = ` with `std::span<T> ident`:
2322-
EndLocForReplacement = Init->getBeginLoc().getLocWithOffset(-1);
23232306
}
2324-
SS << " " << IdentText->str();
2307+
// For declaration of the form `T * ident = init;`, we want to replace
2308+
// `T * ` with `std::span<T>`.
2309+
// We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2310+
// just `T *` with `std::span<T>`.
2311+
const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
23252312
if (!EndLocForReplacement.isValid()) {
23262313
DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
23272314
return {};
23282315
}
2316+
// The only exception is that for `T *ident` we'll add a single space between
2317+
// "std::span<T>" and "ident".
2318+
// FIXME: The condition is false for identifiers expended from macros.
2319+
if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2320+
SS << " ";
2321+
23292322
FixIts.push_back(FixItHint::CreateReplacement(
23302323
SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
23312324
return FixIts;

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-add-assign.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ void foo(int * , int *);
55

66
void add_assign_test(unsigned int n, int *a, int y) {
77
int *p = new int[10];
8-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> p"
8+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
99
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
1010
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
1111
p += 2;
1212
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:7}:"p = p.subspan("
1313
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:9-[[@LINE-2]]:9}:")"
1414

1515
int *r = p;
16-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> r"
16+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
1717
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
1818
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:13}:", <# placeholder #>}"
1919
while (*r != 0) {
@@ -48,7 +48,7 @@ void add_assign_test(unsigned int n, int *a, int y) {
4848

4949
int expr_test(unsigned x, int *q, int y) {
5050
char *p = new char[8];
51-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<char> p"
51+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:9}:"std::span<char> "
5252
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
5353
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 8}"
5454
p += (x + 1);

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-addressof-arraysubscript.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ void ignore_unsafe_calls(int x) {
5252
&p[x]);
5353

5454
int * q = new int[10];
55-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> q"
55+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
5656
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
5757
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
5858
unsafe_f((unsigned long) &q[5],
@@ -78,7 +78,7 @@ void index_is_zero() {
7878

7979
void pointer_subtraction(int x) {
8080
int * p = new int[10];
81-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::span<int> p"
81+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
8282
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
8383
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
8484

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-array-assign-to-ptr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void safe_array_assigned_to_unsafe_ptr(unsigned idx) {
1515
int buffer[10];
1616
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
1717
int* ptr;
18-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> ptr"
18+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:7}:"std::span<int>"
1919
ptr = buffer;
2020
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
2121
ptr[idx] = 0;

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-array-inits-ptr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ void safe_array_initing_unsafe_ptr(unsigned idx) {
1313
int buffer[123321123];
1414
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
1515
int* ptr = buffer;
16-
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:13}:"std::span<int> ptr"
16+
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:7}:"std::span<int>"
1717
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:{{.*}}123321123
1818
ptr[idx + 1] = 0;
1919
}

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-assign-to-array-subscr-on-ptr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// By testing presence of the declaration Fix-It we indirectly test presence of the trivial Fix-It for its operations.
1111
void test() {
1212
int *p = new int[10];
13-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> p"
13+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
1414
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
1515
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
1616
p[5] = 1;

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-deref-simple-ptr-arith.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
void basic() {
1212
int *ptr;
13-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span<int> ptr"
13+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
1414
*(ptr+5)=1;
1515
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:5}:""
1616
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:8-[[@LINE-2]]:9}:"["
@@ -20,7 +20,7 @@ void basic() {
2020
// The weird preceding semicolon ensures that we preserve that range intact.
2121
void char_ranges() {
2222
int *p;
23-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:9}:"std::span<int> p"
23+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
2424

2525
;* ( p + 5 ) = 1;
2626
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:4-[[@LINE-1]]:8}:""
@@ -108,7 +108,7 @@ void char_ranges() {
108108
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:14-[[@LINE-3]]:15}:"]"
109109

110110
int *ptrrrrrr;
111-
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span<int> ptrrrrrr"
111+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
112112

113113
;* ( ptrrrrrr + 123456 )= 1;
114114
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:4-[[@LINE-1]]:8}:""
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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+
int ptr(unsigned idx) {
6+
int * ptr = new int[1];
7+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
8+
int a;
9+
a = ptr[idx];
10+
return a;
11+
}
12+
13+
int ptr_to_const(unsigned idx) {
14+
const int * ptr = new int[1];
15+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const>"
16+
int a;
17+
a = ptr[idx];
18+
return a;
19+
}
20+
21+
int const_ptr(unsigned idx) {
22+
int * const ptr = new int[1];
23+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
24+
int a;
25+
a = ptr[idx];
26+
return a;
27+
}
28+
29+
int const_ptr_to_const(unsigned idx) {
30+
const int * const ptr = new int[1];
31+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const>"
32+
int a;
33+
a = ptr[idx];
34+
return a;
35+
}
36+
37+
int ptr_to_const_volatile(unsigned idx) {
38+
const volatile int * ptr = new int[1];
39+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:23}:"std::span<int const volatile>"
40+
int a;
41+
a = ptr[idx];
42+
return a;
43+
}
44+
45+
int const_volatile_ptr(unsigned idx) {
46+
int * const volatile ptr = new int[1];
47+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
48+
int a;
49+
a = ptr[idx];
50+
return a;
51+
}
52+
53+
int const_volatile_ptr_to_const_volatile(unsigned idx) {
54+
const volatile int * const volatile ptr = new int[1];
55+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:23}:"std::span<int const volatile>"
56+
int a;
57+
a = ptr[idx];
58+
return a;
59+
}
60+
61+
typedef const int * my_const_int_star;
62+
int typedef_ptr_to_const(unsigned idx) {
63+
my_const_int_star ptr = new int[1];
64+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
65+
int a;
66+
a = ptr[idx];
67+
return a;
68+
}
69+
70+
typedef int * const my_int_star_const;
71+
int typedef_const_ptr(unsigned idx) {
72+
my_int_star_const ptr = new int[1];
73+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
74+
int a;
75+
a = ptr[idx];
76+
return a;
77+
}
78+
79+
typedef const int * const my_const_int_star_const;
80+
int typedef_const_ptr_to_const(unsigned idx) {
81+
my_const_int_star_const ptr = new int[1];
82+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
83+
int a;
84+
a = ptr[idx];
85+
return a;
86+
}
87+
88+
int ptr_to_decltype(unsigned idx) {
89+
int a;
90+
decltype(a) * ptr = new int[1];
91+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span<decltype(a)>"
92+
a = ptr[idx];
93+
return a;
94+
}
95+
96+
int decltype_ptr(unsigned idx) {
97+
int * p;
98+
decltype(p) ptr = new int[1];
99+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
100+
int a;
101+
a = ptr[idx];
102+
return a;
103+
}

0 commit comments

Comments
 (0)