Skip to content

Commit 9aad38b

Browse files
authored
[clang-tidy] fix false-positives for templates in bugprone-return-const-ref-from-parameter (#90273)
In the AST for function templates, the return will be a DeclRefExpr, even if the return type differs from that of the returned variable. Protect against false-positives by constraining the canonical return type to be that of the parameter. Also streams the source range of the returned expression into the diagnostic.
1 parent 77d8c38 commit 9aad38b

File tree

2 files changed

+122
-4
lines changed

2 files changed

+122
-4
lines changed

clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ namespace clang::tidy::bugprone {
1717

1818
void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
1919
Finder->addMatcher(
20-
returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType(
21-
hasCanonicalType(matchers::isReferenceToConst())))))))
20+
returnStmt(
21+
hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType(
22+
qualType(matchers::isReferenceToConst()).bind("type"))))))),
23+
hasAncestor(functionDecl(hasReturnTypeLoc(
24+
loc(qualType(hasCanonicalType(equalsBoundNode("type"))))))))
2225
.bind("ret"),
2326
this);
2427
}
@@ -28,7 +31,8 @@ void ReturnConstRefFromParameterCheck::check(
2831
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
2932
diag(R->getRetValue()->getBeginLoc(),
3033
"returning a constant reference parameter may cause a use-after-free "
31-
"when the parameter is constructed from a temporary");
34+
"when the parameter is constructed from a temporary")
35+
<< R->getRetValue()->getSourceRange();
3236
}
3337

3438
} // namespace clang::tidy::bugprone

clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t
1+
// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t -- -- -fno-delayed-template-parsing
22

33
using T = int;
44
using TConst = int const;
55
using TConstRef = int const&;
66

7+
template <typename T>
8+
struct Wrapper { Wrapper(T); };
9+
10+
template <typename T>
11+
struct Identity { using type = T; };
12+
13+
template <typename T>
14+
struct ConstRef { using type = const T&; };
15+
716
namespace invalid {
817

918
int const &f1(int const &a) { return a; }
@@ -18,8 +27,59 @@ int const &f3(TConstRef a) { return a; }
1827
int const &f4(TConst &a) { return a; }
1928
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter
2029

30+
template <typename T>
31+
const T& tf1(const T &a) { return a; }
32+
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter
33+
34+
template <typename T>
35+
const T& itf1(const T &a) { return a; }
36+
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: returning a constant reference parameter
37+
38+
template <typename T>
39+
typename ConstRef<T>::type itf2(const T &a) { return a; }
40+
// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter
41+
42+
template <typename T>
43+
typename ConstRef<T>::type itf3(typename ConstRef<T>::type a) { return a; }
44+
// CHECK-MESSAGES: :[[@LINE-1]]:72: warning: returning a constant reference parameter
45+
46+
template <typename T>
47+
const T& itf4(typename ConstRef<T>::type a) { return a; }
48+
// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter
49+
50+
void instantiate(const int &param, const float &paramf, int &mut_param, float &mut_paramf) {
51+
itf1(0);
52+
itf1(param);
53+
itf1(paramf);
54+
itf2(0);
55+
itf2(param);
56+
itf2(paramf);
57+
itf3<int>(0);
58+
itf3<int>(param);
59+
itf3<float>(paramf);
60+
itf4<int>(0);
61+
itf4<int>(param);
62+
itf4<float>(paramf);
63+
}
64+
65+
struct C {
66+
const C& foo(const C&c) { return c; }
67+
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter
68+
};
69+
2170
} // namespace invalid
2271

72+
namespace false_negative_because_dependent_and_not_instantiated {
73+
template <typename T>
74+
typename ConstRef<T>::type tf2(const T &a) { return a; }
75+
76+
template <typename T>
77+
typename ConstRef<T>::type tf3(typename ConstRef<T>::type a) { return a; }
78+
79+
template <typename T>
80+
const T& tf4(typename ConstRef<T>::type a) { return a; }
81+
} // false_negative_because_dependent_and_not_instantiated
82+
2383
namespace valid {
2484

2585
int const &f1(int &a) { return a; }
@@ -28,4 +88,58 @@ int const &f2(int &&a) { return a; }
2888

2989
int f1(int const &a) { return a; }
3090

91+
template <typename T>
92+
T tf1(T a) { return a; }
93+
94+
template <typename T>
95+
T tf2(const T a) { return a; }
96+
97+
template <typename T>
98+
T tf3(const T &a) { return a; }
99+
100+
template <typename T>
101+
Identity<T>::type tf4(const T &a) { return a; }
102+
103+
template <typename T>
104+
T itf1(T a) { return a; }
105+
106+
template <typename T>
107+
T itf2(const T a) { return a; }
108+
109+
template <typename T>
110+
T itf3(const T &a) { return a; }
111+
112+
template <typename T>
113+
Wrapper<T> itf4(const T& a) { return a; }
114+
115+
template <typename T>
116+
const T& itf5(T& a) { return a; }
117+
118+
template <typename T>
119+
T itf6(T& a) { return a; }
120+
121+
void instantiate(const int &param, const float &paramf, int &mut_param, float &mut_paramf) {
122+
itf1(0);
123+
itf1(param);
124+
itf1(paramf);
125+
itf2(0);
126+
itf2(param);
127+
itf2(paramf);
128+
itf3(0);
129+
itf3(param);
130+
itf3(paramf);
131+
itf2(0);
132+
itf2(param);
133+
itf2(paramf);
134+
itf3(0);
135+
itf3(param);
136+
itf3(paramf);
137+
itf4(param);
138+
itf4(paramf);
139+
itf5(mut_param);
140+
itf5(mut_paramf);
141+
itf6(mut_param);
142+
itf6(mut_paramf);
143+
}
144+
31145
} // namespace valid

0 commit comments

Comments
 (0)