blob: e1acfe97c3bbcae46c2887363d45aee513cbb02d [file] [log] [blame]
Alexander Kornienko5b982e52015-03-09 11:48:54 +00001//===--- UnusedRAIICheck.cpp - clang-tidy ---------------------------------===//
Benjamin Kramerda3658e2014-07-23 11:49:46 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Alexander Kornienko5b982e52015-03-09 11:48:54 +000010#include "UnusedRAIICheck.h"
Benjamin Kramerda3658e2014-07-23 11:49:46 +000011#include "clang/AST/ASTContext.h"
12#include "clang/Lex/Lexer.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
Etienne Bergeron456177b2016-05-02 18:00:29 +000017namespace tidy {
18namespace misc {
19
Alexander Kornienko50d7f4612015-06-17 13:11:37 +000020namespace {
Alexander Kornienkobdaa6812015-05-12 12:17:20 +000021AST_MATCHER(CXXRecordDecl, hasNonTrivialDestructor) {
Benjamin Kramerda3658e2014-07-23 11:49:46 +000022 // TODO: If the dtor is there but empty we don't want to warn either.
Alexander Kornienkobdaa6812015-05-12 12:17:20 +000023 return Node.hasDefinition() && Node.hasNonTrivialDestructor();
Benjamin Kramerda3658e2014-07-23 11:49:46 +000024}
Alexander Kornienko50d7f4612015-06-17 13:11:37 +000025} // namespace
Benjamin Kramerda3658e2014-07-23 11:49:46 +000026
Benjamin Kramerda3658e2014-07-23 11:49:46 +000027void UnusedRAIICheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman327e97b2015-08-28 19:27:19 +000028 // Only register the matchers for C++; the functionality currently does not
29 // provide any benefit to other languages, despite being benign.
Aaron Ballmanbf891092015-08-31 15:28:57 +000030 if (!getLangOpts().CPlusPlus)
31 return;
32
33 // Look for temporaries that are constructed in-place and immediately
34 // destroyed. Look for temporaries created by a functional cast but not for
35 // those returned from a call.
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000036 auto BindTemp =
37 cxxBindTemporaryExpr(unless(has(ignoringParenImpCasts(callExpr()))))
38 .bind("temp");
Aaron Ballmanbf891092015-08-31 15:28:57 +000039 Finder->addMatcher(
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000040 exprWithCleanups(unless(isInTemplateInstantiation()),
41 hasParent(compoundStmt().bind("compound")),
42 hasType(cxxRecordDecl(hasNonTrivialDestructor())),
43 anyOf(has(ignoringParenImpCasts(BindTemp)),
44 has(ignoringParenImpCasts(cxxFunctionalCastExpr(
45 has(ignoringParenImpCasts(BindTemp)))))))
Aaron Ballmanbf891092015-08-31 15:28:57 +000046 .bind("expr"),
47 this);
Benjamin Kramerda3658e2014-07-23 11:49:46 +000048}
49
50void UnusedRAIICheck::check(const MatchFinder::MatchResult &Result) {
Alexander Kornienko9f58fe02016-12-13 16:19:19 +000051 const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
Benjamin Kramerda3658e2014-07-23 11:49:46 +000052
53 // We ignore code expanded from macros to reduce the number of false
54 // positives.
55 if (E->getLocStart().isMacroID())
56 return;
57
58 // Don't emit a warning for the last statement in the surrounding compund
59 // statement.
Alexander Kornienko9f58fe02016-12-13 16:19:19 +000060 const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
Benjamin Kramerda3658e2014-07-23 11:49:46 +000061 if (E == CS->body_back())
62 return;
63
64 // Emit a warning.
65 auto D = diag(E->getLocStart(), "object destroyed immediately after "
66 "creation; did you mean to name the object?");
67 const char *Replacement = " give_me_a_name";
68
69 // If this is a default ctor we have to remove the parens or we'll introduce a
70 // most vexing parse.
Alexander Kornienko9f58fe02016-12-13 16:19:19 +000071 const auto *BTE = Result.Nodes.getNodeAs<CXXBindTemporaryExpr>("temp");
Benjamin Kramerda3658e2014-07-23 11:49:46 +000072 if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr()))
73 if (TOE->getNumArgs() == 0) {
74 D << FixItHint::CreateReplacement(
75 CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()),
76 Replacement);
77 return;
78 }
79
Benjamin Kramer4ff1ffa2014-07-23 11:50:54 +000080 // Otherwise just suggest adding a name. To find the place to insert the name
81 // find the first TypeLoc in the children of E, which always points to the
82 // written type.
Benjamin Kramerc1730e92014-07-24 08:34:42 +000083 auto Matches =
84 match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context);
85 const auto *TL = selectFirst<TypeLoc>("t", Matches);
Benjamin Kramerda3658e2014-07-23 11:49:46 +000086 D << FixItHint::CreateInsertion(
87 Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager,
Gabor Horvathafad84c2016-09-24 02:13:45 +000088 getLangOpts()),
Benjamin Kramerda3658e2014-07-23 11:49:46 +000089 Replacement);
90}
91
Alexander Kornienko2b3124202015-03-02 12:25:03 +000092} // namespace misc
Benjamin Kramerda3658e2014-07-23 11:49:46 +000093} // namespace tidy
94} // namespace clang