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/ASTMatchers/ASTMatchFinder.h"
11#include "clang/ASTMatchers/ASTMatchers.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::bugprone {
16
18 Finder->addMatcher(
19 returnStmt(
20 hasReturnValue(declRefExpr(
21 to(parmVarDecl(hasType(hasCanonicalType(
22 qualType(lValueReferenceType(pointee(
23 qualType(isConstQualified()))))
24 .bind("type"))))
25 .bind("param")))),
26 hasAncestor(
27 functionDecl(hasReturnTypeLoc(loc(qualType(
28 hasCanonicalType(equalsBoundNode("type"))))))
29 .bind("func")))
30 .bind("ret"),
31 this);
32}
33
34static bool isSameTypeIgnoringConst(QualType A, QualType B) {
35 return A.getCanonicalType().withConst() == B.getCanonicalType().withConst();
36}
37
38static bool isSameTypeIgnoringConstRef(QualType A, QualType B) {
39 return isSameTypeIgnoringConst(A.getCanonicalType().getNonReferenceType(),
40 B.getCanonicalType().getNonReferenceType());
41}
42
43static bool hasSameParameterTypes(const FunctionDecl &FD, const FunctionDecl &O,
44 const ParmVarDecl &PD) {
45 if (FD.getNumParams() != O.getNumParams())
46 return false;
47 for (unsigned I = 0, E = FD.getNumParams(); I < E; ++I) {
48 const ParmVarDecl *DPD = FD.getParamDecl(I);
49 const QualType OPT = O.getParamDecl(I)->getType();
50 if (DPD == &PD) {
51 if (!llvm::isa<RValueReferenceType>(OPT) ||
52 !isSameTypeIgnoringConstRef(DPD->getType(), OPT))
53 return false;
54 } else {
55 if (!isSameTypeIgnoringConst(DPD->getType(), OPT))
56 return false;
57 }
58 }
59 return true;
60}
61
62static const Decl *findRVRefOverload(const FunctionDecl &FD,
63 const ParmVarDecl &PD) {
64 // Actually it would be better to do lookup in caller site.
65 // But in most of cases, overloads of LVRef and RVRef will appear together.
66 // FIXME:
67 // 1. overload in anonymous namespace
68 // 2. forward reference
69 DeclContext::lookup_result LookupResult =
70 FD.getParent()->lookup(FD.getNameInfo().getName());
71 if (LookupResult.isSingleResult()) {
72 return nullptr;
73 }
74 for (const Decl *Overload : LookupResult) {
75 if (Overload == &FD)
76 continue;
77 if (const auto *O = dyn_cast<FunctionDecl>(Overload))
78 if (hasSameParameterTypes(FD, *O, PD))
79 return O;
80 }
81 return nullptr;
82}
83
85 const MatchFinder::MatchResult &Result) {
86 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
87 const auto *PD = Result.Nodes.getNodeAs<ParmVarDecl>("param");
88 const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
89 const SourceRange Range = R->getRetValue()->getSourceRange();
90 if (Range.isInvalid())
91 return;
92
93 if (findRVRefOverload(*FD, *PD) != nullptr)
94 return;
95
96 diag(Range.getBegin(),
97 "returning a constant reference parameter may cause use-after-free "
98 "when the parameter is constructed from a temporary")
99 << Range;
100}
101
102} // namespace clang::tidy::bugprone
const Expr * E
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
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)
static bool isSameTypeIgnoringConstRef(QualType A, QualType B)