diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5129fc8cdbf3..586be85cf61e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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 " diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index bd2b0aef3d36..0c13558c8dd4 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -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())); } +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(RawHandlers.get()); - for(unsigned i = 0; i < NumHandlers - 1; ++i) { + llvm::SmallVector TypesWithHandlers; + + for(unsigned i = 0; i < NumHandlers; ++i) { CXXCatchStmt *Handler = llvm::cast(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)); } - // FIXME: We should detect handlers for the same type as an earlier one. - // This one is rather easy. + + // 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 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 index 000000000000..c8b642e7ab53 --- /dev/null +++ b/clang/test/SemaCXX/unreachable-catch-clauses.cpp @@ -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}} +