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
7namespace gsl {
8template <class T>
9using owner = T;
10} // namespace gsl
11
12extern "C" {
13using size_t = decltype(sizeof(void*));
14using FILE = int;
15
16void *malloc(size_t ByteCount);
17void *aligned_alloc(size_t Alignment, size_t Size);
18void *calloc(size_t Count, size_t SizeSingle);
19void *realloc(void *Resource, size_t NewByteCount);
20void free(void *Resource);
21
22FILE *tmpfile(void);
23FILE *fopen(const char *filename, const char *mode);
24FILE *freopen(const char *filename, const char *mode, FILE *stream);
25void fclose(FILE *Resource);
26}
27
28namespace std {
29using ::FILE;
30using ::size_t;
31
32using ::fclose;
33using ::fopen;
34using ::freopen;
35using ::tmpfile;
36
37using ::aligned_alloc;
38using ::calloc;
39using ::free;
40using ::malloc;
41using ::realloc;
42} // namespace std
43
44void nonOwningCall(int *Resource, size_t Size) {}
45void nonOwningCall(FILE *Resource) {}
46
47void consumesResource(gsl::owner<int *> Resource, size_t Size) {}
48void consumesResource(gsl::owner<FILE *> Resource) {}
49
50void testNonCasted(void *Resource) {}
51
52void testNonCastedOwner(gsl::owner<void *> Resource) {}
53
54FILE *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<>'
56gsl::owner<FILE *> fileFactory2() { return std::fopen(filename: "new_file.txt", mode: "w"); } // Ok
57
58int *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<>'
60gsl::owner<int *> arrayFactory2() { return (int *)std::malloc(ByteCount: 100); } // Ok
61void *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<>'
63gsl::owner<void *> dataFactory2() { return std::malloc(ByteCount: 100); } // Ok
64
65void 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
141void 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

source code of clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory-legacy-functions.cpp