506 lines · cpp
1// RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t2 3namespace gsl {4template <class T>5using owner = T;6} // namespace gsl7 8template <typename T>9class unique_ptr {10public:11 unique_ptr(gsl::owner<T> resource) : memory(resource) {}12 unique_ptr(const unique_ptr<T> &) = default;13 14 ~unique_ptr() { delete memory; }15 16private:17 gsl::owner<T> memory;18};19 20void takes_owner(gsl::owner<int *> owned_int) {21}22 23void takes_pointer(int *unowned_int) {24}25 26void takes_owner_and_more(int some_int, gsl::owner<int *> owned_int, float f) {27}28 29template <typename T>30void takes_templated_owner(gsl::owner<T> owned_T) {31}32 33gsl::owner<int *> returns_owner1() { return gsl::owner<int *>(new int(42)); } // Ok34gsl::owner<int *> returns_owner2() { return new int(42); } // Ok35 36int *returns_no_owner1() { return nullptr; }37int *returns_no_owner2() {38 return new int(42);39 // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'40}41int *returns_no_owner3() {42 int *should_be_owner = new int(42);43 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'44 return should_be_owner;45}46int *returns_no_owner4() {47 gsl::owner<int *> owner = new int(42);48 return owner;49 // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'50}51 52unique_ptr<int *> returns_no_owner5() {53 return unique_ptr<int *>(new int(42)); // Ok54}55 56/// FIXME: CSA finds it, but the report is misleading. Ownersemantics can catch this57/// by flow analysis similar to bugprone-use-after-move.58void csa_not_finding_leak() {59 gsl::owner<int *> o1 = new int(42); // Ok60 61 gsl::owner<int *> o2 = o1; // Ok62 o2 = new int(45); // conceptual leak, the memory from o1 is now leaked, since its considered moved in the guidelines63 64 delete o2;65 // actual leak occurs here, its found, but mixed66 delete o1;67}68 69void test_assignment_and_initialization() {70 int stack_int1 = 15;71 int stack_int2;72 73 gsl::owner<int *> owned_int1 = &stack_int1; // BAD74 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'75 76 gsl::owner<int *> owned_int2;77 owned_int2 = &stack_int2; // BAD since no owner, bad since uninitialized78 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'79 80 gsl::owner<int *> owned_int3 = new int(42); // Good81 owned_int3 = nullptr; // Good82 83 gsl::owner<int *> owned_int4(nullptr); // Ok84 owned_int4 = new int(42); // Good85 86 gsl::owner<int *> owned_int5 = owned_int3; // Good87 88 gsl::owner<int *> owned_int6{nullptr}; // Ok89 owned_int6 = owned_int4; // Good90 91 // FIXME:, flow analysis for the case of reassignment. Value must be released before92 owned_int6 = owned_int3; // BAD, because reassignment without resource release93 94 auto owned_int7 = returns_owner1(); // Ok, since type deduction does not eliminate the owner wrapper95 96 const auto owned_int8 = returns_owner2(); // Ok, since type deduction does not eliminate the owner wrapper97 98 gsl::owner<int *> owned_int9 = returns_owner1(); // Ok99 int *unowned_int3 = returns_owner1(); // Bad100 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'101 102 gsl::owner<int *> owned_int10;103 owned_int10 = returns_owner1(); // Ok104 105 int *unowned_int4;106 unowned_int4 = returns_owner1(); // Bad107 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'108 109 gsl::owner<int *> owned_int11 = returns_no_owner1(); // Bad since no owner110 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'111 112 gsl::owner<int *> owned_int12;113 owned_int12 = returns_no_owner1(); // Bad since no owner114 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'115 116 int *unowned_int5 = returns_no_owner1(); // Ok117 int *unowned_int6;118 unowned_int6 = returns_no_owner1(); // Ok119 120 int *unowned_int7 = new int(42); // Bad, since resource not assigned to an owner121 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'122 123 int *unowned_int8;124 unowned_int8 = new int(42);125 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'126 127 gsl::owner<int *> owned_int13 = nullptr; // Ok128}129 130void test_deletion() {131 gsl::owner<int *> owned_int1 = new int(42);132 delete owned_int1; // Good133 134 gsl::owner<int *> owned_int2 = new int[42];135 delete[] owned_int2; // Good136 137 int *unowned_int1 = new int(42); // BAD, since new creates and owner138 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'139 delete unowned_int1; // BAD, since no owner140 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead141 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here142 143 int *unowned_int2 = new int[42]; // BAD, since new creates and owner144 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'145 delete[] unowned_int2; // BAD since no owner146 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead147 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here148 149 delete new int(42); // Technically ok, but stupid150 delete[] new int[42]; // Technically ok, but stupid151}152 153void test_owner_function_calls() {154 int stack_int = 42;155 int *unowned_int1 = &stack_int;156 takes_owner(&stack_int); // BAD157 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'158 takes_owner(unowned_int1); // BAD159 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'160 161 gsl::owner<int *> owned_int1 = new int(42);162 takes_owner(owned_int1); // Ok163 164 takes_owner_and_more(42, &stack_int, 42.0f); // BAD165 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'166 takes_owner_and_more(42, unowned_int1, 42.0f); // BAD167 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'168 169 takes_owner_and_more(42, new int(42), 42.0f); // Ok, since new is consumed by owner170 takes_owner_and_more(42, owned_int1, 42.0f); // Ok, since owner as argument171 172 takes_templated_owner(owned_int1); // Ok173 takes_templated_owner(new int(42)); // Ok174 takes_templated_owner(unowned_int1); // Bad175 // CHECK-NOTES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *'176 177 takes_owner(returns_owner1()); // Ok178 takes_owner(returns_no_owner1()); // BAD179 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'180}181 182void test_unowned_function_calls() {183 int stack_int = 42;184 int *unowned_int1 = &stack_int;185 gsl::owner<int *> owned_int1 = new int(42);186 187 takes_pointer(&stack_int); // Ok188 takes_pointer(unowned_int1); // Ok189 takes_pointer(owned_int1); // Ok190 takes_pointer(new int(42)); // Bad, since new creates and owner191 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'192 193 takes_pointer(returns_owner1()); // Bad194 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'195 196 takes_pointer(returns_no_owner1()); // Ok197}198 199// FIXME: Typedefing owner<> to something else does not work.200// This might be necessary for code already having a similar typedef like owner<> and201// replacing it with owner<>. This might be the same problem as with templates.202// The canonical type will ignore the owner<> alias, since its a typedef as well.203//204// Check, if owners hidden by typedef are handled the same as 'obvious' owners.205#if 0206using heap_int = gsl::owner<int *>;207typedef gsl::owner<float *> heap_float;208 209// This tests only a subset, assuming that the check will either see through the210// typedef or not (it doesn't!).211void test_typedefed_values() {212 // Modern typedef.213 int StackInt1 = 42;214 heap_int HeapInt1 = &StackInt1;215 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'216 217 //FIXME: Typedef not considered correctly here.218 // heap_int HeapInt2 = new int(42); // Ok219 takes_pointer(HeapInt1); // Ok220 takes_owner(HeapInt1); // Ok221 222 // Traditional typedef.223 float StackFloat1 = 42.0f;224 heap_float HeapFloat1 = &StackFloat1;225 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'float *'226 227 //FIXME: Typedef not considered correctly here.228 // heap_float HeapFloat2 = new float(42.0f);229 HeapFloat2 = HeapFloat1; // Ok230}231#endif232 233struct ArbitraryClass {};234struct ClassWithOwner { // Does not define destructor, necessary with owner235 ClassWithOwner() : owner_var(nullptr) {} // Ok236 237 ClassWithOwner(ArbitraryClass &other) : owner_var(&other) {}238 // CHECK-NOTES: [[@LINE-1]]:43: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'ArbitraryClass *'239 240 ClassWithOwner(gsl::owner<ArbitraryClass *> other) : owner_var(other) {} // Ok241 242 ClassWithOwner(gsl::owner<ArbitraryClass *> data, int /* unused */) { // Ok243 owner_var = data; // Ok244 }245 246 ClassWithOwner(ArbitraryClass *bad_data, int /* unused */, int /* unused */) {247 owner_var = bad_data;248 // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'249 }250 251 ClassWithOwner(ClassWithOwner &&other) : owner_var{other.owner_var} {} // Ok252 253 ClassWithOwner &operator=(ClassWithOwner &&other) {254 owner_var = other.owner_var; // Ok255 return *this;256 }257 258 // Returning means, that the owner is "moved", so the class should not access this259 // variable anymore after this method gets called.260 gsl::owner<ArbitraryClass *> buggy_but_returns_owner() { return owner_var; }261 262 gsl::owner<ArbitraryClass *> owner_var;263 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'ClassWithOwner' to implement a destructor to release the owned resource264};265 266class DefaultedDestructor { // Bad since default constructor with owner267 ~DefaultedDestructor() = default; // Bad, since will not destroy the owner268 gsl::owner<int *> Owner;269 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DefaultedDestructor' to implement a destructor to release the owned resource270};271 272struct DeletedDestructor {273 ~DeletedDestructor() = delete;274 gsl::owner<int *> Owner;275 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DeletedDestructor' to implement a destructor to release the owned resource276};277 278void test_class_with_owner() {279 ArbitraryClass A;280 ClassWithOwner C1; // Ok281 ClassWithOwner C2{A}; // Bad, since the owner would be initialized with an non-owner, but catched in the class282 ClassWithOwner C3{gsl::owner<ArbitraryClass *>(new ArbitraryClass)}; // Ok283 284 const auto Owner1 = C3.buggy_but_returns_owner(); // Ok, deduces Owner1 to owner<ArbitraryClass *> const285 286 auto Owner2 = C2.buggy_but_returns_owner(); // Ok, deduces Owner2 to owner<ArbitraryClass *>287 288 Owner2 = &A; // BAD, since type deduction resulted in owner<ArbitraryClass *>289 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'290 291 gsl::owner<ArbitraryClass *> Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner292 Owner3 = &A; // Bad, since assignment of non-owner to owner293 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'294}295 296template <typename T>297struct HeapArray { // Ok, since destructor with owner298 HeapArray() : _data(nullptr), size(0) {} // Ok299 HeapArray(int size) : _data(new int[size]), size(size) {} // Ok300 HeapArray(int size, T val) {301 _data = new int[size]; // Ok302 size = size;303 for (auto i = 0u; i < size; ++i)304 _data[i] = val; // Ok305 }306 HeapArray(int size, T val, int *problematic) : _data{problematic}, size(size) {} // Bad307 // CHECK-NOTES: [[@LINE-1]]:50: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'void'308 // FIXME: void is incorrect type, probably wrong thing matched309 310 HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok311 other._data = nullptr; // Ok312 // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t'313 // FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types.314 other.size = 0;315 }316 317 HeapArray<T> &operator=(HeapArray<T> &&other) {318 _data = other._data; // Ok, NOLINT warning here about bad types, why?319 size = other.size;320 return *this;321 }322 323 ~HeapArray() { delete[] _data; } // Ok324 325 T *data() { return _data; } // Ok NOLINT, because it "looks" like a factory326 327 gsl::owner<T *> _data;328 unsigned int size;329};330 331void test_inner_template() {332 HeapArray<int> Array1;333 HeapArray<int> Array2(100);334 HeapArray<int> Array3(100, 0);335 HeapArray<int> Array4(100, 0, nullptr);336 337 Array1 = static_cast<HeapArray<int> &&>(Array2);338 HeapArray<int> Array5(static_cast<HeapArray<int> &&>(Array3));339 340 int *NonOwningPtr = Array1.data(); // Ok341 gsl::owner<int *> OwningPtr = Array1.data(); // Bad, since it does not return the owner342 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'343}344 345// FIXME: Typededuction removes the owner - wrapper, therefore gsl::owner can not be used346// with Template classes like this. Is there a walkaround?347template <typename T>348struct TemplateValue {349 TemplateValue() = default;350 TemplateValue(T t) : val{t} {}351 352 void setVal(const T &t) { val = t; }353 const T getVal() const { return val; }354 355 T val;356};357 358// FIXME: Same typededcution problems359template <typename T>360void template_function(T t) {361 gsl::owner<int *> owner_t = t; // Probably bad, since type deduction still wrong362 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T'363 // CHECK-NOTES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'364}365 366// FIXME: Same typededcution problems367void test_templates() {368 int stack_int = 42;369 int *stack_ptr1 = &stack_int;370 371 TemplateValue<gsl::owner<int *>> Owner0; // Ok, T should be owner, but is int*372 373 TemplateValue<gsl::owner<int *>> Owner1(new int(42)); // Ok, T should be owner, but is int*374 Owner1.setVal(&stack_int); // Bad since non-owner assignment375 Owner1.setVal(stack_ptr1); // Bad since non-owner assignment376 //Owner1.setVal(new int(42)); // Ok, but since type deduction is wrong, this one is considered harmful377 378 int *stack_ptr2 = Owner1.getVal(); // Bad, initializing non-owner with owner379 380 TemplateValue<int *> NonOwner1(new int(42)); // Bad, T is int *, hence dynamic memory to non-owner381 gsl::owner<int *> IntOwner1 = NonOwner1.getVal(); // Bad, since owner initialized with non-owner382 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'383 384 template_function(IntOwner1); // Ok, but not actually ok, since type deduction removes owner385 template_function(stack_ptr1); // Bad, but type deduction gets it wrong386}387 388namespace PR63994 {389 struct A {390 virtual ~A() {}391 };392 393 struct B : public A {};394 395 A* foo(int x) {396 return new B;397 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'A *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'398 }399}400 401namespace PR59389 {402 struct S {403 S();404 S(int);405 406 int value = 1;407 };408 409 void testLambdaInFunctionNegative() {410 const auto MakeS = []() -> ::gsl::owner<S*> {411 return ::gsl::owner<S*>{new S{}};412 };413 }414 415 void testLambdaInFunctionPositive() {416 const auto MakeS = []() -> S* {417 return ::gsl::owner<S*>{new S{}};418 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'419 };420 }421 422 void testFunctionInFunctionNegative() {423 struct C {424 ::gsl::owner<S*> test() {425 return ::gsl::owner<S*>{new S{}};426 }427 };428 }429 430 void testFunctionInFunctionPositive() {431 struct C {432 S* test() {433 return ::gsl::owner<S*>{new S{}};434 // CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'435 }436 };437 }438 439 ::gsl::owner<S*> testReverseLambdaNegative() {440 const auto MakeI = [] -> int { return 5; };441 return ::gsl::owner<S*>{new S(MakeI())};442 }443 444 S* testReverseLambdaPositive() {445 const auto MakeI = [] -> int { return 5; };446 return ::gsl::owner<S*>{new S(MakeI())};447 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'448 }449 450 ::gsl::owner<S*> testReverseFunctionNegative() {451 struct C {452 int test() { return 5; }453 };454 return ::gsl::owner<S*>{new S(C().test())};455 }456 457 S* testReverseFunctionPositive() {458 struct C {459 int test() { return 5; }460 };461 return ::gsl::owner<S*>{new S(C().test())};462 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'463 }464 465 void testLambdaInLambdaNegative() {466 const auto MakeS = []() -> ::gsl::owner<S*> {467 const auto MakeI = []() -> int { return 5; };468 return ::gsl::owner<S*>{new S(MakeI())};469 };470 }471 472 void testLambdaInLambdaPositive() {473 const auto MakeS = []() -> S* {474 const auto MakeI = []() -> int { return 5; };475 return ::gsl::owner<S*>{new S(MakeI())};476 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'477 };478 }479 480 void testLambdaInLambdaWithDoubleReturns() {481 const auto MakeS = []() -> S* {482 const auto MakeS2 = []() -> S* {483 return ::gsl::owner<S*>{new S(1)};484 // CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>' [cppcoreguidelines-owning-memory]485 };486 return ::gsl::owner<S*>{new S(2)};487 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'488 };489 }490 491 void testReverseLambdaInLambdaNegative() {492 const auto MakeI = []() -> int {493 const auto MakeS = []() -> ::gsl::owner<S*> { return new S(); };494 return 5;495 };496 }497 498 void testReverseLambdaInLambdaPositive() {499 const auto MakeI = []() -> int {500 const auto MakeS = []() -> S* { return new S(); };501 // CHECK-NOTES: [[@LINE-1]]:39: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'502 return 5;503 };504 }505}506