xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp (revision a07e8cdae7727583e20c9dec632a376365a6e209)
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 //===----------------------------------------------------------------------===//
8 
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"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang::tidy::bugprone {
18 
19 namespace {
20 
21 AST_MATCHER(ParmVarDecl, hasLifetimeBoundAttr) {
22   return Node.hasAttr<LifetimeBoundAttr>();
23 }
24 
25 } // namespace
26 
27 void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
28   const auto DRef = ignoringParens(
29       declRefExpr(
30           to(parmVarDecl(hasType(hasCanonicalType(
31                              qualType(lValueReferenceType(pointee(
32                                           qualType(isConstQualified()))))
33                                  .bind("type"))),
34                          hasDeclContext(functionDecl(
35                              equalsBoundNode("func"),
36                              hasReturnTypeLoc(loc(qualType(
37                                  hasCanonicalType(equalsBoundNode("type"))))))),
38                          unless(hasLifetimeBoundAttr()))
39                  .bind("param")))
40           .bind("dref"));
41 
42   Finder->addMatcher(
43       returnStmt(
44           hasAncestor(functionDecl().bind("func")),
45           hasReturnValue(anyOf(
46               DRef, ignoringParens(conditionalOperator(eachOf(
47                         hasTrueExpression(DRef), hasFalseExpression(DRef))))))),
48       this);
49 }
50 
51 static bool isSameTypeIgnoringConst(QualType A, QualType B) {
52   return A.getCanonicalType().withConst() == B.getCanonicalType().withConst();
53 }
54 
55 static bool isSameTypeIgnoringConstRef(QualType A, QualType B) {
56   return isSameTypeIgnoringConst(A.getCanonicalType().getNonReferenceType(),
57                                  B.getCanonicalType().getNonReferenceType());
58 }
59 
60 static bool hasSameParameterTypes(const FunctionDecl &FD, const FunctionDecl &O,
61                                   const ParmVarDecl &PD) {
62   if (FD.getNumParams() != O.getNumParams())
63     return false;
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();
67     if (DPD == &PD) {
68       if (!llvm::isa<RValueReferenceType>(OPT) ||
69           !isSameTypeIgnoringConstRef(DPD->getType(), OPT))
70         return false;
71     } else {
72       if (!isSameTypeIgnoringConst(DPD->getType(), OPT))
73         return false;
74     }
75   }
76   return true;
77 }
78 
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.
83   // FIXME:
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()) {
89     return nullptr;
90   }
91   for (const Decl *Overload : LookupResult) {
92     if (Overload == &FD)
93       continue;
94     if (const auto *O = dyn_cast<FunctionDecl>(Overload))
95       if (hasSameParameterTypes(FD, *O, PD))
96         return O;
97   }
98   return nullptr;
99 }
100 
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())
108     return;
109 
110   if (findRVRefOverload(*FD, *PD) != nullptr)
111     return;
112 
113   diag(Range.getBegin(),
114        "returning a constant reference parameter may cause use-after-free "
115        "when the parameter is constructed from a temporary")
116       << Range;
117 }
118 
119 } // namespace clang::tidy::bugprone
120