[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / ReturnConstRefFromParameterCheck.cpp
blob7cc4fe519d3a64e32717024c2396a6ac27d4fe90
1 //===--- ReturnConstRefFromParameterCheck.cpp - clang-tidy ----------------===//
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 //===----------------------------------------------------------------------===//
9 #include "ReturnConstRefFromParameterCheck.h"
10 #include "clang/AST/Expr.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
14 using namespace clang::ast_matchers;
16 namespace clang::tidy::bugprone {
18 void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
19 const auto DRef = ignoringParens(
20 declRefExpr(
21 to(parmVarDecl(hasType(hasCanonicalType(
22 qualType(lValueReferenceType(pointee(
23 qualType(isConstQualified()))))
24 .bind("type"))))
25 .bind("param")))
26 .bind("dref"));
27 const auto Func =
28 functionDecl(hasReturnTypeLoc(loc(
29 qualType(hasCanonicalType(equalsBoundNode("type"))))))
30 .bind("func");
32 Finder->addMatcher(returnStmt(hasReturnValue(DRef), hasAncestor(Func)), this);
33 Finder->addMatcher(
34 returnStmt(hasReturnValue(ignoringParens(conditionalOperator(
35 eachOf(hasTrueExpression(DRef), hasFalseExpression(DRef)),
36 hasAncestor(Func))))),
37 this);
40 static bool isSameTypeIgnoringConst(QualType A, QualType B) {
41 return A.getCanonicalType().withConst() == B.getCanonicalType().withConst();
44 static bool isSameTypeIgnoringConstRef(QualType A, QualType B) {
45 return isSameTypeIgnoringConst(A.getCanonicalType().getNonReferenceType(),
46 B.getCanonicalType().getNonReferenceType());
49 static bool hasSameParameterTypes(const FunctionDecl &FD, const FunctionDecl &O,
50 const ParmVarDecl &PD) {
51 if (FD.getNumParams() != O.getNumParams())
52 return false;
53 for (unsigned I = 0, E = FD.getNumParams(); I < E; ++I) {
54 const ParmVarDecl *DPD = FD.getParamDecl(I);
55 const QualType OPT = O.getParamDecl(I)->getType();
56 if (DPD == &PD) {
57 if (!llvm::isa<RValueReferenceType>(OPT) ||
58 !isSameTypeIgnoringConstRef(DPD->getType(), OPT))
59 return false;
60 } else {
61 if (!isSameTypeIgnoringConst(DPD->getType(), OPT))
62 return false;
65 return true;
68 static const Decl *findRVRefOverload(const FunctionDecl &FD,
69 const ParmVarDecl &PD) {
70 // Actually it would be better to do lookup in caller site.
71 // But in most of cases, overloads of LVRef and RVRef will appear together.
72 // FIXME:
73 // 1. overload in anonymous namespace
74 // 2. forward reference
75 DeclContext::lookup_result LookupResult =
76 FD.getParent()->lookup(FD.getNameInfo().getName());
77 if (LookupResult.isSingleResult()) {
78 return nullptr;
80 for (const Decl *Overload : LookupResult) {
81 if (Overload == &FD)
82 continue;
83 if (const auto *O = dyn_cast<FunctionDecl>(Overload))
84 if (hasSameParameterTypes(FD, *O, PD))
85 return O;
87 return nullptr;
90 void ReturnConstRefFromParameterCheck::check(
91 const MatchFinder::MatchResult &Result) {
92 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
93 const auto *PD = Result.Nodes.getNodeAs<ParmVarDecl>("param");
94 const auto *DRef = Result.Nodes.getNodeAs<DeclRefExpr>("dref");
95 const SourceRange Range = DRef->getSourceRange();
96 if (Range.isInvalid())
97 return;
99 if (findRVRefOverload(*FD, *PD) != nullptr)
100 return;
102 diag(Range.getBegin(),
103 "returning a constant reference parameter may cause use-after-free "
104 "when the parameter is constructed from a temporary")
105 << Range;
108 } // namespace clang::tidy::bugprone