isuckatcs updated this revision to Diff 489940.
isuckatcs added a comment.

- Renamed and moved `isMultiLevelConvertiblePointer()` to `ASTContext`
- Added a unit test to test the said function


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D135495/new/

https://reviews.llvm.org/D135495

Files:
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
  clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
  clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/AST/Type.h
  clang/lib/AST/ASTContext.cpp
  clang/unittests/AST/CMakeLists.txt
  clang/unittests/AST/PointerConversionTest.cpp

Index: clang/unittests/AST/PointerConversionTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/AST/PointerConversionTest.cpp
@@ -0,0 +1,245 @@
+//===- unittests/AST/PointerConversionTest.cpp - Pointer conversion tests -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for ASTContext::isQualificationConvertiblePointer(),
+// which checks if one pointer can be converted to the other.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+
+auto DeclMatcher = ast_matchers::declaratorDecl().bind("id");
+
+class TypeExtractor : public ast_matchers::MatchFinder::MatchCallback {
+  int MatchCount = 0;
+
+public:
+  QualType T1, T2;
+
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    if (const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>("id")) {
+      if (MatchCount == 0)
+        T1 = VD->getType();
+      else
+        T2 = VD->getType();
+
+      ++MatchCount;
+    } else if (const auto *FD =
+                   Result.Nodes.getNodeAs<clang::FunctionDecl>("id")) {
+      if (MatchCount == 0)
+        T1 = FD->getType();
+      else
+        T2 = FD->getType();
+
+      ++MatchCount;
+    }
+  }
+};
+
+bool CheckConvertible(llvm::StringRef Source, LangStandard Std) {
+  TypeExtractor Extractor;
+  ast_matchers::MatchFinder Finder;
+
+  Finder.addMatcher(DeclMatcher, &Extractor);
+  std::unique_ptr<tooling::FrontendActionFactory> Factory(
+      tooling::newFrontendActionFactory(&Finder));
+
+  std::string StdArg = "-std=";
+  StdArg += Std.getName();
+
+  LangOptions Opts;
+  std::vector<std::string> Tmp;
+  LangOptions::setLangDefaults(Opts, Std.Language, {}, Tmp,
+                               Std.getLangKind(Std.getName()));
+
+  tooling::runToolOnCodeWithArgs(Factory->create(), Source, {StdArg});
+
+  return ASTContext::isQualificationConvertiblePointer(Extractor.T1,
+                                                       Extractor.T2, Opts);
+}
+
+TEST(QualificationConversion, builtin) {
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(char **p; const char **p1;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin2) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(char **p; const char* const * p2;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin3) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(char **p; volatile char * const * p3;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin4) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(char **p; volatile const char* const* p4;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin5) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(double *a[2][3]; double const * const (*ap)[3];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin6) {
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx17)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx20)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(double *a[2][3]; double * const (*ap1)[];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx2b)));
+}
+
+TEST(QualificationConversion, builtin7) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(int arr[3]; int *p;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin8) {
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(int arr[3][3]; int **p;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(int arr[3][3]; int (*p)[3];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(int arr[3][3]; int (*p)[2];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(int arr[3][3]; int (*p)[];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(int arr[3][3]; int (*p)[];)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx20)));
+}
+
+TEST(QualificationConversion, builtin9) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(void foo(); void (*ptr)() = foo;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(void foo(); void (* const ptr)() = foo;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(void foo(); void (* volatile ptr)() = foo;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(void foo(); void (* const volatile ptr)() = foo;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, builtin10) {
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(const int* p; int* p2;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(const int* p; const int* p2;)cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, derived) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : A {};
+        
+        A **p; 
+        volatile const A* const* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : A {};
+
+        B **p; 
+        volatile const A* const* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : A {};
+
+        A **p; 
+        volatile const B* const* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+TEST(QualificationConversion, member) {
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(
+        class A {};
+
+        char * A::*p; 
+        volatile const char* const A::* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_TRUE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : public A {};
+
+        char * B::*p; 
+        volatile const char* const B::* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : public A {};
+
+        char * A::*p; 
+        volatile const char* const B::* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+
+  ASSERT_FALSE(CheckConvertible(
+      R"cpp(
+        class A {};
+        class B : public A {};
+
+        char * B::*p; 
+        volatile const char* const A::* p4;
+        )cpp",
+      LangStandard::getLangStandardForKind(LangStandard::lang_cxx98)));
+}
+
+} // namespace clang
Index: clang/unittests/AST/CMakeLists.txt
===================================================================
--- clang/unittests/AST/CMakeLists.txt
+++ clang/unittests/AST/CMakeLists.txt
@@ -34,6 +34,7 @@
   StructuralEquivalenceTest.cpp
   TemplateNameTest.cpp
   TypePrinterTest.cpp
+  PointerConversionTest.cpp
   )
 
 clang_target_link_libraries(ASTTests
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -13456,3 +13456,104 @@
   CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
   return CUIDHash;
 }
+
+// Checks if From is qualification convertible to To based on the current
+// LangOpts. If From is any array, we perform the array to pointer conversion
+// first. The function only performs check based on C++ rules, which can differ
+// from the C rules.
+//
+// The function should only be called in C++ mode.
+bool ASTContext::isQualificationConvertiblePointer(QualType From, QualType To,
+                                                   LangOptions LangOpts,
+                                                   unsigned CurrentLevel,
+                                                   bool IsToConstSoFar) {
+  if (CurrentLevel == 0) {
+    if (From->isMemberPointerType() != To->isMemberPointerType())
+      return false;
+
+    if (From->isMemberPointerType() && To->isMemberPointerType()) {
+      const auto *FromMember = cast<MemberPointerType>(From);
+      const auto *ToMember = cast<MemberPointerType>(To);
+
+      if (FromMember->Class != ToMember->Class)
+        return false;
+
+      From = FromMember->PointeeType;
+      To = ToMember->PointeeType;
+    }
+
+    if (!To->isPointerType() ||
+        !(From->canDecayToPointerType() || From->isPointerType()))
+      return false;
+
+    auto FromPointeeTy = From->getPointeeOrArrayElementQualType();
+    auto ToPointeeTy = To->getPointeeOrArrayElementQualType();
+
+    return isQualificationConvertiblePointer(FromPointeeTy, ToPointeeTy,
+                                             LangOpts, CurrentLevel + 1, true);
+  }
+
+  bool Convertible = true;
+
+  if (From->isArrayType() && To->isArrayType()) {
+    if (From->isConstantArrayType() && To->isConstantArrayType()) {
+      Convertible &=
+          cast<ConstantArrayType>(From->getAsArrayTypeUnsafe())->getSize() ==
+          cast<ConstantArrayType>(To->getAsArrayTypeUnsafe())->getSize();
+    } else if (LangOpts.CPlusPlus20) {
+      // If there is an array type of unknown bound at some level
+      // (other than level zero) of From, there is an array type of
+      // unknown bound in the same level of To; (since C++20)
+      if (From->isIncompleteArrayType())
+        Convertible &= To->isIncompleteArrayType();
+
+      // ... [or there is an array type of known bound in From and
+      // an array type of unknown bound in To (since C++20)] then
+      // there must be a 'const' at every single level (other than
+      // level zero) of To up until the current level.
+      if (From->isConstantArrayType() && To->isIncompleteArrayType())
+        Convertible &= IsToConstSoFar;
+    } else {
+      return false;
+    }
+
+    From = From->getAsArrayTypeUnsafe()->getElementType();
+    To = To->getAsArrayTypeUnsafe()->getElementType();
+  }
+
+  // If at the current level To is more cv-qualified than From [...],
+  // then there must be a 'const' at every single level (other than level zero)
+  // of To up until the current level
+  bool MoreCVQualified =
+      To.getQualifiers().isStrictSupersetOf(From.getQualifiers());
+  if (MoreCVQualified)
+    Convertible &= IsToConstSoFar;
+
+  // If the pointers don't have the same amount of levels, they are not
+  // convertible to each other.
+  if (!From->isPointerType() || !To->isPointerType())
+    return Convertible &&
+           (MoreCVQualified || From.getQualifiers() == To.getQualifiers()) &&
+           From->getCanonicalTypeUnqualified() ==
+               To->getCanonicalTypeUnqualified();
+
+  // Note that in the C programming language, const/volatile can be added to the
+  // first level only.
+  bool CanBeCVQualified = LangOpts.CPlusPlus || CurrentLevel == 1;
+
+  // If the current level (other than level zero) in From is 'const' qualified,
+  // the same level in To must also be 'const' qualified.
+  if (From.isConstQualified())
+    Convertible &= To.isConstQualified() && CanBeCVQualified;
+
+  // If the current level (other than level zero) in From is 'volatile'
+  // qualified, the same level in To must also be 'volatile' qualified.
+  if (From.isVolatileQualified())
+    Convertible &= To.isVolatileQualified() && CanBeCVQualified;
+
+  IsToConstSoFar &= To.isConstQualified();
+  return Convertible && isQualificationConvertiblePointer(
+                            From->getAs<PointerType>()->getPointeeType(),
+                            To->getAs<PointerType>()->getPointeeType(),
+                            LangOpts, CurrentLevel + 1, IsToConstSoFar);
+}
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -2466,6 +2466,10 @@
   /// This should never be used when type qualifiers are meaningful.
   const Type *getPointeeOrArrayElementType() const;
 
+  /// If this is a pointer type, return the pointee type.
+  /// If this is an array type, return the array element type.
+  QualType getPointeeOrArrayElementQualType() const;
+
   /// If this is a pointer, ObjC object pointer, or block
   /// pointer, this returns the respective pointee.
   QualType getPointeeType() const;
@@ -7350,13 +7354,18 @@
 }
 
 inline const Type *Type::getPointeeOrArrayElementType() const {
-  const Type *type = this;
-  if (type->isAnyPointerType())
-    return type->getPointeeType().getTypePtr();
-  else if (type->isArrayType())
-    return type->getBaseElementTypeUnsafe();
-  return type;
+  return getPointeeOrArrayElementQualType().getTypePtr();
 }
+
+inline QualType Type::getPointeeOrArrayElementQualType() const {
+  if (isAnyPointerType())
+    return getPointeeType();
+  else if (isArrayType())
+    return getAsArrayTypeUnsafe()->getElementType();
+
+  return {this, 0};
+}
+
 /// Insertion operator for partial diagnostics. This allows sending adress
 /// spaces into a diagnostic with <<.
 inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &PD,
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -2825,6 +2825,11 @@
   bool propertyTypesAreCompatible(QualType, QualType);
   bool typesAreBlockPointerCompatible(QualType, QualType);
 
+  static bool isQualificationConvertiblePointer(QualType From, QualType To,
+                                                LangOptions LangOpts,
+                                                unsigned CurrentLevel = 0,
+                                                bool IsToConstSoFar = false);
+
   bool isObjCIdType(QualType T) const {
     if (const auto *ET = dyn_cast<ElaboratedType>(T))
       T = ET->getNamedType();
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp
@@ -101,6 +101,126 @@
   }
 }
 
+void throw_catch_pointer_c() noexcept {
+  try {
+    int a = 1;
+    throw &a;
+  } catch(const int *) {}
+}
+
+void throw_catch_pointer_v() noexcept {
+  try {
+    int a = 1;
+    throw &a;
+  } catch(volatile int *) {}
+}
+
+void throw_catch_pointer_cv() noexcept {
+  try {
+    int a = 1;
+    throw &a;
+  } catch(const volatile int *) {}
+}
+
+void throw_catch_multi_ptr_1() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_catch_multi_ptr_1' which should not throw exceptions
+  try {
+    char **p = 0;
+    throw p;
+  } catch (const char **) {
+  }
+}
+
+void throw_catch_multi_ptr_2() noexcept {
+  try {
+    char **p = 0;
+    throw p;
+  } catch (const char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_3() noexcept {
+  try {
+    char **p = 0;
+    throw p;
+  } catch (volatile char *const *) {
+  }
+}
+
+void throw_catch_multi_ptr_4() noexcept {
+  try {
+    char **p = 0;
+    throw p;
+  } catch (volatile const char *const *) {
+  }
+}
+
+// FIXME: In this case 'a' is convertible to the handler and should be caught
+// but in reality it's thrown. Note that clang doesn't report a warning for 
+// this either.
+void throw_catch_multi_ptr_5() noexcept {
+  try {
+    double *a[2][3];
+    throw a;
+  } catch (double *(*)[3]) {
+  }
+}
+
+
+void throw_c_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer' which should not throw exceptions
+  try {
+    int a = 1;
+    const int *p = &a;
+    throw p;
+  } catch(int *) {}
+}
+
+void throw_c_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_c_catch_pointer_v' which should not throw exceptions
+  try {
+    int a = 1;
+    const int *p = &a;
+    throw p;
+  } catch(volatile int *) {}
+}
+
+void throw_v_catch_pointer() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer' which should not throw exceptions
+  try {
+    int a = 1;
+    volatile int *p = &a;
+    throw p;
+  } catch(int *) {}
+}
+
+void throw_v_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_v_catch_pointer_c' which should not throw exceptions
+  try {
+    int a = 1;
+    volatile int *p = &a;
+    throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_c() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_c' which should not throw exceptions
+  try {
+    int a = 1;
+    const volatile int *p = &a;
+    throw p;
+  } catch(const int *) {}
+}
+
+void throw_cv_catch_pointer_v() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_cv_catch_pointer_v' which should not throw exceptions
+  try {
+    int a = 1;
+    const volatile int *p = &a;
+    throw p;
+  } catch(volatile int *) {}
+}
+
 class base {};
 class derived: public base {};
 
