[clang-tools-extra] [clang-tidy] avoid false positive when overload for bugprone-return-const-ref-from-parameter (PR #95434)
Congcong Cai via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 13 09:50:33 PDT 2024
https://github.com/HerrCai0907 created https://github.com/llvm/llvm-project/pull/95434
Fixes: #90274
>From ecbd726bb937361b243ea4433e8c597c8d30f857 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Fri, 14 Jun 2024 00:50:04 +0800
Subject: [PATCH] [clang-tidy] avoid false positive when overload for
bugprone-return-const-ref-from-parameter
Fixes: #90274
---
.../ReturnConstRefFromParameterCheck.cpp | 76 +++++++++++++++++--
.../return-const-ref-from-parameter.cpp | 34 +++++++++
2 files changed, 105 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
index cacba38b4a5aa..ecdcd3c2d2571 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "ReturnConstRefFromParameterCheck.h"
-#include "../utils/Matchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@@ -18,20 +17,87 @@ namespace clang::tidy::bugprone {
void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(
- hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType(
- qualType(matchers::isReferenceToConst()).bind("type"))))))),
- hasAncestor(functionDecl(hasReturnTypeLoc(
- loc(qualType(hasCanonicalType(equalsBoundNode("type"))))))))
+ hasReturnValue(declRefExpr(
+ to(parmVarDecl(hasType(hasCanonicalType(
+ qualType(lValueReferenceType(pointee(
+ qualType(isConstQualified()))))
+ .bind("type"))))
+ .bind("param")))),
+ hasAncestor(
+ functionDecl(hasReturnTypeLoc(loc(qualType(
+ hasCanonicalType(equalsBoundNode("type"))))))
+ .bind("func")))
.bind("ret"),
this);
}
+static bool isSameTypeIgnoringConst(QualType A, QualType B) {
+ A = A.getCanonicalType();
+ B = B.getCanonicalType();
+ A.addConst();
+ B.addConst();
+ return A == B;
+}
+
+static bool isSameTypeIgnoringConstRef(QualType A, QualType B) {
+ return isSameTypeIgnoringConst(A.getCanonicalType().getNonReferenceType(),
+ B.getCanonicalType().getNonReferenceType());
+}
+
+static bool hasSameParameterTypes(const FunctionDecl &FD, const FunctionDecl &O,
+ const ParmVarDecl &PD) {
+ if (FD.getNumParams() != O.getNumParams())
+ return false;
+ for (unsigned I = 0, E = FD.getNumParams(); I < E; ++I) {
+ const ParmVarDecl *DPD = FD.getParamDecl(I);
+ const QualType OPT = O.getParamDecl(I)->getType();
+ if (DPD == &PD) {
+ if (!llvm::isa<RValueReferenceType>(OPT) ||
+ !isSameTypeIgnoringConstRef(DPD->getType(), OPT))
+ return false;
+ } else {
+ if (!isSameTypeIgnoringConst(DPD->getType(), OPT))
+ return false;
+ }
+ }
+ return true;
+}
+
+static const Decl *findRVRefOverload(const FunctionDecl &FD,
+ const ParmVarDecl &PD) {
+ // Actually it would be better to do lookup in caller site.
+ // But in most of cases, overloads of LVRef and RVRef will appear together.
+ // FIXME:
+ // 1. overload in anonymous namespace
+ // 2. forward reference
+ DeclContext::lookup_result LookupResult =
+ FD.getParent()->lookup(FD.getNameInfo().getName());
+ if (LookupResult.isSingleResult()) {
+ return nullptr;
+ }
+ for (const Decl *Overload : LookupResult) {
+ if (Overload == &FD)
+ continue;
+ Overload->dumpColor();
+ if (const auto *O = dyn_cast<FunctionDecl>(Overload))
+ if (hasSameParameterTypes(FD, *O, PD))
+ return O;
+ }
+ return nullptr;
+}
+
void ReturnConstRefFromParameterCheck::check(
const MatchFinder::MatchResult &Result) {
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *PD = Result.Nodes.getNodeAs<ParmVarDecl>("param");
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
const SourceRange Range = R->getRetValue()->getSourceRange();
if (Range.isInvalid())
return;
+
+ if (findRVRefOverload(*FD, *PD) != nullptr)
+ return;
+
diag(Range.getBegin(),
"returning a constant reference parameter may cause use-after-free "
"when the parameter is constructed from a temporary")
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 ca41bdf74a107..d13c127da7c2a 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
@@ -143,3 +143,37 @@ void instantiate(const int ¶m, const float ¶mf, int &mut_param, float &m
}
} // namespace valid
+
+namespace overload {
+
+int const &overload_base(int const &a) { return a; }
+int const &overload_base(int &&a);
+
+int const &overload_ret_type(int const &a) { return a; }
+void overload_ret_type(int &&a);
+
+int const &overload_params1(int p1, int const &a) { return a; }
+int const & overload_params1(int p1, int &&a);
+
+int const &overload_params2(int p1, int const &a, int p2) { return a; }
+int const &overload_params2(int p1, int &&a, int p2);
+
+int const &overload_params3(T p1, int const &a, int p2) { return a; }
+int const &overload_params3(int p1, int &&a, T p2);
+
+int const &overload_params_const(int p1, int const &a, int const p2) { return a; }
+int const &overload_params_const(int const p1, int &&a, int p2);
+
+int const &overload_params_difference1(int p1, int const &a, int p2) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:79: warning: returning a constant reference parameter
+int const &overload_params_difference1(long p1, int &&a, int p2);
+
+int const &overload_params_difference2(int p1, int const &a, int p2) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:79: warning: returning a constant reference parameter
+int const &overload_params_difference2(int p1, int &&a, long p2);
+
+int const &overload_params_difference3(int p1, int const &a, int p2) { return a; }
+// CHECK-MESSAGES: :[[@LINE-1]]:79: warning: returning a constant reference parameter
+int const &overload_params_difference3(int p1, long &&a, int p2);
+
+} // namespace overload
More information about the cfe-commits
mailing list