1 //===--- ReferenceToConstructedTemporaryCheck.cpp - clang-tidy
2 //--------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "ReferenceToConstructedTemporaryCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::readability {
17 
18 namespace {
19 
20 // Predicate structure to check if lifetime of temporary is not extended by
21 // ValueDecl pointed out by ID
22 struct NotExtendedByDeclBoundToPredicate {
operator ()clang::tidy::readability::__anone31db9cb0111::NotExtendedByDeclBoundToPredicate23   bool operator()(const internal::BoundNodesMap &Nodes) const {
24     const auto *Other = Nodes.getNodeAs<ValueDecl>(ID);
25     if (!Other)
26       return true;
27 
28     const auto *Self = Node.get<MaterializeTemporaryExpr>();
29     if (!Self)
30       return true;
31 
32     return Self->getExtendingDecl() != Other;
33   }
34 
35   StringRef ID;
36   ::clang::DynTypedNode Node;
37 };
38 
AST_MATCHER_P(MaterializeTemporaryExpr,isExtendedByDeclBoundTo,StringRef,ID)39 AST_MATCHER_P(MaterializeTemporaryExpr, isExtendedByDeclBoundTo, StringRef,
40               ID) {
41   NotExtendedByDeclBoundToPredicate Predicate{
42       ID, ::clang::DynTypedNode::create(Node)};
43   return Builder->removeBindings(Predicate);
44 }
45 
46 } // namespace
47 
isLanguageVersionSupported(const LangOptions & LangOpts) const48 bool ReferenceToConstructedTemporaryCheck::isLanguageVersionSupported(
49     const LangOptions &LangOpts) const {
50   return LangOpts.CPlusPlus;
51 }
52 
53 std::optional<TraversalKind>
getCheckTraversalKind() const54 ReferenceToConstructedTemporaryCheck::getCheckTraversalKind() const {
55   return TK_AsIs;
56 }
57 
registerMatchers(MatchFinder * Finder)58 void ReferenceToConstructedTemporaryCheck::registerMatchers(
59     MatchFinder *Finder) {
60   Finder->addMatcher(
61       varDecl(unless(isExpansionInSystemHeader()),
62               hasType(qualType(references(qualType().bind("type")))),
63               decl().bind("var"),
64               hasInitializer(expr(hasDescendant(
65                   materializeTemporaryExpr(
66                       isExtendedByDeclBoundTo("var"),
67                       has(expr(anyOf(cxxTemporaryObjectExpr(), initListExpr(),
68                                      cxxConstructExpr()),
69                                hasType(qualType(equalsBoundNode("type"))))))
70                       .bind("temporary"))))),
71       this);
72 }
73 
check(const MatchFinder::MatchResult & Result)74 void ReferenceToConstructedTemporaryCheck::check(
75     const MatchFinder::MatchResult &Result) {
76   const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var");
77   const auto *MatchedTemporary = Result.Nodes.getNodeAs<Expr>("temporary");
78 
79   diag(MatchedDecl->getLocation(),
80        "reference variable %0 extends the lifetime of a just-constructed "
81        "temporary object %1, consider changing reference to value")
82       << MatchedDecl << MatchedTemporary->getType();
83 }
84 
85 } // namespace clang::tidy::readability
86