@@ -112,6 +232,24 @@
   }
 }
 
+void throw_derived_catch_base_ptr_c() noexcept {
+  try {
+    derived d;
+    throw &d; 
+  } catch(const base *) {
+  }
+}
+
+void throw_derived_catch_base_ptr() noexcept {
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_derived_catch_base_ptr' which should not throw exceptions
+  try {
+    derived d;
+    const derived *p = &d;
+    throw p; 
+  } catch(base *) {
+  }
+}
+
 void try_nested_try(int n) noexcept {
   // CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'try_nested_try' which should not throw exceptions
   try {
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
@@ -80,7 +80,7 @@
     /// possible to catch multiple exception types by one 'catch' if they
     /// are a subclass of the 'catch'ed exception type.
     /// Returns 'true' if some exceptions were filtered, otherwise 'false'.
-    bool filterByCatch(const Type *BaseClass);
+    bool filterByCatch(const Type *BaseClass, const LangOptions &LangOpts);
 
     /// Filter the set of thrown exception type against a set of ignored
     /// types that shall not be considered in the exception analysis.
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
@@ -56,10 +56,26 @@
       [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
 }
 
-bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(const Type *BaseClass) {
+bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
+    const Type *BaseClass, const LangOptions &LangOpts) {
   llvm::SmallVector<const Type *, 8> TypesToDelete;
   for (const Type *T : ThrownExceptions) {
-    if (T == BaseClass || isBaseOf(T, BaseClass))
+    if (T->isPointerType() && BaseClass->isPointerType()) {
+
+      SplitQualType TPointeeTy = T->getPointeeType().split();
+      SplitQualType BasePointeeTy = BaseClass->getPointeeType().split();
+
+      if (!TPointeeTy.Ty->isPointerType() &&
+          (TPointeeTy.Ty == BasePointeeTy.Ty ||
+           isBaseOf(TPointeeTy.Ty, BasePointeeTy.Ty)) &&
+          (BasePointeeTy.Quals.isStrictSupersetOf(TPointeeTy.Quals) ||
+           BasePointeeTy.Quals == TPointeeTy.Quals)) {
+        TypesToDelete.push_back(T);
+      } else if (ASTContext::isQualificationConvertiblePointer(
+                     QualType(T, 0), QualType(BaseClass, 0), LangOpts)) {
+        TypesToDelete.push_back(T);
+      }
+    } else if (T == BaseClass || isBaseOf(T, BaseClass))
       TypesToDelete.push_back(T);
   }
 
@@ -186,11 +202,14 @@
                            ->getUnqualifiedDesugaredType();
         }
 
+        const auto &LangOpts =
+            Catch->getExceptionDecl()->getASTContext().getLangOpts();
+
         // If the caught exception will catch multiple previously potential
         // thrown types (because it's sensitive to inheritance) the throwing
         // situation changes. First of all filter the exception types and
         // analyze if the baseclass-exception is rethrown.
-        if (Uncaught.filterByCatch(CaughtType)) {
+        if (Uncaught.filterByCatch(CaughtType, LangOpts)) {
           ExceptionInfo::Throwables CaughtExceptions;
           CaughtExceptions.insert(CaughtType);
           ExceptionInfo Rethrown = throwsException(Catch->getHandlerBlock(),
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to