@@ -25,25 +25,37 @@ AST_MATCHER(CXXRecordDecl, hasNonTrivialDestructor) {
25
25
26
26
void UnusedRaiiCheck::registerMatchers (MatchFinder *Finder) {
27
27
// Look for temporaries that are constructed in-place and immediately
28
- // destroyed. Look for temporaries created by a functional cast but not for
29
- // those returned from a call.
30
- auto BindTemp = cxxBindTemporaryExpr (
31
- unless (has (ignoringParenImpCasts (callExpr ()))),
32
- unless (has (ignoringParenImpCasts (objcMessageExpr ()))))
33
- .bind (" temp" );
28
+ // destroyed.
34
29
Finder->addMatcher (
35
- traverse (TK_AsIs,
36
- exprWithCleanups (
37
- unless (isInTemplateInstantiation ()),
38
- hasParent (compoundStmt ().bind (" compound" )),
39
- hasType (cxxRecordDecl (hasNonTrivialDestructor ())),
40
- anyOf (has (ignoringParenImpCasts (BindTemp)),
41
- has (ignoringParenImpCasts (cxxFunctionalCastExpr (
42
- has (ignoringParenImpCasts (BindTemp)))))))
43
- .bind (" expr" )),
30
+ mapAnyOf (cxxConstructExpr, cxxUnresolvedConstructExpr)
31
+ .with (hasParent (compoundStmt ().bind (" compound" )),
32
+ anyOf (hasType (cxxRecordDecl (hasNonTrivialDestructor ())),
33
+ hasType (templateSpecializationType (
34
+ hasDeclaration (classTemplateDecl (has (
35
+ cxxRecordDecl (hasNonTrivialDestructor ()))))))))
36
+ .bind (" expr" ),
44
37
this );
45
38
}
46
39
40
+ template <typename T>
41
+ void reportDiagnostic (DiagnosticBuilder D, const T *Node, SourceRange SR,
42
+ bool DefaultConstruction) {
43
+ const char *Replacement = " give_me_a_name" ;
44
+
45
+ // If this is a default ctor we have to remove the parens or we'll introduce a
46
+ // most vexing parse.
47
+ if (DefaultConstruction) {
48
+ D << FixItHint::CreateReplacement (CharSourceRange::getTokenRange (SR),
49
+ Replacement);
50
+ return ;
51
+ }
52
+
53
+ // Otherwise just suggest adding a name. To find the place to insert the name
54
+ // find the first TypeLoc in the children of E, which always points to the
55
+ // written type.
56
+ D << FixItHint::CreateInsertion (SR.getBegin (), Replacement);
57
+ }
58
+
47
59
void UnusedRaiiCheck::check (const MatchFinder::MatchResult &Result) {
48
60
const auto *E = Result.Nodes .getNodeAs <Expr>(" expr" );
49
61
@@ -55,35 +67,32 @@ void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) {
55
67
// Don't emit a warning for the last statement in the surrounding compound
56
68
// statement.
57
69
const auto *CS = Result.Nodes .getNodeAs <CompoundStmt>(" compound" );
58
- if (E == CS->body_back ())
70
+ const auto *LastExpr = dyn_cast<Expr>(CS->body_back ());
71
+
72
+ if (LastExpr && E == LastExpr->IgnoreUnlessSpelledInSource ())
59
73
return ;
60
74
61
75
// Emit a warning.
62
76
auto D = diag (E->getBeginLoc (), " object destroyed immediately after "
63
77
" creation; did you mean to name the object?" );
64
- const char *Replacement = " give_me_a_name" ;
65
78
66
- // If this is a default ctor we have to remove the parens or we'll introduce a
67
- // most vexing parse.
68
- const auto *BTE = Result.Nodes .getNodeAs <CXXBindTemporaryExpr>(" temp" );
69
- if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr ()))
70
- if (TOE->getNumArgs () == 0 ) {
71
- D << FixItHint::CreateReplacement (
72
- CharSourceRange::getTokenRange (TOE->getParenOrBraceRange ()),
73
- Replacement);
74
- return ;
79
+ if (const auto *Node = dyn_cast<CXXConstructExpr>(E))
80
+ reportDiagnostic (D, Node, Node->getParenOrBraceRange (),
81
+ Node->getNumArgs () == 0 ||
82
+ isa<CXXDefaultArgExpr>(Node->getArg (0 )));
83
+ if (const auto *Node = dyn_cast<CXXUnresolvedConstructExpr>(E)) {
84
+ auto SR = SourceRange (Node->getLParenLoc (), Node->getRParenLoc ());
85
+ auto DefaultConstruction = Node->getNumArgs () == 0 ;
86
+ if (!DefaultConstruction) {
87
+ auto FirstArg = Node->getArg (0 );
88
+ DefaultConstruction = isa<CXXDefaultArgExpr>(FirstArg);
89
+ if (auto ILE = dyn_cast<InitListExpr>(FirstArg)) {
90
+ DefaultConstruction = ILE->getNumInits () == 0 ;
91
+ SR = SourceRange (ILE->getLBraceLoc (), ILE->getRBraceLoc ());
92
+ }
75
93
}
76
-
77
- // Otherwise just suggest adding a name. To find the place to insert the name
78
- // find the first TypeLoc in the children of E, which always points to the
79
- // written type.
80
- auto Matches =
81
- match (expr (hasDescendant (typeLoc ().bind (" t" ))), *E, *Result.Context );
82
- if (const auto *TL = selectFirst<TypeLoc>(" t" , Matches))
83
- D << FixItHint::CreateInsertion (
84
- Lexer::getLocForEndOfToken (TL->getEndLoc (), 0 , *Result.SourceManager ,
85
- getLangOpts ()),
86
- Replacement);
94
+ reportDiagnostic (D, Node, SR, DefaultConstruction);
95
+ }
87
96
}
88
97
89
98
} // namespace bugprone
0 commit comments