| 1 | // RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t \ |
| 2 | // RUN: -config='{CheckOptions: \ |
| 3 | // RUN: {cppcoreguidelines-owning-memory.LegacyResourceProducers: "::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile", \ |
| 4 | // RUN: cppcoreguidelines-owning-memory.LegacyResourceConsumers: "::free;::realloc;::freopen;::fclose"}}' \ |
| 5 | // RUN: -- -nostdlib -nostdinc++ |
| 6 | |
| 7 | namespace gsl { |
| 8 | template <class T> |
| 9 | using owner = T; |
| 10 | } // namespace gsl |
| 11 | |
| 12 | extern "C" { |
| 13 | using size_t = decltype(sizeof(void*)); |
| 14 | using FILE = int; |
| 15 | |
| 16 | void *malloc(size_t ByteCount); |
| 17 | void *aligned_alloc(size_t Alignment, size_t Size); |
| 18 | void *calloc(size_t Count, size_t SizeSingle); |
| 19 | void *realloc(void *Resource, size_t NewByteCount); |
| 20 | void free(void *Resource); |
| 21 | |
| 22 | FILE *tmpfile(void); |
| 23 | FILE *fopen(const char *filename, const char *mode); |
| 24 | FILE *freopen(const char *filename, const char *mode, FILE *stream); |
| 25 | void fclose(FILE *Resource); |
| 26 | } |
| 27 | |
| 28 | namespace std { |
| 29 | using ::FILE; |
| 30 | using ::size_t; |
| 31 | |
| 32 | using ::fclose; |
| 33 | using ::fopen; |
| 34 | using ::freopen; |
| 35 | using ::tmpfile; |
| 36 | |
| 37 | using ::aligned_alloc; |
| 38 | using ::calloc; |
| 39 | using ::free; |
| 40 | using ::malloc; |
| 41 | using ::realloc; |
| 42 | } // namespace std |
| 43 | |
| 44 | void nonOwningCall(int *Resource, size_t Size) {} |
| 45 | void nonOwningCall(FILE *Resource) {} |
| 46 | |
| 47 | void consumesResource(gsl::owner<int *> Resource, size_t Size) {} |
| 48 | void consumesResource(gsl::owner<FILE *> Resource) {} |
| 49 | |
| 50 | void testNonCasted(void *Resource) {} |
| 51 | |
| 52 | void testNonCastedOwner(gsl::owner<void *> Resource) {} |
| 53 | |
| 54 | FILE *fileFactory1() { return ::fopen(filename: "new_file.txt" , mode: "w" ); } |
| 55 | // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'FILE *' (aka 'int *') or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' |
| 56 | gsl::owner<FILE *> fileFactory2() { return std::fopen(filename: "new_file.txt" , mode: "w" ); } // Ok |
| 57 | |
| 58 | int *arrayFactory1() { return (int *)std::malloc(ByteCount: 100); } |
| 59 | // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' |
| 60 | gsl::owner<int *> arrayFactory2() { return (int *)std::malloc(ByteCount: 100); } // Ok |
| 61 | void *dataFactory1() { return std::malloc(ByteCount: 100); } |
| 62 | // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'void *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' |
| 63 | gsl::owner<void *> dataFactory2() { return std::malloc(ByteCount: 100); } // Ok |
| 64 | |
| 65 | void test_resource_creators() { |
| 66 | const unsigned int ByteCount = 25 * sizeof(int); |
| 67 | int Bad = 42; |
| 68 | |
| 69 | int *IntArray1 = (int *)std::malloc(ByteCount); |
| 70 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 71 | int *IntArray2 = static_cast<int *>(std::malloc(ByteCount)); // Bad |
| 72 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 73 | void *IntArray3 = std::malloc(ByteCount); |
| 74 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' |
| 75 | |
| 76 | int *IntArray4 = (int *)::malloc(ByteCount); |
| 77 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 78 | int *IntArray5 = static_cast<int *>(::malloc(ByteCount)); // Bad |
| 79 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 80 | void *IntArray6 = ::malloc(ByteCount); |
| 81 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' |
| 82 | |
| 83 | gsl::owner<int *> IntArray7 = (int *)malloc(ByteCount); // Ok |
| 84 | gsl::owner<void *> IntArray8 = malloc(ByteCount); // Ok |
| 85 | |
| 86 | gsl::owner<int *> IntArray9 = &Bad; |
| 87 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' |
| 88 | |
| 89 | nonOwningCall(Resource: (int *)malloc(ByteCount), Size: 25); |
| 90 | // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' |
| 91 | nonOwningCall(Resource: (int *)::malloc(ByteCount), Size: 25); |
| 92 | // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' |
| 93 | |
| 94 | consumesResource(Resource: (int *)malloc(ByteCount), Size: 25); // Ok |
| 95 | consumesResource(Resource: (int *)::malloc(ByteCount), Size: 25); // Ok |
| 96 | |
| 97 | testNonCasted(Resource: malloc(ByteCount)); |
| 98 | // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' |
| 99 | testNonCastedOwner(Resource: gsl::owner<void *>(malloc(ByteCount))); // Ok |
| 100 | testNonCastedOwner(Resource: malloc(ByteCount)); // Ok |
| 101 | |
| 102 | FILE *File1 = std::fopen(filename: "test_name.txt" , mode: "w+" ); |
| 103 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 104 | FILE *File2 = ::fopen(filename: "test_name.txt" , mode: "w+" ); |
| 105 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 106 | |
| 107 | gsl::owner<FILE *> File3 = ::fopen(filename: "test_name.txt" , mode: "w+" ); // Ok |
| 108 | |
| 109 | FILE *File4; |
| 110 | File4 = ::fopen(filename: "test_name.txt" , mode: "w+" ); |
| 111 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'FILE *' (aka 'int *') |
| 112 | |
| 113 | gsl::owner<FILE *> File5; |
| 114 | File5 = ::fopen(filename: "test_name.txt" , mode: "w+" ); // Ok |
| 115 | File5 = File1; |
| 116 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'FILE *' (aka 'int *') |
| 117 | |
| 118 | gsl::owner<FILE *> File6 = File1; |
| 119 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'FILE *' (aka 'int *') |
| 120 | |
| 121 | FILE *File7 = tmpfile(); |
| 122 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 123 | gsl::owner<FILE *> File8 = tmpfile(); // Ok |
| 124 | |
| 125 | nonOwningCall(Resource: ::fopen(filename: "test_name.txt" , mode: "r" )); |
| 126 | // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 127 | nonOwningCall(Resource: std::fopen(filename: "test_name.txt" , mode: "r" )); |
| 128 | // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 129 | |
| 130 | consumesResource(Resource: ::fopen(filename: "test_name.txt" , mode: "r" )); // Ok |
| 131 | |
| 132 | int *HeapPointer3 = (int *)aligned_alloc(Alignment: 16ul, Size: 4ul * 32ul); |
| 133 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 134 | gsl::owner<int *> HeapPointer4 = static_cast<int *>(aligned_alloc(Alignment: 16ul, Size: 4ul * 32ul)); // Ok |
| 135 | |
| 136 | void *HeapPointer5 = calloc(Count: 10ul, SizeSingle: 4ul); |
| 137 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' |
| 138 | gsl::owner<void *> HeapPointer6 = calloc(Count: 10ul, SizeSingle: 4ul); // Ok |
| 139 | } |
| 140 | |
| 141 | void test_legacy_consumers() { |
| 142 | int StackInteger = 42; |
| 143 | |
| 144 | int *StackPointer = &StackInteger; |
| 145 | int *HeapPointer1 = (int *)malloc(ByteCount: 100); |
| 146 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' |
| 147 | gsl::owner<int *> HeapPointer2 = (int *)malloc(ByteCount: 100); |
| 148 | |
| 149 | std::free(Resource: StackPointer); |
| 150 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 151 | std::free(Resource: HeapPointer1); |
| 152 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 153 | std::free(Resource: HeapPointer2); // Ok |
| 154 | // CHECK MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 155 | |
| 156 | // FIXME: the check complains about initialization of 'void *' with new created owner. |
| 157 | // This happens, because the argument of `free` is not marked as 'owner<>' (and cannot be), |
| 158 | // and the check will not figure out could be meant as owner. |
| 159 | // This property will probably never be fixed, because it is probably a rather rare |
| 160 | // use-case and 'owner<>' should be wrapped in RAII classes anyway! |
| 161 | std::free(Resource: std::malloc(ByteCount: 100)); // Ok but silly :) |
| 162 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' |
| 163 | |
| 164 | // Demonstrate, that multi-argument functions are diagnosed as well. |
| 165 | std::realloc(Resource: StackPointer, NewByteCount: 200); |
| 166 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 167 | std::realloc(Resource: HeapPointer1, NewByteCount: 200); |
| 168 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 169 | std::realloc(Resource: HeapPointer2, NewByteCount: 200); // Ok |
| 170 | std::realloc(Resource: std::malloc(ByteCount: 100), NewByteCount: 200); // Ok but silly |
| 171 | // CHECK-MESSAGES: [[@LINE-1]]:16: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' |
| 172 | |
| 173 | fclose(Resource: fileFactory1()); |
| 174 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 175 | fclose(Resource: fileFactory2()); // Ok, same as FIXME with `free(malloc(100))` applies here |
| 176 | // CHECK-MESSAGES: [[@LINE-1]]:10: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 177 | |
| 178 | gsl::owner<FILE *> File1 = fopen(filename: "testfile.txt" , mode: "r" ); // Ok |
| 179 | FILE *File2 = freopen(filename: "testfile.txt" , mode: "w" , stream: File1); |
| 180 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 181 | // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 182 | // FIXME: The warning for not passing and owner<> is a false positive since both the filename and the |
| 183 | // mode are not supposed to be owners but still pointers. The check is to coarse for |
| 184 | // this function. Maybe `freopen` gets special treatment. |
| 185 | |
| 186 | gsl::owner<FILE *> File3 = freopen(filename: "testfile.txt" , mode: "w" , stream: File2); // Bad, File2 no owner |
| 187 | // CHECK-MESSAGES: [[@LINE-1]]:30: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 188 | |
| 189 | FILE *TmpFile = tmpfile(); |
| 190 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 191 | FILE *File6 = freopen(filename: "testfile.txt" , mode: "w" , stream: TmpFile); // Bad, both return and argument |
| 192 | // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' |
| 193 | // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>' |
| 194 | } |
| 195 | |