brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · 1d4e6dd Raw
369 lines · cpp
1//=======- ForwardDeclChecker.cpp --------------------------------*- C++ -*-==//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 "ASTUtils.h"10#include "DiagOutputUtils.h"11#include "PtrTypesSemantics.h"12#include "clang/AST/Decl.h"13#include "clang/AST/DeclCXX.h"14#include "clang/AST/RecursiveASTVisitor.h"15#include "clang/Analysis/DomainSpecific/CocoaConventions.h"16#include "clang/Basic/SourceLocation.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"20#include "clang/StaticAnalyzer/Core/Checker.h"21#include "llvm/ADT/DenseSet.h"22#include "llvm/Support/SaveAndRestore.h"23 24using namespace clang;25using namespace ento;26 27namespace {28 29class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {30  BugType Bug;31  mutable BugReporter *BR = nullptr;32  mutable RetainTypeChecker RTC;33  mutable llvm::DenseSet<const Type *> SystemTypes;34 35public:36  ForwardDeclChecker()37      : Bug(this, "Forward declared member or local variable or parameter",38            "WebKit coding guidelines") {}39 40  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,41                    BugReporter &BRArg) const {42    BR = &BRArg;43 44    // The calls to checkAST* from AnalysisConsumer don't45    // visit template instantiations or lambda classes. We46    // want to visit those, so we make our own RecursiveASTVisitor.47    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {48      using Base = RecursiveASTVisitor<LocalVisitor>;49 50      const ForwardDeclChecker *Checker;51      Decl *DeclWithIssue{nullptr};52 53      explicit LocalVisitor(const ForwardDeclChecker *Checker)54          : Checker(Checker) {55        assert(Checker);56      }57 58      bool shouldVisitTemplateInstantiations() const { return true; }59      bool shouldVisitImplicitCode() const { return false; }60 61      bool VisitTypedefDecl(TypedefDecl *TD) {62        Checker->visitTypedef(TD);63        return true;64      }65 66      bool VisitRecordDecl(const RecordDecl *RD) {67        Checker->visitRecordDecl(RD, DeclWithIssue);68        return true;69      }70 71      bool TraverseDecl(Decl *D) {72        llvm::SaveAndRestore SavedDecl(DeclWithIssue);73        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))74          DeclWithIssue = D;75        return Base::TraverseDecl(D);76      }77 78      bool VisitVarDecl(VarDecl *V) {79        if (V->isLocalVarDecl())80          Checker->visitVarDecl(V, DeclWithIssue);81        return true;82      }83 84      bool VisitCallExpr(const CallExpr *CE) {85        Checker->visitCallExpr(CE, DeclWithIssue);86        return true;87      }88 89      bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {90        Checker->visitConstructExpr(CE, DeclWithIssue);91        return true;92      }93 94      bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {95        Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);96        return true;97      }98    };99 100    LocalVisitor visitor(this);101    RTC.visitTranslationUnitDecl(TUD);102    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));103  }104 105  void visitTypedef(const TypedefDecl *TD) const {106    RTC.visitTypedef(TD);107    auto QT = TD->getUnderlyingType().getCanonicalType();108    assert(BR && "expected nonnull BugReporter");109    if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {110      if (auto *Type = QT.getTypePtrOrNull())111        SystemTypes.insert(Type);112    }113  }114 115  bool isUnknownType(QualType QT) const {116    auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();117    if (!CanonicalType)118      return false;119    auto PointeeQT = CanonicalType->getPointeeType();120    auto *PointeeType = PointeeQT.getTypePtrOrNull();121    if (!PointeeType)122      return false;123    auto *R = PointeeType->getAsCXXRecordDecl();124    if (!R) // Forward declaration of a Objective-C interface is safe.125      return false;126    auto Name = R->getName();127    if (R->hasDefinition())128      return false;129    // Find a definition amongst template declarations.130    if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(R)) {131      if (auto *S = Specialization->getSpecializedTemplate()) {132        for (S = S->getMostRecentDecl(); S; S = S->getPreviousDecl()) {133          if (S->isThisDeclarationADefinition())134            return false;135        }136      }137    }138    return !RTC.isUnretained(QT) && !SystemTypes.contains(CanonicalType) &&139           !SystemTypes.contains(PointeeType) && !Name.starts_with("Opaque") &&140           Name != "_NSZone";141  }142 143  void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {144    if (!RD->isThisDeclarationADefinition())145      return;146 147    if (RD->isImplicit() || RD->isLambda())148      return;149 150    const auto RDLocation = RD->getLocation();151    if (!RDLocation.isValid())152      return;153 154    const auto Kind = RD->getTagKind();155    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)156      return;157 158    assert(BR && "expected nonnull BugReporter");159    if (BR->getSourceManager().isInSystemHeader(RDLocation))160      return;161 162    // Ref-counted smartpointers actually have raw-pointer to uncounted type as163    // a member but we trust them to handle it correctly.164    auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);165    if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtrOrOSPtr(R))166      return;167 168    for (auto *Member : RD->fields()) {169      auto QT = Member->getType();170      if (isUnknownType(QT)) {171        SmallString<100> Buf;172        llvm::raw_svector_ostream Os(Buf);173 174        const std::string TypeName = QT.getAsString();175        Os << "Member variable ";176        printQuotedName(Os, Member);177        Os << " uses a forward declared type '" << TypeName << "'";178 179        const SourceLocation SrcLocToReport = Member->getBeginLoc();180        PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());181        auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);182        Report->addRange(Member->getSourceRange());183        Report->setDeclWithIssue(DeclWithIssue);184        BR->emitReport(std::move(Report));185      }186    }187  }188 189  void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {190    assert(BR && "expected nonnull BugReporter");191    if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))192      return;193 194    auto QT = V->getType();195    if (!isUnknownType(QT))196      return;197 198    SmallString<100> Buf;199    llvm::raw_svector_ostream Os(Buf);200    Os << "Local variable ";201    printQuotedQualifiedName(Os, V);202 203    reportBug(V->getBeginLoc(), V->getSourceRange(), DeclWithIssue, Os.str(),204              QT);205  }206 207  void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {208    assert(BR && "expected nonnull BugReporter");209    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))210      return;211 212    if (auto *F = CE->getDirectCallee()) {213      // Skip the first argument for overloaded member operators (e. g. lambda214      // or std::function call operator).215      unsigned ArgIdx =216          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);217 218      for (auto P = F->param_begin();219           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)220        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);221    }222  }223 224  void visitConstructExpr(const CXXConstructExpr *CE,225                          const Decl *DeclWithIssue) const {226    assert(BR && "expected nonnull BugReporter");227    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))228      return;229 230    if (auto *F = CE->getConstructor()) {231      // Skip the first argument for overloaded member operators (e. g. lambda232      // or std::function call operator).233      unsigned ArgIdx =234          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);235 236      for (auto P = F->param_begin();237           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)238        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);239    }240  }241 242  void visitObjCMessageExpr(const ObjCMessageExpr *E,243                            const Decl *DeclWithIssue) const {244    assert(BR && "expected nonnull BugReporter");245    if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))246      return;247 248    if (auto *Receiver = E->getInstanceReceiver()) {249      Receiver = Receiver->IgnoreParenCasts();250      if (isUnknownType(E->getReceiverType()))251        reportUnknownRecieverType(Receiver, DeclWithIssue);252    }253 254    auto *MethodDecl = E->getMethodDecl();255    if (!MethodDecl)256      return;257 258    auto ArgCount = E->getNumArgs();259    for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)260      visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);261  }262 263  void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,264                    const Decl *DeclWithIssue) const {265    auto *ArgExpr = Arg->IgnoreParenCasts();266    while (ArgExpr) {267      ArgExpr = ArgExpr->IgnoreParenCasts();268      if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {269        auto *InnerCallee = InnerCE->getDirectCallee();270        if (InnerCallee && InnerCallee->isInStdNamespace() &&271            safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {272          ArgExpr = InnerCE->getArg(0);273          continue;274        }275      }276      if (auto *UO = dyn_cast<UnaryOperator>(ArgExpr)) {277        auto OpCode = UO->getOpcode();278        if (OpCode == UO_Deref || OpCode == UO_AddrOf) {279          ArgExpr = UO->getSubExpr();280          continue;281        }282      }283      break;284    }285 286    if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(ArgExpr)) {287      if (isOwnerPtrType(MemberCallExpr->getObjectType()))288        return;289    }290 291    if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(ArgExpr)) {292      auto *Method = dyn_cast_or_null<CXXMethodDecl>(OpCE->getDirectCallee());293      if (Method && isOwnerPtr(safeGetName(Method->getParent()))) {294        if (OpCE->getOperator() == OO_Star && OpCE->getNumArgs() == 1)295          return;296      }297    }298 299    if (isNullPtr(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||300        isa<CXXDefaultArgExpr>(ArgExpr))301      return;302 303    if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {304      if (auto *ValDecl = DRE->getDecl()) {305        if (isa<ParmVarDecl>(ValDecl))306          return;307      }308    }309 310    QualType ArgType = Param->getType();311    if (!isUnknownType(ArgType))312      return;313 314    reportUnknownArgType(Arg, Param, DeclWithIssue);315  }316 317  void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,318                            const Decl *DeclWithIssue) const {319    assert(CA);320 321    SmallString<100> Buf;322    llvm::raw_svector_ostream Os(Buf);323 324    const std::string paramName = safeGetName(Param);325    Os << "Call argument";326    if (!paramName.empty()) {327      Os << " for parameter ";328      printQuotedQualifiedName(Os, Param);329    }330 331    reportBug(CA->getExprLoc(), CA->getSourceRange(), DeclWithIssue, Os.str(),332              Param->getType());333  }334 335  void reportUnknownRecieverType(const Expr *Receiver,336                                 const Decl *DeclWithIssue) const {337    assert(Receiver);338    reportBug(Receiver->getExprLoc(), Receiver->getSourceRange(), DeclWithIssue,339              "Receiver", Receiver->getType());340  }341 342  void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,343                 const Decl *DeclWithIssue, const StringRef &Description,344                 QualType Type) const {345    SmallString<100> Buf;346    llvm::raw_svector_ostream Os(Buf);347 348    const std::string TypeName = Type.getAsString();349    Os << Description << " uses a forward declared type '" << TypeName << "'";350 351    assert(BR && "expected nonnull BugReporter");352    PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());353    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);354    Report->addRange(SrcRange);355    Report->setDeclWithIssue(DeclWithIssue);356    BR->emitReport(std::move(Report));357  }358};359 360} // namespace361 362void ento::registerForwardDeclChecker(CheckerManager &Mgr) {363  Mgr.registerChecker<ForwardDeclChecker>();364}365 366bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {367  return true;368}369