aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/readability/ReferenceToConstructedTemporaryCheck.cpp
blob: 587ae8ea3058028f2d5f90f56bfcea38ab77ccc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//===--- ReferenceToConstructedTemporaryCheck.cpp - clang-tidy
//--------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "ReferenceToConstructedTemporaryCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

namespace {

// Predicate structure to check if lifetime of temporary is not extended by
// ValueDecl pointed out by ID
struct NotExtendedByDeclBoundToPredicate {
  bool operator()(const internal::BoundNodesMap &Nodes) const {
    const auto *Other = Nodes.getNodeAs<ValueDecl>(ID);
    if (!Other)
      return true;

    const auto *Self = Node.get<MaterializeTemporaryExpr>();
    if (!Self)
      return true;

    return Self->getExtendingDecl() != Other;
  }

  StringRef ID;
  ::clang::DynTypedNode Node;
};

AST_MATCHER_P(MaterializeTemporaryExpr, isExtendedByDeclBoundTo, StringRef,
              ID) {
  NotExtendedByDeclBoundToPredicate Predicate{
      ID, ::clang::DynTypedNode::create(Node)};
  return Builder->removeBindings(Predicate);
}

} // namespace

bool ReferenceToConstructedTemporaryCheck::isLanguageVersionSupported(
    const LangOptions &LangOpts) const {
  return LangOpts.CPlusPlus;
}

std::optional<TraversalKind>
ReferenceToConstructedTemporaryCheck::getCheckTraversalKind() const {
  return TK_AsIs;
}

void ReferenceToConstructedTemporaryCheck::registerMatchers(
    MatchFinder *Finder) {
  Finder->addMatcher(
      varDecl(unless(isExpansionInSystemHeader()),
              hasType(qualType(references(qualType().bind("type")))),
              decl().bind("var"),
              hasInitializer(expr(hasDescendant(
                  materializeTemporaryExpr(
                      isExtendedByDeclBoundTo("var"),
                      has(expr(anyOf(cxxTemporaryObjectExpr(), initListExpr(),
                                     cxxConstructExpr()),
                               hasType(qualType(equalsBoundNode("type"))))))
                      .bind("temporary"))))),
      this);
}

void ReferenceToConstructedTemporaryCheck::check(
    const MatchFinder::MatchResult &Result) {
  const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var");
  const auto *MatchedTemporary = Result.Nodes.getNodeAs<Expr>("temporary");

  diag(MatchedDecl->getLocation(),
       "reference variable %0 extends the lifetime of a just-constructed "
       "temporary object %1, consider changing reference to value")
      << MatchedDecl << MatchedTemporary->getType();
}

} // namespace clang::tidy::readability