120 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 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} // namespace26 27void 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 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 namespace85 // 2. forward reference86 const 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 101void 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::bugprone120