187d0aedaSPiotr Zegar //===--- ReferenceToConstructedTemporaryCheck.cpp - clang-tidy
287d0aedaSPiotr Zegar //--------------===//
387d0aedaSPiotr Zegar //
487d0aedaSPiotr Zegar // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
587d0aedaSPiotr Zegar // See https://llvm.org/LICENSE.txt for license information.
687d0aedaSPiotr Zegar // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
787d0aedaSPiotr Zegar //
887d0aedaSPiotr Zegar //===----------------------------------------------------------------------===//
987d0aedaSPiotr Zegar 
1087d0aedaSPiotr Zegar #include "ReferenceToConstructedTemporaryCheck.h"
1187d0aedaSPiotr Zegar #include "clang/AST/ASTContext.h"
1287d0aedaSPiotr Zegar #include "clang/ASTMatchers/ASTMatchFinder.h"
1387d0aedaSPiotr Zegar 
1487d0aedaSPiotr Zegar using namespace clang::ast_matchers;
1587d0aedaSPiotr Zegar 
1687d0aedaSPiotr Zegar namespace clang::tidy::readability {
1787d0aedaSPiotr Zegar 
1887d0aedaSPiotr Zegar namespace {
1987d0aedaSPiotr Zegar 
2087d0aedaSPiotr Zegar // Predicate structure to check if lifetime of temporary is not extended by
2187d0aedaSPiotr Zegar // ValueDecl pointed out by ID
2287d0aedaSPiotr Zegar struct NotExtendedByDeclBoundToPredicate {
operator ()clang::tidy::readability::__anone31db9cb0111::NotExtendedByDeclBoundToPredicate23*11a411a4SPiotr Zegar   bool operator()(const internal::BoundNodesMap &Nodes) const {
2487d0aedaSPiotr Zegar     const auto *Other = Nodes.getNodeAs<ValueDecl>(ID);
2587d0aedaSPiotr Zegar     if (!Other)
2687d0aedaSPiotr Zegar       return true;
2787d0aedaSPiotr Zegar 
2887d0aedaSPiotr Zegar     const auto *Self = Node.get<MaterializeTemporaryExpr>();
2987d0aedaSPiotr Zegar     if (!Self)
3087d0aedaSPiotr Zegar       return true;
3187d0aedaSPiotr Zegar 
3287d0aedaSPiotr Zegar     return Self->getExtendingDecl() != Other;
3387d0aedaSPiotr Zegar   }
3487d0aedaSPiotr Zegar 
3587d0aedaSPiotr Zegar   StringRef ID;
3687d0aedaSPiotr Zegar   ::clang::DynTypedNode Node;
3787d0aedaSPiotr Zegar };
3887d0aedaSPiotr Zegar 
AST_MATCHER_P(MaterializeTemporaryExpr,isExtendedByDeclBoundTo,StringRef,ID)3987d0aedaSPiotr Zegar AST_MATCHER_P(MaterializeTemporaryExpr, isExtendedByDeclBoundTo, StringRef,
4087d0aedaSPiotr Zegar               ID) {
4187d0aedaSPiotr Zegar   NotExtendedByDeclBoundToPredicate Predicate{
4287d0aedaSPiotr Zegar       ID, ::clang::DynTypedNode::create(Node)};
4387d0aedaSPiotr Zegar   return Builder->removeBindings(Predicate);
4487d0aedaSPiotr Zegar }
4587d0aedaSPiotr Zegar 
4687d0aedaSPiotr Zegar } // namespace
4787d0aedaSPiotr Zegar 
isLanguageVersionSupported(const LangOptions & LangOpts) const4887d0aedaSPiotr Zegar bool ReferenceToConstructedTemporaryCheck::isLanguageVersionSupported(
4987d0aedaSPiotr Zegar     const LangOptions &LangOpts) const {
5087d0aedaSPiotr Zegar   return LangOpts.CPlusPlus;
5187d0aedaSPiotr Zegar }
5287d0aedaSPiotr Zegar 
5387d0aedaSPiotr Zegar std::optional<TraversalKind>
getCheckTraversalKind() const5487d0aedaSPiotr Zegar ReferenceToConstructedTemporaryCheck::getCheckTraversalKind() const {
5587d0aedaSPiotr Zegar   return TK_AsIs;
5687d0aedaSPiotr Zegar }
5787d0aedaSPiotr Zegar 
registerMatchers(MatchFinder * Finder)5887d0aedaSPiotr Zegar void ReferenceToConstructedTemporaryCheck::registerMatchers(
5987d0aedaSPiotr Zegar     MatchFinder *Finder) {
6087d0aedaSPiotr Zegar   Finder->addMatcher(
6187d0aedaSPiotr Zegar       varDecl(unless(isExpansionInSystemHeader()),
6287d0aedaSPiotr Zegar               hasType(qualType(references(qualType().bind("type")))),
6387d0aedaSPiotr Zegar               decl().bind("var"),
6487d0aedaSPiotr Zegar               hasInitializer(expr(hasDescendant(
6587d0aedaSPiotr Zegar                   materializeTemporaryExpr(
6687d0aedaSPiotr Zegar                       isExtendedByDeclBoundTo("var"),
6787d0aedaSPiotr Zegar                       has(expr(anyOf(cxxTemporaryObjectExpr(), initListExpr(),
6887d0aedaSPiotr Zegar                                      cxxConstructExpr()),
6987d0aedaSPiotr Zegar                                hasType(qualType(equalsBoundNode("type"))))))
7087d0aedaSPiotr Zegar                       .bind("temporary"))))),
7187d0aedaSPiotr Zegar       this);
7287d0aedaSPiotr Zegar }
7387d0aedaSPiotr Zegar 
check(const MatchFinder::MatchResult & Result)7487d0aedaSPiotr Zegar void ReferenceToConstructedTemporaryCheck::check(
7587d0aedaSPiotr Zegar     const MatchFinder::MatchResult &Result) {
7687d0aedaSPiotr Zegar   const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var");
7787d0aedaSPiotr Zegar   const auto *MatchedTemporary = Result.Nodes.getNodeAs<Expr>("temporary");
7887d0aedaSPiotr Zegar 
7987d0aedaSPiotr Zegar   diag(MatchedDecl->getLocation(),
8087d0aedaSPiotr Zegar        "reference variable %0 extends the lifetime of a just-constructed "
8187d0aedaSPiotr Zegar        "temporary object %1, consider changing reference to value")
8287d0aedaSPiotr Zegar       << MatchedDecl << MatchedTemporary->getType();
8387d0aedaSPiotr Zegar }
8487d0aedaSPiotr Zegar 
8587d0aedaSPiotr Zegar } // namespace clang::tidy::readability
86