1 //===--- ReturnConstRefFromParameterCheck.cpp - clang-tidy ----------------===//
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
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(
21 to(parmVarDecl(hasType(hasCanonicalType(
22 qualType(lValueReferenceType(pointee(
23 qualType(isConstQualified()))))
28 functionDecl(hasReturnTypeLoc(loc(
29 qualType(hasCanonicalType(equalsBoundNode("type"))))))
32 Finder
->addMatcher(returnStmt(hasReturnValue(DRef
), hasAncestor(Func
)), this);
34 returnStmt(hasReturnValue(ignoringParens(conditionalOperator(
35 eachOf(hasTrueExpression(DRef
), hasFalseExpression(DRef
)),
36 hasAncestor(Func
))))),
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())
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();
57 if (!llvm::isa
<RValueReferenceType
>(OPT
) ||
58 !isSameTypeIgnoringConstRef(DPD
->getType(), OPT
))
61 if (!isSameTypeIgnoringConst(DPD
->getType(), OPT
))
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.
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()) {
80 for (const Decl
*Overload
: LookupResult
) {
83 if (const auto *O
= dyn_cast
<FunctionDecl
>(Overload
))
84 if (hasSameParameterTypes(FD
, *O
, PD
))
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())
99 if (findRVRefOverload(*FD
, *PD
) != nullptr)
102 diag(Range
.getBegin(),
103 "returning a constant reference parameter may cause use-after-free "
104 "when the parameter is constructed from a temporary")
108 } // namespace clang::tidy::bugprone