xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- UnusedRaiiCheck.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 "UnusedRaiiCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/Lex/Lexer.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang::tidy::bugprone {
16 
17 namespace {
AST_MATCHER(CXXRecordDecl,hasNonTrivialDestructor)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();
21 }
22 } // namespace
23 
registerMatchers(MatchFinder * Finder)24 void UnusedRaiiCheck::registerMatchers(MatchFinder *Finder) {
25   // Look for temporaries that are constructed in-place and immediately
26   // destroyed.
27   Finder->addMatcher(
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())))))))))
35           .bind("expr"),
36       this);
37 }
38 
39 template <typename T>
reportDiagnostic(DiagnosticBuilder D,const T * Node,SourceRange SR,bool DefaultConstruction)40 void reportDiagnostic(DiagnosticBuilder D, const T *Node, SourceRange SR,
41                       bool DefaultConstruction) {
42   const char *Replacement = " give_me_a_name";
43 
44   // If this is a default ctor we have to remove the parens or we'll introduce a
45   // most vexing parse.
46   if (DefaultConstruction) {
47     D << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(SR),
48                                       Replacement);
49     return;
50   }
51 
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
54   // written type.
55   D << FixItHint::CreateInsertion(SR.getBegin(), Replacement);
56 }
57 
check(const MatchFinder::MatchResult & Result)58 void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) {
59   const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
60 
61   // We ignore code expanded from macros to reduce the number of false
62   // positives.
63   if (E->getBeginLoc().isMacroID())
64     return;
65 
66   // Don't emit a warning for the last statement in the surrounding compound
67   // statement.
68   const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
69   const auto *LastExpr = dyn_cast<Expr>(CS->body_back());
70 
71   if (LastExpr && E == LastExpr->IgnoreUnlessSpelledInSource())
72     return;
73 
74   // Emit a warning.
75   auto D = diag(E->getBeginLoc(), "object destroyed immediately after "
76                                   "creation; did you mean to name the object?");
77 
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());
91       }
92     }
93     reportDiagnostic(D, Node, SR, DefaultConstruction);
94   }
95 }
96 
97 } // namespace clang::tidy::bugprone
98