// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -fcxx-exceptions -fsafe-buffer-usage-suggestions -verify %s // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fcxx-exceptions -fdiagnostics-parseable-fixits -fsafe-buffer-usage-suggestions %s 2>&1 | FileCheck %s typedef int * TYPEDEF_PTR; #define MACRO_PTR int* // We CANNOT fix a pointer whose type is defined in a typedef or a // macro. Because if the typedef is changed after the fix, the fix // becomes incorrect and may not be noticed. // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]] void typedefPointer(TYPEDEF_PTR p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} if (++p) { // expected-note{{used in pointer arithmetic here}} } } // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE+1]] void macroPointer(MACRO_PTR p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} if (++p) { // expected-note{{used in pointer arithmetic here}} } } // The analysis requires accurate source location informations from // `TypeLoc`s of types of variable (parameter) declarations in order // to generate fix-its for them. But those information is not always // available (probably due to some bugs in clang but it is irrelevant // to the safe-buffer project). The following is an example. When // `_Atomic` is used, we cannot get valid source locations of the // pointee type of `unsigned *`. The analysis gives up in such a // case. // CHECK-NOT: fix-it: void typeLocSourceLocationInvalid(_Atomic unsigned *map) { // expected-warning{{'map' is an unsafe pointer used for buffer access}} map[5] = 5; // expected-note{{used in buffer access here}} } // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:33-[[@LINE+1]]:46}:"std::span map" void typeLocSourceLocationValid(unsigned *map) { // expected-warning{{'map' is an unsafe pointer used for buffer access}} \ expected-note{{change type of 'map' to 'std::span' to preserve bounds information}} map[5] = 5; // expected-note{{used in buffer access here}} } // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:2-[[@LINE-1]]:2}:"\n{{\[}}{{\[}}clang::unsafe_buffer_usage{{\]}}{{\]}} void typeLocSourceLocationValid(unsigned *map) {return typeLocSourceLocationValid(std::span(map, <# size #>));}\n" // We do not fix parameters participating unsafe operations for the // following functions/methods or function-like expressions: // CHECK-NOT: fix-it: class A { // constructor & descructor A(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} int tmp; tmp = p[5]; // expected-note{{used in buffer access here}} } // class member methods void foo(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} int tmp; tmp = p[5]; // expected-note{{used in buffer access here}} } // overload operator int operator+(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} int tmp; tmp = p[5]; // expected-note{{used in buffer access here}} return tmp; } }; // lambdas void foo() { auto Lamb = [&](int *p) // expected-warning{{'p' is an unsafe pointer used for buffer access}} -> int { int tmp; tmp = p[5]; // expected-note{{used in buffer access here}} return tmp; }; } // template template void template_foo(T * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} T tmp; tmp = p[5]; // expected-note{{used in buffer access here}} } void instantiate_template_foo() { int * p; template_foo(p); // FIXME expected note {{in instantiation of function template specialization 'template_foo' requested here}} } // variadic function void vararg_foo(int * p...) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} int tmp; tmp = p[5]; // expected-note{{used in buffer access here}} } // constexpr functions constexpr int constexpr_foo(int * p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} return p[5]; // expected-note{{used in buffer access here}} } // function body is a try-block void fn_with_try_block(int* p) // expected-warning{{'p' is an unsafe pointer used for buffer access}} try { int tmp; if (p == nullptr) throw 42; tmp = p[5]; // expected-note{{used in buffer access here}} } catch (int) { *p = 0; } // The following two unsupported cases are not specific to // parm-fixits. Adding them here in case they get forgotten. void isArrayDecayToPointerUPC(int a[][10], int (*b)[10]) { // expected-warning@-1{{'a' is an unsafe pointer used for buffer access}} // expected-warning@-2{{'b' is an unsafe pointer used for buffer access}} int tmp; tmp = a[5][5] + b[5][5]; // expected-warning2{{unsafe buffer access}} expected-note2{{used in buffer access here}} } // parameter having default values: void parmWithDefaultValue(int * x = 0) { // expected-warning@-1{{'x' is an unsafe pointer used for buffer access}} int tmp; tmp = x[5]; // expected-note{{used in buffer access here}} } void parmWithDefaultValueDecl(int * x = 0); void parmWithDefaultValueDecl(int * x) { // expected-warning@-1{{'x' is an unsafe pointer used for buffer access}} int tmp; tmp = x[5]; // expected-note{{used in buffer access here}} } #define MACRO_NAME MyName // The fix-it ends with a macro. It will be discarded due to overlap with macros. // CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]] void macroIdentifier(int * MACRO_NAME) { // expected-warning{{'MyName' is an unsafe pointer used for buffer access}} if (++MyName){} // expected-note{{used in pointer arithmetic here}} } // CHECK-NOT: fix-it:{{.*}}:{[[@LINE+1]] void parmHasNoName(int *p, int *) { // cannot fix the function because there is one parameter has no name. \ expected-warning{{'p' is an unsafe pointer used for buffer access}} p[5] = 5; // expected-note{{used in buffer access here}} }