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