diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -10107,19 +10107,9 @@ } namespace { -void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName, - const UnaryOperator *UnaryExpr) { - if (UnaryExpr->getOpcode() != UnaryOperator::Opcode::UO_AddrOf) - return; - - const auto *Lvalue = dyn_cast(UnaryExpr->getSubExpr()); - if (Lvalue == nullptr) - return; - - const auto *Var = dyn_cast(Lvalue->getDecl()); - if (Var == nullptr) - return; - +void CheckFreeArgumentsOnLvalue(Sema &S, const std::string &CalleeName, + const UnaryOperator *UnaryExpr, + const VarDecl *Var) { StorageClass Class = Var->getStorageClass(); if (Class == StorageClass::SC_Extern || Class == StorageClass::SC_PrivateExtern || @@ -10130,6 +10120,27 @@ << CalleeName << Var; } +void CheckFreeArgumentsOnLvalue(Sema &S, const std::string &CalleeName, + const UnaryOperator *UnaryExpr, const Decl *D) { + if (const auto *Field = dyn_cast(D)) + S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object) + << CalleeName << Field; +} + +void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName, + const UnaryOperator *UnaryExpr) { + if (UnaryExpr->getOpcode() != UnaryOperator::Opcode::UO_AddrOf) + return; + + if (const auto *Lvalue = dyn_cast(UnaryExpr->getSubExpr())) + if (const auto *Var = dyn_cast(Lvalue->getDecl())) + return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr, Var); + + if (const auto *Lvalue = dyn_cast(UnaryExpr->getSubExpr())) + return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr, + Lvalue->getMemberDecl()); +} + void CheckFreeArgumentsStackArray(Sema &S, const std::string &CalleeName, const DeclRefExpr *Lvalue) { if (!Lvalue->getType()->isArrayType()) diff --git a/clang/test/Sema/warn-free-nonheap-object.c b/clang/test/Sema/warn-free-nonheap-object.c --- a/clang/test/Sema/warn-free-nonheap-object.c +++ b/clang/test/Sema/warn-free-nonheap-object.c @@ -4,6 +4,11 @@ void *malloc(size_t); void free(void *); +struct S { + int I; + char *P; +}; + int GI; void test() { { @@ -31,4 +36,9 @@ free(A); // expected-warning {{attempt to call free on non-heap object 'A'}} free(&A); // expected-warning {{attempt to call free on non-heap object 'A'}} } + { + struct S s; + free(&s.I); // expected-warning {{attempt to call free on non-heap object 'I'}} + free(s.P); + } } diff --git a/clang/test/Sema/warn-free-nonheap-object.cpp b/clang/test/Sema/warn-free-nonheap-object.cpp --- a/clang/test/Sema/warn-free-nonheap-object.cpp +++ b/clang/test/Sema/warn-free-nonheap-object.cpp @@ -13,10 +13,25 @@ struct S { operator char *() { return ptr; } + void CFree() { + ::free(&ptr); // expected-warning {{attempt to call free on non-heap object 'ptr'}} + ::free(&I); // expected-warning {{attempt to call free on non-heap object 'I'}} + ::free(ptr); + } + + void CXXFree() { + std::free(&ptr); // expected-warning {{attempt to call std::free on non-heap object 'ptr'}} + std::free(&I); // expected-warning {{attempt to call std::free on non-heap object 'I'}} + std::free(ptr); + } + private: char *ptr = (char *)std::malloc(10); + static int I; }; +int S::I = 0; + void test1() { { free(&GI); // expected-warning {{attempt to call free on non-heap object 'GI'}} @@ -51,6 +66,10 @@ free(s); free(&s); // expected-warning {{attempt to call free on non-heap object 's'}} } + { + S s; + s.CFree(); + } } void test2() { @@ -87,4 +106,8 @@ std::free(s); std::free(&s); // expected-warning {{attempt to call std::free on non-heap object 's'}} } + { + S s; + s.CXXFree(); + } }