brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.1 KiB · 1107cef Raw
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