1 //===--- UnusedRaiiCheck.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 "UnusedRaiiCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/Lex/Lexer.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
18 AST_MATCHER(CXXRecordDecl
, hasNonTrivialDestructor
) {
19 // TODO: If the dtor is there but empty we don't want to warn either.
20 return Node
.hasDefinition() && Node
.hasNonTrivialDestructor();
24 void UnusedRaiiCheck::registerMatchers(MatchFinder
*Finder
) {
25 // Look for temporaries that are constructed in-place and immediately
28 mapAnyOf(cxxConstructExpr
, cxxUnresolvedConstructExpr
)
29 .with(hasParent(compoundStmt().bind("compound")),
30 anyOf(hasType(hasCanonicalType(recordType(hasDeclaration(
31 cxxRecordDecl(hasNonTrivialDestructor()))))),
32 hasType(hasCanonicalType(templateSpecializationType(
33 hasDeclaration(classTemplateDecl(has(
34 cxxRecordDecl(hasNonTrivialDestructor())))))))))
40 void reportDiagnostic(DiagnosticBuilder D
, const T
*Node
, SourceRange SR
,
41 bool DefaultConstruction
) {
42 const char *Replacement
= " give_me_a_name";
44 // If this is a default ctor we have to remove the parens or we'll introduce a
46 if (DefaultConstruction
) {
47 D
<< FixItHint::CreateReplacement(CharSourceRange::getTokenRange(SR
),
52 // Otherwise just suggest adding a name. To find the place to insert the name
53 // find the first TypeLoc in the children of E, which always points to the
55 D
<< FixItHint::CreateInsertion(SR
.getBegin(), Replacement
);
58 void UnusedRaiiCheck::check(const MatchFinder::MatchResult
&Result
) {
59 const auto *E
= Result
.Nodes
.getNodeAs
<Expr
>("expr");
61 // We ignore code expanded from macros to reduce the number of false
63 if (E
->getBeginLoc().isMacroID())
66 // Don't emit a warning for the last statement in the surrounding compound
68 const auto *CS
= Result
.Nodes
.getNodeAs
<CompoundStmt
>("compound");
69 const auto *LastExpr
= dyn_cast
<Expr
>(CS
->body_back());
71 if (LastExpr
&& E
== LastExpr
->IgnoreUnlessSpelledInSource())
75 auto D
= diag(E
->getBeginLoc(), "object destroyed immediately after "
76 "creation; did you mean to name the object?");
78 if (const auto *Node
= dyn_cast
<CXXConstructExpr
>(E
))
79 reportDiagnostic(D
, Node
, Node
->getParenOrBraceRange(),
80 Node
->getNumArgs() == 0 ||
81 isa
<CXXDefaultArgExpr
>(Node
->getArg(0)));
82 if (const auto *Node
= dyn_cast
<CXXUnresolvedConstructExpr
>(E
)) {
83 auto SR
= SourceRange(Node
->getLParenLoc(), Node
->getRParenLoc());
84 auto DefaultConstruction
= Node
->getNumArgs() == 0;
85 if (!DefaultConstruction
) {
86 auto *FirstArg
= Node
->getArg(0);
87 DefaultConstruction
= isa
<CXXDefaultArgExpr
>(FirstArg
);
88 if (auto *ILE
= dyn_cast
<InitListExpr
>(FirstArg
)) {
89 DefaultConstruction
= ILE
->getNumInits() == 0;
90 SR
= SourceRange(ILE
->getLBraceLoc(), ILE
->getRBraceLoc());
93 reportDiagnostic(D
, Node
, SR
, DefaultConstruction
);
97 } // namespace clang::tidy::bugprone