189a1d03eSRichard // RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t \
289a1d03eSRichard // RUN: -config='{CheckOptions: \
3*e8a3ddafSNathan James // RUN:  {cppcoreguidelines-owning-memory.LegacyResourceProducers: "::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile", \
4*e8a3ddafSNathan James // RUN:   cppcoreguidelines-owning-memory.LegacyResourceConsumers: "::free;::realloc;::freopen;::fclose"}}' \
589a1d03eSRichard // RUN: -- -nostdlib -nostdinc++
689a1d03eSRichard 
789a1d03eSRichard namespace gsl {
889a1d03eSRichard template <class T>
989a1d03eSRichard using owner = T;
1089a1d03eSRichard } // namespace gsl
1189a1d03eSRichard 
1289a1d03eSRichard extern "C" {
1389a1d03eSRichard using size_t = decltype(sizeof(void*));
1489a1d03eSRichard using FILE = int;
1589a1d03eSRichard 
1689a1d03eSRichard void *malloc(size_t ByteCount);
1789a1d03eSRichard void *aligned_alloc(size_t Alignment, size_t Size);
1889a1d03eSRichard void *calloc(size_t Count, size_t SizeSingle);
1989a1d03eSRichard void *realloc(void *Resource, size_t NewByteCount);
2089a1d03eSRichard void free(void *Resource);
2189a1d03eSRichard 
2289a1d03eSRichard FILE *tmpfile(void);
2389a1d03eSRichard FILE *fopen(const char *filename, const char *mode);
2489a1d03eSRichard FILE *freopen(const char *filename, const char *mode, FILE *stream);
2589a1d03eSRichard void fclose(FILE *Resource);
2689a1d03eSRichard }
2789a1d03eSRichard 
2889a1d03eSRichard namespace std {
2989a1d03eSRichard using ::FILE;
3089a1d03eSRichard using ::size_t;
3189a1d03eSRichard 
3289a1d03eSRichard using ::fclose;
3389a1d03eSRichard using ::fopen;
3489a1d03eSRichard using ::freopen;
3589a1d03eSRichard using ::tmpfile;
3689a1d03eSRichard 
3789a1d03eSRichard using ::aligned_alloc;
3889a1d03eSRichard using ::calloc;
3989a1d03eSRichard using ::free;
4089a1d03eSRichard using ::malloc;
4189a1d03eSRichard using ::realloc;
4289a1d03eSRichard } // namespace std
4389a1d03eSRichard 
nonOwningCall(int * Resource,size_t Size)4489a1d03eSRichard void nonOwningCall(int *Resource, size_t Size) {}
nonOwningCall(FILE * Resource)4589a1d03eSRichard void nonOwningCall(FILE *Resource) {}
4689a1d03eSRichard 
consumesResource(gsl::owner<int * > Resource,size_t Size)4789a1d03eSRichard void consumesResource(gsl::owner<int *> Resource, size_t Size) {}
consumesResource(gsl::owner<FILE * > Resource)4889a1d03eSRichard void consumesResource(gsl::owner<FILE *> Resource) {}
4989a1d03eSRichard 
testNonCasted(void * Resource)5089a1d03eSRichard void testNonCasted(void *Resource) {}
5189a1d03eSRichard 
testNonCastedOwner(gsl::owner<void * > Resource)5289a1d03eSRichard void testNonCastedOwner(gsl::owner<void *> Resource) {}
5389a1d03eSRichard 
fileFactory1()5489a1d03eSRichard FILE *fileFactory1() { return ::fopen("new_file.txt", "w"); }
5589a1d03eSRichard // 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<>'
fileFactory2()5689a1d03eSRichard gsl::owner<FILE *> fileFactory2() { return std::fopen("new_file.txt", "w"); } // Ok
5789a1d03eSRichard 
arrayFactory1()5889a1d03eSRichard int *arrayFactory1() { return (int *)std::malloc(100); }
5989a1d03eSRichard // 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<>'
arrayFactory2()6089a1d03eSRichard gsl::owner<int *> arrayFactory2() { return (int *)std::malloc(100); } // Ok
dataFactory1()6189a1d03eSRichard void *dataFactory1() { return std::malloc(100); }
6289a1d03eSRichard // 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<>'
dataFactory2()6389a1d03eSRichard gsl::owner<void *> dataFactory2() { return std::malloc(100); } // Ok
6489a1d03eSRichard 
test_resource_creators()6589a1d03eSRichard void test_resource_creators() {
6689a1d03eSRichard   const unsigned int ByteCount = 25 * sizeof(int);
6789a1d03eSRichard   int Bad = 42;
6889a1d03eSRichard 
6989a1d03eSRichard   int *IntArray1 = (int *)std::malloc(ByteCount);
7089a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
7189a1d03eSRichard   int *IntArray2 = static_cast<int *>(std::malloc(ByteCount)); // Bad
7289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
7389a1d03eSRichard   void *IntArray3 = std::malloc(ByteCount);
7489a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>'
7589a1d03eSRichard 
7689a1d03eSRichard   int *IntArray4 = (int *)::malloc(ByteCount);
7789a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
7889a1d03eSRichard   int *IntArray5 = static_cast<int *>(::malloc(ByteCount)); // Bad
7989a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
8089a1d03eSRichard   void *IntArray6 = ::malloc(ByteCount);
8189a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>'
8289a1d03eSRichard 
8389a1d03eSRichard   gsl::owner<int *> IntArray7 = (int *)malloc(ByteCount); // Ok
8489a1d03eSRichard   gsl::owner<void *> IntArray8 = malloc(ByteCount);       // Ok
8589a1d03eSRichard 
8689a1d03eSRichard   gsl::owner<int *> IntArray9 = &Bad;
8789a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
8889a1d03eSRichard 
8989a1d03eSRichard   nonOwningCall((int *)malloc(ByteCount), 25);
9089a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
9189a1d03eSRichard   nonOwningCall((int *)::malloc(ByteCount), 25);
9289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
9389a1d03eSRichard 
9489a1d03eSRichard   consumesResource((int *)malloc(ByteCount), 25);   // Ok
9589a1d03eSRichard   consumesResource((int *)::malloc(ByteCount), 25); // Ok
9689a1d03eSRichard 
9789a1d03eSRichard   testNonCasted(malloc(ByteCount));
9889a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>'
9989a1d03eSRichard   testNonCastedOwner(gsl::owner<void *>(malloc(ByteCount))); // Ok
10089a1d03eSRichard   testNonCastedOwner(malloc(ByteCount));                     // Ok
10189a1d03eSRichard 
10289a1d03eSRichard   FILE *File1 = std::fopen("test_name.txt", "w+");
10389a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
10489a1d03eSRichard   FILE *File2 = ::fopen("test_name.txt", "w+");
10589a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
10689a1d03eSRichard 
10789a1d03eSRichard   gsl::owner<FILE *> File3 = ::fopen("test_name.txt", "w+"); // Ok
10889a1d03eSRichard 
10989a1d03eSRichard   FILE *File4;
11089a1d03eSRichard   File4 = ::fopen("test_name.txt", "w+");
11189a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'FILE *' (aka 'int *')
11289a1d03eSRichard 
11389a1d03eSRichard   gsl::owner<FILE *> File5;
11489a1d03eSRichard   File5 = ::fopen("test_name.txt", "w+"); // Ok
11589a1d03eSRichard   File5 = File1;
11689a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'FILE *' (aka 'int *')
11789a1d03eSRichard 
11889a1d03eSRichard   gsl::owner<FILE *> File6 = File1;
11989a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'FILE *' (aka 'int *')
12089a1d03eSRichard 
12189a1d03eSRichard   FILE *File7 = tmpfile();
12289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
12389a1d03eSRichard   gsl::owner<FILE *> File8 = tmpfile(); // Ok
12489a1d03eSRichard 
12589a1d03eSRichard   nonOwningCall(::fopen("test_name.txt", "r"));
12689a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
12789a1d03eSRichard   nonOwningCall(std::fopen("test_name.txt", "r"));
12889a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
12989a1d03eSRichard 
13089a1d03eSRichard   consumesResource(::fopen("test_name.txt", "r")); // Ok
13189a1d03eSRichard 
13289a1d03eSRichard   int *HeapPointer3 = (int *)aligned_alloc(16ul, 4ul * 32ul);
13389a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
13489a1d03eSRichard   gsl::owner<int *> HeapPointer4 = static_cast<int *>(aligned_alloc(16ul, 4ul * 32ul)); // Ok
13589a1d03eSRichard 
13689a1d03eSRichard   void *HeapPointer5 = calloc(10ul, 4ul);
13789a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>'
13889a1d03eSRichard   gsl::owner<void *> HeapPointer6 = calloc(10ul, 4ul); // Ok
13989a1d03eSRichard }
14089a1d03eSRichard 
test_legacy_consumers()14189a1d03eSRichard void test_legacy_consumers() {
14289a1d03eSRichard   int StackInteger = 42;
14389a1d03eSRichard 
14489a1d03eSRichard   int *StackPointer = &StackInteger;
14589a1d03eSRichard   int *HeapPointer1 = (int *)malloc(100);
14689a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
14789a1d03eSRichard   gsl::owner<int *> HeapPointer2 = (int *)malloc(100);
14889a1d03eSRichard 
14989a1d03eSRichard   std::free(StackPointer);
15089a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
15189a1d03eSRichard   std::free(HeapPointer1);
15289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
15389a1d03eSRichard   std::free(HeapPointer2); // Ok
15489a1d03eSRichard   // CHECK MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
15589a1d03eSRichard 
15689a1d03eSRichard   // FIXME: the check complains about initialization of 'void *' with new created owner.
15789a1d03eSRichard   // This happens, because the argument of `free` is not marked as 'owner<>' (and cannot be),
15889a1d03eSRichard   // and the check will not figure out could be meant as owner.
15989a1d03eSRichard   // This property will probably never be fixed, because it is probably a rather rare
16089a1d03eSRichard   // use-case and 'owner<>' should be wrapped in RAII classes anyway!
16189a1d03eSRichard   std::free(std::malloc(100)); // Ok but silly :)
16289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>'
16389a1d03eSRichard 
16489a1d03eSRichard   // Demonstrate, that multi-argument functions are diagnosed as well.
16589a1d03eSRichard   std::realloc(StackPointer, 200);
16689a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
16789a1d03eSRichard   std::realloc(HeapPointer1, 200);
16889a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
16989a1d03eSRichard   std::realloc(HeapPointer2, 200);     // Ok
17089a1d03eSRichard   std::realloc(std::malloc(100), 200); // Ok but silly
17189a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:16: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>'
17289a1d03eSRichard 
17389a1d03eSRichard   fclose(fileFactory1());
17489a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>'
17589a1d03eSRichard   fclose(fileFactory2()); // Ok, same as FIXME with `free(malloc(100))` applies here
17689a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
17789a1d03eSRichard 
17889a1d03eSRichard   gsl::owner<FILE *> File1 = fopen("testfile.txt", "r"); // Ok
17989a1d03eSRichard   FILE *File2 = freopen("testfile.txt", "w", File1);
18089a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
18189a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>'
18289a1d03eSRichard   // FIXME: The warning for not passing and owner<> is a false positive since both the filename and the
18389a1d03eSRichard   // mode are not supposed to be owners but still pointers. The check is to coarse for
18489a1d03eSRichard   // this function. Maybe `freopen` gets special treatment.
18589a1d03eSRichard 
18689a1d03eSRichard   gsl::owner<FILE *> File3 = freopen("testfile.txt", "w", File2); // Bad, File2 no owner
18789a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:30: warning: calling legacy resource function without passing a 'gsl::owner<>'
18889a1d03eSRichard 
18989a1d03eSRichard   FILE *TmpFile = tmpfile();
19089a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
19189a1d03eSRichard   FILE *File6 = freopen("testfile.txt", "w", TmpFile); // Bad, both return and argument
19289a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>'
19389a1d03eSRichard   // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>'
19489a1d03eSRichard }
195