-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[clang-tidy] fix false-positives for templates in bugprone-return-const-ref-from-parameter
#90273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang-tidy] fix false-positives for templates in bugprone-return-const-ref-from-parameter
#90273
Conversation
…nst-ref-from-parameter` 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.
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Julian Schmidt (5chmidti) ChangesIn the AST for function templates, the return will be a DeclRefExpr, Full diff: https://github.com/llvm/llvm-project/pull/90273.diff 2 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
index 8ae37d4f774d23..b3f7dd6d1c86f8 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
@@ -17,8 +17,11 @@ namespace clang::tidy::bugprone {
void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
- returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType(
- hasCanonicalType(matchers::isReferenceToConst())))))))
+ returnStmt(
+ hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType(
+ qualType(matchers::isReferenceToConst()).bind("type"))))))),
+ hasAncestor(functionDecl(hasReturnTypeLoc(
+ loc(qualType(hasCanonicalType(equalsBoundNode("type"))))))))
.bind("ret"),
this);
}
@@ -28,7 +31,8 @@ void ReturnConstRefFromParameterCheck::check(
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
diag(R->getRetValue()->getBeginLoc(),
"returning a constant reference parameter may cause a use-after-free "
- "when the parameter is constructed from a temporary");
+ "when the parameter is constructed from a temporary")
+ << R->getRetValue()->getSourceRange();
}
} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp
index a83a019ec7437d..205d93606993e1 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp
@@ -4,6 +4,15 @@ using T = int;
using TConst = int const;
using TConstRef = int const&;
+template <typename T>
+struct Wrapper { Wrapper(T); };
+
+template <typename T>
+struct Identity { using type = T; };
+
+template <typename T>
+struct ConstRef { using type = const T&; };
+
namespace invalid {
int const &f1(int const &a) { return a; }
@@ -18,8 +27,59 @@ int const &f3(TConstRef a) { return a; }
int const &f4(TConst &a) { return a; }
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter
+template <typename T>
+const T& tf1(const T &a) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter
+
+template <typename T>
+const T& itf1(const T &a) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: returning a constant reference parameter
+
+template <typename T>
+typename ConstRef<T>::type itf2(const T &a) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter
+
+template <typename T>
+typename ConstRef<T>::type itf3(typename ConstRef<T>::type a) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:72: warning: returning a constant reference parameter
+
+template <typename T>
+const T& itf4(typename ConstRef<T>::type a) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter
+
+void instantiate(const int ¶m, const float ¶mf, int &mut_param, float &mut_paramf) {
+ itf1(0);
+ itf1(param);
+ itf1(paramf);
+ itf2(0);
+ itf2(param);
+ itf2(paramf);
+ itf3<int>(0);
+ itf3<int>(param);
+ itf3<float>(paramf);
+ itf4<int>(0);
+ itf4<int>(param);
+ itf4<float>(paramf);
+}
+
+struct C {
+ const C& foo(const C&c) { return c; }
+// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter
+};
+
} // namespace invalid
+namespace false_negative_because_dependent_and_not_instantiated {
+template <typename T>
+typename ConstRef<T>::type tf2(const T &a) { return a; }
+
+template <typename T>
+typename ConstRef<T>::type tf3(typename ConstRef<T>::type a) { return a; }
+
+template <typename T>
+const T& tf4(typename ConstRef<T>::type a) { return a; }
+} // false_negative_because_dependent_and_not_instantiated
+
namespace valid {
int const &f1(int &a) { return a; }
@@ -28,4 +88,58 @@ int const &f2(int &&a) { return a; }
int f1(int const &a) { return a; }
+template <typename T>
+T tf1(T a) { return a; }
+
+template <typename T>
+T tf2(const T a) { return a; }
+
+template <typename T>
+T tf3(const T &a) { return a; }
+
+template <typename T>
+Identity<T>::type tf4(const T &a) { return a; }
+
+template <typename T>
+T itf1(T a) { return a; }
+
+template <typename T>
+T itf2(const T a) { return a; }
+
+template <typename T>
+T itf3(const T &a) { return a; }
+
+template <typename T>
+Wrapper<T> itf4(const T& a) { return a; }
+
+template <typename T>
+const T& itf5(T& a) { return a; }
+
+template <typename T>
+T itf6(T& a) { return a; }
+
+void instantiate(const int ¶m, const float ¶mf, int &mut_param, float &mut_paramf) {
+ itf1(0);
+ itf1(param);
+ itf1(paramf);
+ itf2(0);
+ itf2(param);
+ itf2(paramf);
+ itf3(0);
+ itf3(param);
+ itf3(paramf);
+ itf2(0);
+ itf2(param);
+ itf2(paramf);
+ itf3(0);
+ itf3(param);
+ itf3(paramf);
+ itf4(param);
+ itf4(paramf);
+ itf5(mut_param);
+ itf5(mut_paramf);
+ itf6(mut_param);
+ itf6(mut_paramf);
+}
+
} // namespace valid
|
@HerrCai0907 I was looking at false positives for templates regarding this check and ended up implementing the fix. |
The false positives that are fixed by implementing this fix are in lines: 98, 101, 110, 113. |
Do we add |
I don't think that's needed. |
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.