tidy - cppcoreguidelines-owning-memory — Extra Clang Tools 22.0.0git documentation (original) (raw)
This check implements the type-based semantics of gsl::owner<T*>, which allows static analysis on code, that uses raw pointers to handle resources like dynamic memory, but won’t introduce RAII concepts.
This check implements I.11,C.33,R.3and GSL.Viewsfrom the C++ Core Guidelines. The definition of a gsl::owner<T*> is straight forward
namespace gsl { template owner = T; }
It is therefore simple to introduce the owner even without using an implementation of the Guideline Support Library.
All checks are purely type based and not (yet) flow sensitive.
The following examples will demonstrate the correct and incorrect initializations of owners, assignment is handled the same way. Note that both new and malloc()-like resource functions are considered to produce resources.
// Creating an owner with factory functions is checked. gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Dynamic memory must be assigned to an owner int* Something = new int(42); // BAD, will be caught gsl::owner<int*> Owner = new int(42); // Good gsl::owner<int*> Owner = new int[42]; // Good as well
// Returned owner must be assigned to an owner int* Something = function_that_returns_owner(); // Bad, factory function gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner
// Something not a resource or owner should not be assigned to owners int Stack = 42; gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned
In the case of dynamic memory as resource, only gsl::owner<T*> variables are allowed to be deleted.
// Example Bad, non-owner as resource handle, will be caught. int* NonOwner = new int(42); // First warning here, since new must land in an owner delete NonOwner; // Second warning here, since only owners are allowed to be deleted
// Example Good, Ownership correctly stated gsl::owner<int*> Owner = new int(42); // Good delete Owner; // Good as well, statically enforced, that only owners get deleted
The check will furthermore ensure, that functions, that expect agsl::owner<T*> as argument get called with either a gsl::owner<T*> or a newly created resource.
void expects_owner(gsl::owner<int*> o) { delete o; }
// Bad Code int NonOwner = 42; expects_owner(&NonOwner); // Bad, will get caught
// Good Code gsl::owner<int*> Owner = new int(42); expects_owner(Owner); // Good expects_owner(new int(42)); // Good as well, recognized created resource
// Port legacy code for better resource-safety gsl::owner<FILE*> File = fopen("my_file.txt", "rw+"); FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned
// ... use the file
fclose(File); // Ok, File is annotated as 'owner<>' fclose(BadFile); // BadFile is not an 'owner<>', will be warned
Options¶
LegacyResourceProducers¶
Semicolon-separated list of fully qualified names of legacy functions that create resources but cannot introduce gsl::owner<>. Defaults to ::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile.
LegacyResourceConsumers¶
Semicolon-separated list of fully qualified names of legacy functions expecting resource owners as pointer arguments but cannot introduce gsl::owner<>. Defaults to ::free;::realloc;::freopen;::fclose.
Limitations¶
Using gsl::owner<T*> in a typedef or alias is not handled correctly.
using heap_int = gsl::owner<int*>; heap_int allocated = new int(42); // False positive!
The gsl::owner<T*> is declared as a templated type alias. In template functions and classes, like in the example below, the information of the type aliases gets lost. Therefore using gsl::owner<T*> in a heavy templated code base might lead to false positives.
Known code constructs that do not get diagnosed correctly are:
std::exchangestd::vector<gsl::owner<T*>>
// This template function works as expected. Type information doesn't get lost. template void delete_owner(gsl::owner<T*> owned_object) { delete owned_object; // Everything alright }
gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Type deduction does not work for auto variables. // This is caught by the check and will be noted accordingly. auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*
// Problematic function template that looses the typeinformation on owner template void bad_template_function(T some_object) { // This line will trigger the warning, that a non-owner is assigned to an owner gsl::owner<T*> new_owner = some_object; }
// Calling the function with an owner still yields a false positive. bad_template_function(gsl::owner<int*>(new int(42)));
// The same issue occurs with templated classes like the following. template class OwnedValue { public: const T getValue() const { return _val; } private: T _val; };
// Code, that yields a false positive. OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int * // False positive, getValue returns int* and not gsl::owner<int*> gsl::owner<int*> OwnedInt = Owner.getValue();
Another limitation of the current implementation is only the type based checking. Suppose you have code like the following:
// Two owners with assigned resources gsl::owner<int*> Owner1 = new int(42); gsl::owner<int*> Owner2 = new int(42);
Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2! Owner1 = nullptr;
The semantic of a gsl::owner<T*> is mostly like a std::unique_ptr<T>, therefore assignment of two gsl::owner<T*> is considered a move, which requires that the resource Owner2 must have been released before the assignment. This kind of condition could be caught in later improvements of this check with flowsensitive analysis. Currently, the Clang Static Analyzercatches this bug for dynamic memory, but not for general types of resources.