Skip to content

[clang-tidy] avoid false positive when overload for bugprone-return-const-ref-from-parameter #95434

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

Merged
merged 2 commits into from
Jun 14, 2024

Conversation

HerrCai0907
Copy link
Contributor

Fixes: #90274

@llvmbot
Copy link
Member

llvmbot commented Jun 13, 2024

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

Fixes: #90274


Full diff: https://github.com/llvm/llvm-project/pull/95434.diff

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp (+71-5)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp (+34)
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 &param, const float &paramf, 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

@HerrCai0907 HerrCai0907 requested a review from PiotrZSL June 14, 2024 00:29
Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few nits.

Comment on lines 35 to 39
A = A.getCanonicalType();
B = B.getCanonicalType();
A.addConst();
B.addConst();
return A == B;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A = A.getCanonicalType();
B = B.getCanonicalType();
A.addConst();
B.addConst();
return A == B;
return A.getCanonicalType().withConst() == B.getCanonicalType().withConst();

for (const Decl *Overload : LookupResult) {
if (Overload == &FD)
continue;
Overload->dumpColor();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug ?

@HerrCai0907 HerrCai0907 merged commit 3d25e5a into llvm:main Jun 14, 2024
5 of 6 checks passed
@HerrCai0907 HerrCai0907 deleted the fix/#90274 branch June 15, 2024 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] false-positive in bugprone-return-const-ref-from-parameter when overload for rvalues exists
3 participants