Author: Aditya Medhane Date: 2026-07-06T13:39:31+02:00 New Revision: fb1de34e8db8d11c764c7ff422879f923554cbd9
URL: https://github.com/llvm/llvm-project/commit/fb1de34e8db8d11c764c7ff422879f923554cbd9 DIFF: https://github.com/llvm/llvm-project/commit/fb1de34e8db8d11c764c7ff422879f923554cbd9.diff LOG: [Clang][Sema] Warn on a function reference compared/converted to null (#204944) Look through a reference-to-function in `DiagnoseAlwaysNonNullPointer` so `-Wtautological-pointer-compare` and `-Wpointer-bool-conversion` fire for it, as they already do for a bare function name. Fixes #46362 Added: Modified: clang/docs/ReleaseNotes.md clang/lib/Sema/SemaChecking.cpp clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp clang/test/SemaCXX/condition.cpp clang/test/SemaCXX/warn-bool-conversion.cpp clang/test/SemaCXX/warn-tautological-compare.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b7142ecb072ff..ea79b3b1b3692 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -686,6 +686,11 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the by the kernel, so setting them is almost always a typo (matching the bionic libc `diagnose_if` check). +- `-Wtautological-pointer-compare` and `-Wpointer-bool-conversion` now + diagnose a reference to a function (e.g. of type `void (&)()`) compared + against or converted to a null pointer, the same as a bare function name. + (#GH46362) + ### Improvements to Clang's time-trace ### Improvements to Coverage Mapping diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 01b1f4c26f017..965b92a2b5874 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -14398,6 +14398,11 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E, } QualType T = D->getType(); + // A reference to a function is never null either; look through it. + const bool IsFunctionReference = + T->isReferenceType() && T->getPointeeType()->isFunctionType(); + if (IsFunctionReference) + T = T->getPointeeType(); const bool IsArray = T->isArrayType(); const bool IsFunction = T->isFunctionType(); @@ -14433,7 +14438,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E, Diag(E->getExprLoc(), DiagID) << DiagType << S.str() << E->getSourceRange() << Range << IsEqual; - if (!IsFunction) + // The fix-it notes below only apply to a bare function name, not a reference. + if (!IsFunction || IsFunctionReference) return; // Suggest '&' to silence the function warning. diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp index 6744ce1cad174..1d18a667ead59 100644 --- a/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp +++ b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp @@ -45,7 +45,7 @@ void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { *ft; +ft; -ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} - !ft; + !ft; // expected-warning {{address of function 'ft' will always evaluate to 'true'}} ~ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} &ft; ++ft; // expected-error {{cannot increment value of type 'T ()'}} @@ -62,4 +62,4 @@ void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { } // Make sure we only emit diagnostics once. -template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]); +template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]); // expected-note {{in instantiation of function template specialization 'dependent<A, B>' requested here}} diff --git a/clang/test/SemaCXX/condition.cpp b/clang/test/SemaCXX/condition.cpp index db7694c7ee024..2526df22ec836 100644 --- a/clang/test/SemaCXX/condition.cpp +++ b/clang/test/SemaCXX/condition.cpp @@ -56,7 +56,7 @@ void test3() { } void test4(bool (&x)(void)) { - while (x); + while (x); // expected-warning {{address of function 'x' will always evaluate to 'true'}} } template <class> diff --git a/clang/test/SemaCXX/warn-bool-conversion.cpp b/clang/test/SemaCXX/warn-bool-conversion.cpp index 18c35776b17bc..8cf2a7a5816f5 100644 --- a/clang/test/SemaCXX/warn-bool-conversion.cpp +++ b/clang/test/SemaCXX/warn-bool-conversion.cpp @@ -132,6 +132,15 @@ void bar() { b = S2::f4; if (S2::f4) {} } + +// GH46362: a reference to a function is never null, like a bare function name. +void func_ref(void (&f)(), int *&ptr) { + bool b; + b = f; // expected-warning {{address of function 'f' will always evaluate to 'true'}} + if (f) {} // expected-warning {{address of function 'f' will always evaluate to 'true'}} + b = ptr; + if (ptr) {} +} } namespace Array { diff --git a/clang/test/SemaCXX/warn-tautological-compare.cpp b/clang/test/SemaCXX/warn-tautological-compare.cpp index 7d5b4b14e9981..3028a9cf935cd 100644 --- a/clang/test/SemaCXX/warn-tautological-compare.cpp +++ b/clang/test/SemaCXX/warn-tautological-compare.cpp @@ -110,6 +110,25 @@ namespace FunctionCompare { } } +namespace FunctionReferenceCompare { + // GH46362: a reference to a function is never null, like a bare function name. + // No fix-it note is emitted, since prefixing '&' would not silence it. + void test(void (&f)(), int *&ptr) { + if (f == 0) {} + // expected-warning@-1{{comparison of function 'f' equal to a null pointer is always false}} + if (f != 0) {} + // expected-warning@-1{{comparison of function 'f' not equal to a null pointer is always true}} + if (f == nullptr) {} + // expected-warning@-1{{comparison of function 'f' equal to a null pointer is always false}} + if (nullptr != f) {} + // expected-warning@-1{{comparison of function 'f' not equal to a null pointer is always true}} + + // A reference to a pointer can be null: no warning. + if (ptr == nullptr) {} + if (ptr != nullptr) {} + } +} + namespace PointerCompare { extern int a __attribute__((weak)); int b; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
