1 | //===--- UnhandledExceptionAtNewCheck.cpp - clang-tidy --------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "UnhandledExceptionAtNewCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::bugprone { |
16 | |
17 | AST_MATCHER_P(CXXTryStmt, hasHandlerFor, |
18 | ast_matchers::internal::Matcher<QualType>, InnerMatcher) { |
19 | for (unsigned NH = Node.getNumHandlers(), I = 0; I < NH; ++I) { |
20 | const CXXCatchStmt *CatchS = Node.getHandler(i: I); |
21 | // Check for generic catch handler (match anything). |
22 | if (CatchS->getCaughtType().isNull()) |
23 | return true; |
24 | ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); |
25 | if (InnerMatcher.matches(Node: CatchS->getCaughtType(), Finder, Builder: &Result)) { |
26 | *Builder = std::move(Result); |
27 | return true; |
28 | } |
29 | } |
30 | return false; |
31 | } |
32 | |
33 | AST_MATCHER(CXXNewExpr, mayThrow) { |
34 | FunctionDecl *OperatorNew = Node.getOperatorNew(); |
35 | if (!OperatorNew) |
36 | return false; |
37 | return !OperatorNew->getType()->castAs<FunctionProtoType>()->isNothrow(); |
38 | } |
39 | |
40 | UnhandledExceptionAtNewCheck::UnhandledExceptionAtNewCheck( |
41 | StringRef Name, ClangTidyContext *Context) |
42 | : ClangTidyCheck(Name, Context) {} |
43 | |
44 | void UnhandledExceptionAtNewCheck::registerMatchers(MatchFinder *Finder) { |
45 | auto BadAllocType = |
46 | recordType(hasDeclaration(InnerMatcher: cxxRecordDecl(hasName(Name: "::std::bad_alloc" )))); |
47 | auto ExceptionType = |
48 | recordType(hasDeclaration(InnerMatcher: cxxRecordDecl(hasName(Name: "::std::exception" )))); |
49 | auto BadAllocReferenceType = referenceType(pointee(BadAllocType)); |
50 | auto ExceptionReferenceType = referenceType(pointee(ExceptionType)); |
51 | |
52 | auto CatchBadAllocType = |
53 | qualType(hasCanonicalType(InnerMatcher: anyOf(BadAllocType, BadAllocReferenceType, |
54 | ExceptionType, ExceptionReferenceType))); |
55 | auto BadAllocCatchingTryBlock = cxxTryStmt(hasHandlerFor(InnerMatcher: CatchBadAllocType)); |
56 | |
57 | auto FunctionMayNotThrow = functionDecl(isNoThrow()); |
58 | |
59 | Finder->addMatcher(NodeMatch: cxxNewExpr(mayThrow(), |
60 | unless(hasAncestor(BadAllocCatchingTryBlock)), |
61 | hasAncestor(FunctionMayNotThrow)) |
62 | .bind(ID: "new-expr" ), |
63 | Action: this); |
64 | } |
65 | |
66 | void UnhandledExceptionAtNewCheck::check( |
67 | const MatchFinder::MatchResult &Result) { |
68 | const auto *MatchedExpr = Result.Nodes.getNodeAs<CXXNewExpr>(ID: "new-expr" ); |
69 | if (MatchedExpr) |
70 | diag(Loc: MatchedExpr->getBeginLoc(), |
71 | Description: "missing exception handler for allocation failure at 'new'" ); |
72 | } |
73 | |
74 | } // namespace clang::tidy::bugprone |
75 | |