1 //===--- MultipleNewInOneExpressionCheck.cpp - clang-tidy------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "MultipleNewInOneExpressionCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
20 // Determine if the result of an expression is "stored" in some way.
21 // It is true if the value is stored into a variable or used as initialization
22 // or passed to a function or constructor.
23 // For this use case compound assignments are not counted as a "store" (the 'E'
24 // expression should have pointer type).
25 bool isExprValueStored(const Expr
*E
, ASTContext
&C
) {
26 E
= E
->IgnoreParenCasts();
27 // Get first non-paren, non-cast parent.
28 ParentMapContext
&PMap
= C
.getParentMapContext();
29 DynTypedNodeList P
= PMap
.getParents(*E
);
32 const Expr
*ParentE
= nullptr;
33 while ((ParentE
= P
[0].get
<Expr
>()) && ParentE
->IgnoreParenCasts() == E
) {
34 P
= PMap
.getParents(P
[0]);
39 if (const auto *ParentVarD
= P
[0].get
<VarDecl
>())
40 return ParentVarD
->getInit()->IgnoreParenCasts() == E
;
45 if (const auto *BinOp
= dyn_cast
<BinaryOperator
>(ParentE
))
46 return BinOp
->getOpcode() == BO_Assign
&&
47 BinOp
->getRHS()->IgnoreParenCasts() == E
;
49 return isa
<CallExpr
, CXXConstructExpr
>(ParentE
);
54 AST_MATCHER_P(CXXTryStmt
, hasHandlerFor
,
55 ast_matchers::internal::Matcher
<QualType
>, InnerMatcher
) {
56 for (unsigned NH
= Node
.getNumHandlers(), I
= 0; I
< NH
; ++I
) {
57 const CXXCatchStmt
*CatchS
= Node
.getHandler(I
);
58 // Check for generic catch handler (match anything).
59 if (CatchS
->getCaughtType().isNull())
61 ast_matchers::internal::BoundNodesTreeBuilder
Result(*Builder
);
62 if (InnerMatcher
.matches(CatchS
->getCaughtType(), Finder
, &Result
)) {
63 *Builder
= std::move(Result
);
70 AST_MATCHER(CXXNewExpr
, mayThrow
) {
71 FunctionDecl
*OperatorNew
= Node
.getOperatorNew();
74 return !OperatorNew
->getType()->castAs
<FunctionProtoType
>()->isNothrow();
77 void MultipleNewInOneExpressionCheck::registerMatchers(MatchFinder
*Finder
) {
79 recordType(hasDeclaration(cxxRecordDecl(hasName("::std::bad_alloc"))));
81 recordType(hasDeclaration(cxxRecordDecl(hasName("::std::exception"))));
82 auto BadAllocReferenceType
= referenceType(pointee(BadAllocType
));
83 auto ExceptionReferenceType
= referenceType(pointee(ExceptionType
));
85 auto CatchBadAllocType
=
86 qualType(hasCanonicalType(anyOf(BadAllocType
, BadAllocReferenceType
,
87 ExceptionType
, ExceptionReferenceType
)));
88 auto BadAllocCatchingTryBlock
= cxxTryStmt(hasHandlerFor(CatchBadAllocType
));
90 auto NewExprMayThrow
= cxxNewExpr(mayThrow());
91 auto HasNewExpr1
= expr(anyOf(NewExprMayThrow
.bind("new1"),
92 hasDescendant(NewExprMayThrow
.bind("new1"))));
93 auto HasNewExpr2
= expr(anyOf(NewExprMayThrow
.bind("new2"),
94 hasDescendant(NewExprMayThrow
.bind("new2"))));
99 expr(HasNewExpr1
).bind("arg1")),
101 expr(HasNewExpr2
, unless(equalsBoundNode("arg1"))).bind("arg2")),
102 hasAncestor(BadAllocCatchingTryBlock
)),
107 expr(HasNewExpr1
).bind("arg1")),
109 expr(HasNewExpr2
, unless(equalsBoundNode("arg1"))).bind("arg2")),
110 unless(isListInitialization()),
111 hasAncestor(BadAllocCatchingTryBlock
)),
113 Finder
->addMatcher(binaryOperator(hasLHS(HasNewExpr1
), hasRHS(HasNewExpr2
),
114 unless(hasAnyOperatorName("&&", "||", ",")),
115 hasAncestor(BadAllocCatchingTryBlock
)),
118 cxxNewExpr(mayThrow(),
119 hasDescendant(NewExprMayThrow
.bind("new2_in_new1")),
120 hasAncestor(BadAllocCatchingTryBlock
))
125 void MultipleNewInOneExpressionCheck::check(
126 const MatchFinder::MatchResult
&Result
) {
127 const auto *NewExpr1
= Result
.Nodes
.getNodeAs
<CXXNewExpr
>("new1");
128 const auto *NewExpr2
= Result
.Nodes
.getNodeAs
<CXXNewExpr
>("new2");
129 const auto *NewExpr2InNewExpr1
=
130 Result
.Nodes
.getNodeAs
<CXXNewExpr
>("new2_in_new1");
132 NewExpr2
= NewExpr2InNewExpr1
;
133 assert(NewExpr1
&& NewExpr2
&& "Bound nodes not found.");
135 // No warning if both allocations are not stored.
136 // The value may be intentionally not stored (no deallocations needed or
137 // self-destructing object).
138 if (!isExprValueStored(NewExpr1
, *Result
.Context
) &&
139 !isExprValueStored(NewExpr2
, *Result
.Context
))
142 // In C++17 sequencing of a 'new' inside constructor arguments of another
143 // 'new' is fixed. Still a leak can happen if the returned value from the
144 // first 'new' is not saved (yet) and the second fails.
145 if (getLangOpts().CPlusPlus17
&& NewExpr2InNewExpr1
)
146 diag(NewExpr1
->getBeginLoc(),
147 "memory allocation may leak if an other allocation is sequenced after "
148 "it and throws an exception")
149 << NewExpr1
->getSourceRange() << NewExpr2
->getSourceRange();
151 diag(NewExpr1
->getBeginLoc(),
152 "memory allocation may leak if an other allocation is sequenced after "
153 "it and throws an exception; order of these allocations is undefined")
154 << NewExpr1
->getSourceRange() << NewExpr2
->getSourceRange();
157 } // namespace clang::tidy::bugprone