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 | |