From 00fb59690f98ef46666c640db5f7cd79ea4f5178 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Sat, 28 Jun 2014 23:25:37 +0000 Subject: [PATCH] Extend -Wtautological-undefined-compare and -Wundefined-bool-conversion to trigger on taking the address of a reference that is returned from a function call. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@211989 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 1 + lib/Sema/SemaChecking.cpp | 58 +++++++++++--- .../warn-tautological-undefined-compare.cpp | 78 +++++++++++++++++++ .../warn-undefined-bool-conversion.cpp | 60 ++++++++++++++ 4 files changed, 185 insertions(+), 12 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 72a2533f54..ac97e083fa 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2433,6 +2433,7 @@ def warn_address_of_reference_null_compare : Warning< "code; comparison may be assumed to always evaluate to " "%select{true|false}0">, InGroup; +def note_reference_is_return_value : Note<"%0 returns a reference">; def note_function_warning_silence : Note< "prefix with the address-of operator to silence this warning">; diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 72d3b2761d..1a49b4ac4f 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -6192,6 +6192,38 @@ enum { ArrayPointer }; +// Helper function for Sema::DiagnoseAlwaysNonNullPointer. +// Returns true when emitting a warning about taking the address of a reference. +static bool CheckForReference(Sema &SemaRef, const Expr *E, + PartialDiagnostic PD) { + E = E->IgnoreParenImpCasts(); + + const FunctionDecl *FD = nullptr; + + if (const DeclRefExpr *DRE = dyn_cast(E)) { + if (!DRE->getDecl()->getType()->isReferenceType()) + return false; + } else if (const MemberExpr *M = dyn_cast(E)) { + if (!M->getMemberDecl()->getType()->isReferenceType()) + return false; + } else if (const CallExpr *Call = dyn_cast(E)) { + if (!Call->getCallReturnType()->isReferenceType()) + return false; + FD = Call->getDirectCallee(); + } else { + return false; + } + + SemaRef.Diag(E->getExprLoc(), PD); + + // If possible, point to location of function. + if (FD) { + SemaRef.Diag(FD->getLocation(), diag::note_reference_is_return_value) << FD; + } + + return true; +} + /// \brief Diagnose pointers that are always non-null. /// \param E the expression containing the pointer /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is @@ -6227,6 +6259,17 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E, E = UO->getSubExpr(); } + if (IsAddressOf) { + unsigned DiagID = IsCompare + ? diag::warn_address_of_reference_null_compare + : diag::warn_address_of_reference_bool_conversion; + PartialDiagnostic PD = PDiag(DiagID) << E->getSourceRange() << Range + << IsEqual; + if (CheckForReference(*this, E, PD)) { + return; + } + } + // Expect to find a single Decl. Skip anything more complicated. ValueDecl *D = nullptr; if (DeclRefExpr *R = dyn_cast(E)) { @@ -6243,18 +6286,9 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E, const bool IsArray = T->isArrayType(); const bool IsFunction = T->isFunctionType(); - if (IsAddressOf) { - // Address of function is used to silence the function warning. - if (IsFunction) - return; - - if (T->isReferenceType()) { - unsigned DiagID = IsCompare - ? diag::warn_address_of_reference_null_compare - : diag::warn_address_of_reference_bool_conversion; - Diag(E->getExprLoc(), DiagID) << E->getSourceRange() << Range << IsEqual; - return; - } + // Address of function is used to silence the function warning. + if (IsAddressOf && IsFunction) { + return; } // Found nothing. diff --git a/test/SemaCXX/warn-tautological-undefined-compare.cpp b/test/SemaCXX/warn-tautological-undefined-compare.cpp index b30b576d35..ce05bfc14d 100644 --- a/test/SemaCXX/warn-tautological-undefined-compare.cpp +++ b/test/SemaCXX/warn-tautological-undefined-compare.cpp @@ -32,3 +32,81 @@ class test2 { int &x; int y; }; + +namespace function_return_reference { + int& get_int(); + // expected-note@-1 4{{'get_int' returns a reference}} + class B { + public: + static int &stat(); + // expected-note@-1 4{{'stat' returns a reference}} + int &get(); + // expected-note@-1 8{{'get' returns a reference}} + }; + + void test() { + if (&get_int() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&(get_int()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&get_int() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&(get_int()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + + if (&B::stat() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&(B::stat()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&B::stat() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&(B::stat()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + + B b; + if (&b.get() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&(b.get()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&b.get() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&(b.get()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + + B* b_ptr = &b; + if (&b_ptr->get() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&(b_ptr->get()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&b_ptr->get() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&(b_ptr->get()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + + int& (B::*m_ptr)() = &B::get; + if (&(b.*m_ptr)() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&((b.*m_ptr)()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&(b.*m_ptr)() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&((b.*m_ptr)()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + + int& (*f_ptr)() = &get_int; + if (&(*f_ptr)() == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + if (&((*f_ptr)()) == 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to false}} + + if (&(*f_ptr)() != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + if (&((*f_ptr)()) != 0) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + } +} diff --git a/test/SemaCXX/warn-undefined-bool-conversion.cpp b/test/SemaCXX/warn-undefined-bool-conversion.cpp index 40bbbd84a8..1f8baa0e8d 100644 --- a/test/SemaCXX/warn-undefined-bool-conversion.cpp +++ b/test/SemaCXX/warn-undefined-bool-conversion.cpp @@ -35,3 +35,63 @@ class test2 { int &x; int y; }; + +namespace function_return_reference { + int& get_int(); + // expected-note@-1 3{{'get_int' returns a reference}} + class B { + public: + static int &stat(); + // expected-note@-1 3{{'stat' returns a reference}} + int &get(); + // expected-note@-1 6{{'get' returns a reference}} + }; + + void test() { + if (&get_int()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&(get_int())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&get_int()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + + if (&B::stat()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&(B::stat())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&B::stat()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + + B b; + if (&b.get()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&(b.get())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&b.get()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + + B* b_ptr = &b; + if (&b_ptr->get()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&(b_ptr->get())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&b_ptr->get()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + + int& (B::*m_ptr)() = &B::get; + if (&(b.*m_ptr)()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&((b.*m_ptr)())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&(b.*m_ptr)()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + + int& (*f_ptr)() = &get_int; + if (&(*f_ptr)()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (&((*f_ptr)())) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + if (!&(*f_ptr)()) {} + // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; pointer may be assumed to always convert to true}} + } +} -- 2.40.0