Skip to content

Commit 87d0aed

Browse files
committed
[clang-tidy] Add readability-reference-to-constructed-temporary check
Detects code where a temporary object is directly constructed by calling a constructor or using an initializer list and immediately assigned to a reference variable. Reviewed By: xgupta Differential Revision: https://reviews.llvm.org/D146368
1 parent 4210204 commit 87d0aed

File tree

8 files changed

+192
-0
lines changed

8 files changed

+192
-0
lines changed

clang-tools-extra/clang-tidy/readability/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ add_clang_library(clangTidyReadabilityModule
4141
RedundantSmartptrGetCheck.cpp
4242
RedundantStringCStrCheck.cpp
4343
RedundantStringInitCheck.cpp
44+
ReferenceToConstructedTemporaryCheck.cpp
4445
SimplifyBooleanExprCheck.cpp
4546
SimplifySubscriptExprCheck.cpp
4647
StaticAccessedThroughInstanceCheck.cpp

clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "RedundantSmartptrGetCheck.h"
4545
#include "RedundantStringCStrCheck.h"
4646
#include "RedundantStringInitCheck.h"
47+
#include "ReferenceToConstructedTemporaryCheck.h"
4748
#include "SimplifyBooleanExprCheck.h"
4849
#include "SimplifySubscriptExprCheck.h"
4950
#include "StaticAccessedThroughInstanceCheck.h"
@@ -116,6 +117,8 @@ class ReadabilityModule : public ClangTidyModule {
116117
"readability-redundant-member-init");
117118
CheckFactories.registerCheck<RedundantPreprocessorCheck>(
118119
"readability-redundant-preprocessor");
120+
CheckFactories.registerCheck<ReferenceToConstructedTemporaryCheck>(
121+
"readability-reference-to-constructed-temporary");
119122
CheckFactories.registerCheck<SimplifySubscriptExprCheck>(
120123
"readability-simplify-subscript-expr");
121124
CheckFactories.registerCheck<StaticAccessedThroughInstanceCheck>(
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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 {
23+
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+
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+
48+
bool ReferenceToConstructedTemporaryCheck::isLanguageVersionSupported(
49+
const LangOptions &LangOpts) const {
50+
return LangOpts.CPlusPlus;
51+
}
52+
53+
std::optional<TraversalKind>
54+
ReferenceToConstructedTemporaryCheck::getCheckTraversalKind() const {
55+
return TK_AsIs;
56+
}
57+
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+
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
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- ReferenceToConstructedTemporaryCheck.h - clang-tidy ----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REFERENCETOCONSTRUCTEDTEMPORARYCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REFERENCETOCONSTRUCTEDTEMPORARYCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::readability {
15+
16+
/// Detects C++ code where a reference variable is used to extend the lifetime
17+
/// of a temporary object that has just been constructed.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/reference-to-constructed-temporary.html
21+
class ReferenceToConstructedTemporaryCheck : public ClangTidyCheck {
22+
public:
23+
ReferenceToConstructedTemporaryCheck(StringRef Name,
24+
ClangTidyContext *Context)
25+
: ClangTidyCheck(Name, Context) {}
26+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
27+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
28+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
29+
std::optional<TraversalKind> getCheckTraversalKind() const override;
30+
};
31+
32+
} // namespace clang::tidy::readability
33+
34+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REFERENCETOCONSTRUCTEDTEMPORARYCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ New checks
128128
Recommends the smallest possible underlying type for an ``enum`` or ``enum``
129129
class based on the range of its enumerators.
130130

131+
- New :doc:`readability-reference-to-constructed-temporary
132+
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
133+
134+
Detects C++ code where a reference variable is used to extend the lifetime
135+
of a temporary object that has just been constructed.
136+
131137
New check aliases
132138
^^^^^^^^^^^^^^^^^
133139

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ Clang-Tidy Checks
380380
`readability-redundant-smartptr-get <readability/redundant-smartptr-get.html>`_, "Yes"
381381
`readability-redundant-string-cstr <readability/redundant-string-cstr.html>`_, "Yes"
382382
`readability-redundant-string-init <readability/redundant-string-init.html>`_, "Yes"
383+
`readability-reference-to-constructed-temporary <readability/reference-to-constructed-temporary.html>`_,
383384
`readability-simplify-boolean-expr <readability/simplify-boolean-expr.html>`_, "Yes"
384385
`readability-simplify-subscript-expr <readability/simplify-subscript-expr.html>`_, "Yes"
385386
`readability-static-accessed-through-instance <readability/static-accessed-through-instance.html>`_, "Yes"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.. title:: clang-tidy - readability-reference-to-constructed-temporary
2+
3+
readability-reference-to-constructed-temporary
4+
==============================================
5+
6+
Detects C++ code where a reference variable is used to extend the lifetime of
7+
a temporary object that has just been constructed.
8+
9+
This construction is often the result of multiple code refactorings or a lack
10+
of developer knowledge, leading to confusion or subtle bugs. In most cases,
11+
what the developer really wanted to do is create a new variable rather than
12+
extending the lifetime of a temporary object.
13+
14+
Examples of problematic code include:
15+
16+
.. code-block:: c++
17+
18+
const std::string& str("hello");
19+
20+
struct Point { int x; int y; };
21+
const Point& p = { 1, 2 };
22+
23+
In the first example, a ``const std::string&`` reference variable ``str`` is
24+
assigned a temporary object created by the ``std::string("hello")``
25+
constructor. In the second example, a ``const Point&`` reference variable ``p``
26+
is assigned an object that is constructed from an initializer list ``{ 1, 2 }``.
27+
Both of these examples extend the lifetime of the temporary object to the
28+
lifetime of the reference variable, which can make it difficult to reason about
29+
and may lead to subtle bugs or misunderstanding.
30+
31+
To avoid these issues, it is recommended to change the reference variable to a
32+
(``const``) value variable.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %check_clang_tidy -std=c++11-or-later %s readability-reference-to-constructed-temporary %t
2+
3+
struct WithConstructor
4+
{
5+
WithConstructor(int, int);
6+
};
7+
8+
struct WithoutConstructor
9+
{
10+
int a, b;
11+
};
12+
13+
void test()
14+
{
15+
// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: reference variable 'tmp1' extends the lifetime of a just-constructed temporary object 'const WithConstructor', consider changing reference to value [readability-reference-to-constructed-temporary]
16+
const WithConstructor& tmp1{1,2};
17+
18+
// CHECK-MESSAGES: :[[@LINE+1]]:30: warning: reference variable 'tmp2' extends the lifetime of a just-constructed temporary object 'const WithoutConstructor', consider changing reference to value [readability-reference-to-constructed-temporary]
19+
const WithoutConstructor& tmp2{1,2};
20+
21+
22+
// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: reference variable 'tmp3' extends the lifetime of a just-constructed temporary object 'WithConstructor', consider changing reference to value [readability-reference-to-constructed-temporary]
23+
WithConstructor&& tmp3{1,2};
24+
25+
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: reference variable 'tmp4' extends the lifetime of a just-constructed temporary object 'WithoutConstructor', consider changing reference to value [readability-reference-to-constructed-temporary]
26+
WithoutConstructor&& tmp4{1,2};
27+
28+
WithConstructor tmp5{1,2};
29+
WithoutConstructor tmp6{1,2};
30+
}

0 commit comments

Comments
 (0)