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/Attrs.inc"
11 #include "clang/AST/Expr.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
15 using namespace clang::ast_matchers
;
17 namespace clang::tidy::bugprone
{
21 AST_MATCHER(ParmVarDecl
, hasLifetimeBoundAttr
) {
22 return Node
.hasAttr
<LifetimeBoundAttr
>();
27 void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder
*Finder
) {
28 const auto DRef
= ignoringParens(
30 to(parmVarDecl(hasType(hasCanonicalType(
31 qualType(lValueReferenceType(pointee(
32 qualType(isConstQualified()))))
34 hasDeclContext(functionDecl(
35 equalsBoundNode("func"),
36 hasReturnTypeLoc(loc(qualType(
37 hasCanonicalType(equalsBoundNode("type"))))))),
38 unless(hasLifetimeBoundAttr()))
44 hasAncestor(functionDecl().bind("func")),
46 DRef
, ignoringParens(conditionalOperator(eachOf(
47 hasTrueExpression(DRef
), hasFalseExpression(DRef
))))))),
51 static bool isSameTypeIgnoringConst(QualType A
, QualType B
) {
52 return A
.getCanonicalType().withConst() == B
.getCanonicalType().withConst();
55 static bool isSameTypeIgnoringConstRef(QualType A
, QualType B
) {
56 return isSameTypeIgnoringConst(A
.getCanonicalType().getNonReferenceType(),
57 B
.getCanonicalType().getNonReferenceType());
60 static bool hasSameParameterTypes(const FunctionDecl
&FD
, const FunctionDecl
&O
,
61 const ParmVarDecl
&PD
) {
62 if (FD
.getNumParams() != O
.getNumParams())
64 for (unsigned I
= 0, E
= FD
.getNumParams(); I
< E
; ++I
) {
65 const ParmVarDecl
*DPD
= FD
.getParamDecl(I
);
66 const QualType OPT
= O
.getParamDecl(I
)->getType();
68 if (!llvm::isa
<RValueReferenceType
>(OPT
) ||
69 !isSameTypeIgnoringConstRef(DPD
->getType(), OPT
))
72 if (!isSameTypeIgnoringConst(DPD
->getType(), OPT
))
79 static const Decl
*findRVRefOverload(const FunctionDecl
&FD
,
80 const ParmVarDecl
&PD
) {
81 // Actually it would be better to do lookup in caller site.
82 // But in most of cases, overloads of LVRef and RVRef will appear together.
84 // 1. overload in anonymous namespace
85 // 2. forward reference
86 DeclContext::lookup_result LookupResult
=
87 FD
.getParent()->lookup(FD
.getNameInfo().getName());
88 if (LookupResult
.isSingleResult()) {
91 for (const Decl
*Overload
: LookupResult
) {
94 if (const auto *O
= dyn_cast
<FunctionDecl
>(Overload
))
95 if (hasSameParameterTypes(FD
, *O
, PD
))
101 void ReturnConstRefFromParameterCheck::check(
102 const MatchFinder::MatchResult
&Result
) {
103 const auto *FD
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("func");
104 const auto *PD
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("param");
105 const auto *DRef
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("dref");
106 const SourceRange Range
= DRef
->getSourceRange();
107 if (Range
.isInvalid())
110 if (findRVRefOverload(*FD
, *PD
) != nullptr)
113 diag(Range
.getBegin(),
114 "returning a constant reference parameter may cause use-after-free "
115 "when the parameter is constructed from a temporary")
119 } // namespace clang::tidy::bugprone