You can use the non-standard, but typically implemented, cleanup attribute today for function-scoped cleanup. I don’t know if compilers inline this kind of “destructor”. It works well — I’ve seen it used successfully at multiple employers.
Not only inline it, but also optimize it away (eg. _cleanup_free char *ptr = NULL; the cleanup path just disappears in places before ptr is assigned to a non-NULL value).