diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b42d5f8425af6..0aa8a48eaab93 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -622,6 +622,19 @@ Improvements to Clang's diagnostics - Improved the FixIts for unused lambda captures. +- ``-Wpointer-bool-conversion`` will now also warn in the following case + + .. code-block:: c + + struct B { + B(bool V) {} + }; + void test(const B& b); + void test0(B* b) { + test(b); // this will call B::B(bool) and create a new B + } + + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0f77083dac9df..cb8662f2bc09b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4397,6 +4397,9 @@ def ext_ms_impcast_fn_obj : ExtWarn< "implicit conversion between pointer-to-function and pointer-to-object is a " "Microsoft extension">, InGroup; +def warn_imp_constructor_pointer_to_bool : Warning< + "implicit conversion from %0 to %1 calls %q2; did you intend to dereference ?">, + InGroup; def warn_impcast_pointer_to_bool : Warning< "address of %select{'%1'|function '%1'|array '%1'|lambda function pointer " "conversion operator}0 will always evaluate to 'true'">, diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8f8e1ceb7197e..10fa2b6588ae5 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11767,6 +11767,28 @@ static void CheckImplicitArgumentConversions(Sema &S, const CallExpr *TheCall, SourceLocation CC) { for (unsigned I = 0, N = TheCall->getNumArgs(); I < N; ++I) { const Expr *CurrA = TheCall->getArg(I); + + if (const auto *MTE = dyn_cast(CurrA)) + // We shouldnt skip over any node here as it may be an attempt to silence + // the warning + if (const auto *CCE = dyn_cast(MTE->getSubExpr())) + if (CCE->getNumArgs() == 1 && + CCE->getArg(0)->getType()->isBooleanType() && + !CCE->getConstructor()->isExplicit()) { + const Expr *Inner = CCE->getArg(0)->IgnoreImpCasts(); + if ((Inner->getType()->isAnyPointerType() && + Inner->getType()->getPointeeType().getUnqualifiedType() == + CCE->getType().getUnqualifiedType())) { + S.Diag(CCE->getLocation(), + diag::warn_imp_constructor_pointer_to_bool) + << Inner->getType() << CCE->getType() << CCE->getConstructor() + << FixItHint::CreateInsertion(Inner->getBeginLoc(), "*"); + S.Diag(CCE->getConstructor()->getLocation(), + diag::note_entity_declared_at) + << CCE->getConstructor(); + } + } + if (!IsImplicitBoolFloatConversion(S, CurrA, true)) continue; diff --git a/clang/test/SemaCXX/warn-bool-conversion.cpp b/clang/test/SemaCXX/warn-bool-conversion.cpp index 18c35776b17bc..93090553db8fa 100644 --- a/clang/test/SemaCXX/warn-bool-conversion.cpp +++ b/clang/test/SemaCXX/warn-bool-conversion.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify=expected,expected-cxx11 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s // RUN: %clang_cc1 -fsyntax-only -verify=expected,expected-cxx11 -std=c++11 %s +// RUN: not %clang_cc1 -fsyntax-only %s -fdiagnostics-parseable-fixits 2>&1 | FileCheck %s namespace BooleanFalse { int* j = false; @@ -234,3 +235,35 @@ namespace Template { template void h(); } #endif // __cplusplus < 201703L + +namespace implicit_constructor_bool { + +struct B { + bool a; + B(bool V) : a(V) {} // expected-note {{'B' declared here}} +}; + +void test(const B& b); + +void test0(B* b) { + test(b); // expected-warning {{implicit conversion from 'B *' to 'const B' calls 'implicit_constructor_bool::B::B'; did you intend to dereference ?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:8-[[@LINE-1]]:8}:"*" + test((const B&)b); + test(B(b)); + test((bool)b); + test(static_cast(b)); + test(*b); +} + +struct C { + bool a; + explicit C(bool V) : a(V) {} +}; + +void testC(const C& b); // expected-note {{candidate function not viable: no known conversion from 'C *' to 'const C' for 1st argument; dereference the argument with *}} + +void testC0(C* b) { + testC(b); // expected-error {{no matching function for call to 'testC'}} +} + +}