Check for identical types in C++ catch expression. Patch by Erik Verbruggen.
authorSebastian Redl <sebastian.redl@getdesigned.at>
Wed, 29 Jul 2009 17:15:45 +0000 (17:15 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Wed, 29 Jul 2009 17:15:45 +0000 (17:15 +0000)
llvm-svn: 77475

clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaStmt.cpp
clang/test/SemaCXX/unreachable-catch-clauses.cpp [new file with mode: 0644]

index 5129fc8..586be85 100644 (file)
@@ -1491,6 +1491,9 @@ def err_bad_memptr_rhs : Error<
 def err_bad_memptr_lhs : Error<
   "left hand operand to %0 must be a %select{|pointer to }1class "
   "compatible with the right hand operand, but is %2">;
+def warn_exception_caught_by_earlier_handler : Warning<
+  "exception of type %0 will be caught by earlier handler">;
+def note_previous_exception_handler : Note<"for type %0">;
 
 def err_conditional_void_nonvoid : Error<
   "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand "
index bd2b0ae..0c13558 100644 (file)
@@ -19,6 +19,8 @@
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 using namespace clang;
 
 Sema::OwningStmtResult Sema::ActOnExprStmt(FullExprArg expr) {
@@ -1243,6 +1245,38 @@ Sema::ActOnCXXCatchBlock(SourceLocation CatchLoc, DeclPtrTy ExDecl,
                                           HandlerBlock.takeAs<Stmt>()));
 }
 
+class TypeWithHandler {
+  QualType t;
+  CXXCatchStmt *stmt;
+public:
+  TypeWithHandler(const QualType &type, CXXCatchStmt *statement)
+  : t(type), stmt(statement) {}
+
+  bool operator<(const TypeWithHandler &y) const {
+    if (t.getTypePtr() < y.t.getTypePtr())
+      return true;
+    else if (t.getTypePtr() > y.t.getTypePtr())
+      return false;
+    else if (t.getCVRQualifiers() < y.t.getCVRQualifiers())
+      return true;
+    else if (t.getCVRQualifiers() < y.t.getCVRQualifiers())
+      return false;
+    else
+      return getTypeSpecStartLoc() < y.getTypeSpecStartLoc();
+  }
+  
+  bool operator==(const TypeWithHandler& other) const {
+    return t.getTypePtr() == other.t.getTypePtr()
+        && t.getCVRQualifiers() == other.t.getCVRQualifiers();
+  }
+  
+  QualType getQualType() const { return t; }
+  CXXCatchStmt *getCatchStmt() const { return stmt; }
+  SourceLocation getTypeSpecStartLoc() const {
+    return stmt->getExceptionDecl()->getTypeSpecStartLoc();
+  }
+};
+
 /// ActOnCXXTryBlock - Takes a try compound-statement and a number of
 /// handlers and creates a try statement from them.
 Action::OwningStmtResult
@@ -1253,13 +1287,44 @@ Sema::ActOnCXXTryBlock(SourceLocation TryLoc, StmtArg TryBlock,
          "The parser shouldn't call this if there are no handlers.");
   Stmt **Handlers = reinterpret_cast<Stmt**>(RawHandlers.get());
 
-  for(unsigned i = 0; i < NumHandlers - 1; ++i) {
+  llvm::SmallVector<TypeWithHandler, 8> TypesWithHandlers;
+  
+  for(unsigned i = 0; i < NumHandlers; ++i) {
     CXXCatchStmt *Handler = llvm::cast<CXXCatchStmt>(Handlers[i]);
-    if (!Handler->getExceptionDecl())
-      return StmtError(Diag(Handler->getLocStart(), diag::err_early_catch_all));
+    if (!Handler->getExceptionDecl()) {
+      if (i < NumHandlers - 1)
+        return StmtError(Diag(Handler->getLocStart(),
+                              diag::err_early_catch_all));
+      
+      continue;
+    }
+    
+    const QualType CaughtType = Handler->getCaughtType();
+    const QualType CanonicalCaughtType = Context.getCanonicalType(CaughtType);
+    TypesWithHandlers.push_back(TypeWithHandler(CanonicalCaughtType, Handler));
+  }
+
+  // Detect handlers for the same type as an earlier one.
+  if (NumHandlers > 1) {
+    llvm::array_pod_sort(TypesWithHandlers.begin(), TypesWithHandlers.end());
+    
+    TypeWithHandler prev = TypesWithHandlers[0];
+    for (unsigned i = 1; i < TypesWithHandlers.size(); ++i) {
+      TypeWithHandler curr = TypesWithHandlers[i];
+      
+      if (curr == prev) {
+        Diag(curr.getTypeSpecStartLoc(),
+             diag::warn_exception_caught_by_earlier_handler)
+          << curr.getCatchStmt()->getCaughtType().getAsString();
+        Diag(prev.getTypeSpecStartLoc(),
+             diag::note_previous_exception_handler)
+          << prev.getCatchStmt()->getCaughtType().getAsString();
+      }
+      
+      prev = curr;
+    }
   }
-  // FIXME: We should detect handlers for the same type as an earlier one.
-  // This one is rather easy.
+  
   // FIXME: We should detect handlers that cannot catch anything because an
   // earlier handler catches a superclass. Need to find a method that is not
   // quadratic for this.
diff --git a/clang/test/SemaCXX/unreachable-catch-clauses.cpp b/clang/test/SemaCXX/unreachable-catch-clauses.cpp
new file mode 100644 (file)
index 0000000..c8b642e
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+class BaseEx {};
+class Ex1: public BaseEx {};
+typedef Ex1 Ex2;
+
+void f();
+
+void test()
+try {}
+catch (BaseEx &e) { f(); }
+catch (Ex1 &e) { f(); } // expected-note {{for type class Ex1 &}}
+catch (Ex2 &e) { f(); } // expected-warning {{exception of type Ex2 & will be caught by earlier handler}}
+