clang-tools 20.0.0git
ReturnConstRefFromParameterCheck.cpp
Go to the documentation of this file.
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
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
15using namespace clang::ast_matchers;
16
17namespace clang::tidy::bugprone {
18
19namespace {
20
21AST_MATCHER(ParmVarDecl, hasLifetimeBoundAttr) {
22 return Node.hasAttr<LifetimeBoundAttr>();
23}
24
25} // namespace
26
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
51static bool isSameTypeIgnoringConst(QualType A, QualType B) {
52 return A.getCanonicalType().withConst() == B.getCanonicalType().withConst();
53}
54
55static bool isSameTypeIgnoringConstRef(QualType A, QualType B) {
56 return isSameTypeIgnoringConst(A.getCanonicalType().getNonReferenceType(),
57 B.getCanonicalType().getNonReferenceType());
58}
59
60static 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
79static 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
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
const Expr * E
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static bool hasSameParameterTypes(const FunctionDecl &FD, const FunctionDecl &O, const ParmVarDecl &PD)
static bool isSameTypeIgnoringConst(QualType A, QualType B)
static const Decl * findRVRefOverload(const FunctionDecl &FD, const ParmVarDecl &PD)
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
static bool isSameTypeIgnoringConstRef(QualType A, QualType B)