================
@@ -0,0 +1,335 @@
+//=======- ForwardDeclChecker.cpp --------------------------------*- C++ 
-*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> 
{
+  BugType Bug;
+  mutable BugReporter *BR;
+  mutable RetainTypeChecker RTC;
+  mutable llvm::DenseSet<const Type *> SystemTypes;
+
+public:
+  ForwardDeclChecker()
+      : Bug(this, "Forward declared member or local variable or parameter",
+            "WebKit coding guidelines") {}
+
+  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+                    BugReporter &BRArg) const {
+    BR = &BRArg;
+
+    // The calls to checkAST* from AnalysisConsumer don't
+    // visit template instantiations or lambda classes. We
+    // want to visit those, so we make our own RecursiveASTVisitor.
+    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+      using Base = RecursiveASTVisitor<LocalVisitor>;
+
+      const ForwardDeclChecker *Checker;
+      Decl *DeclWithIssue{nullptr};
+
+      explicit LocalVisitor(const ForwardDeclChecker *Checker)
+          : Checker(Checker) {
+        assert(Checker);
+      }
+
+      bool shouldVisitTemplateInstantiations() const { return true; }
+      bool shouldVisitImplicitCode() const { return false; }
+
+      bool VisitTypedefDecl(TypedefDecl *TD) {
+        Checker->visitTypedef(TD);
+        return true;
+      }
+
+      bool VisitRecordDecl(const RecordDecl *RD) {
+        Checker->visitRecordDecl(RD, DeclWithIssue);
+        return true;
+      }
+
+      bool TraverseDecl(Decl *D) {
+        llvm::SaveAndRestore SavedDecl(DeclWithIssue);
+        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
+          DeclWithIssue = D;
+        return Base::TraverseDecl(D);
+      }
+
+      bool VisitVarDecl(VarDecl *V) {
+        if (V->isLocalVarDecl())
+          Checker->visitVarDecl(V, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitCallExpr(const CallExpr *CE) {
+        Checker->visitCallExpr(CE, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
+        Checker->visitConstructExpr(CE, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
+        Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
+        return true;
+      }
+    };
+
+    LocalVisitor visitor(this);
+    RTC.visitTranslationUnitDecl(TUD);
+    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+  }
+
+  void visitTypedef(const TypedefDecl *TD) const {
+    RTC.visitTypedef(TD);
+    auto QT = TD->getUnderlyingType().getCanonicalType();
+    if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {
+      if (auto *Type = QT.getTypePtrOrNull(); Type && QT->isPointerType())
+        SystemTypes.insert(Type);
+    }
+  }
+
+  bool isUnknownType(QualType QT) const {
+    auto *Type = QT.getTypePtrOrNull();
+    if (!Type)
+      return false;
+    auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();
+    auto PointeeQT = Type->getPointeeType();
+    auto *PointeeType = PointeeQT.getTypePtrOrNull();
+    if (!PointeeType)
+      return false;
+    auto *R = PointeeType->getAsCXXRecordDecl();
+    if (!R) // Forward declaration of a Objective-C interface is safe.
+      return false;
+    auto Name = R->getName();
+    return !R->hasDefinition() && !RTC.isUnretained(QT) &&
+           !SystemTypes.contains(CanonicalType) &&
+           !Name.starts_with("Opaque") && Name != "_NSZone";
+  }
+
+  void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
+    if (!RD->isThisDeclarationADefinition())
+      return;
+
+    if (RD->isImplicit() || RD->isLambda())
+      return;
+
+    const auto RDLocation = RD->getLocation();
+    if (!RDLocation.isValid())
+      return;
+
+    const auto Kind = RD->getTagKind();
+    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
+      return;
+
+    if (BR->getSourceManager().isInSystemHeader(RDLocation))
+      return;
+
+    // Ref-counted smartpointers actually have raw-pointer to uncounted type as
+    // a member but we trust them to handle it correctly.
+    auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
+    if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtr(R))
+      return;
+
+    for (auto *Member : RD->fields()) {
+      auto QT = Member->getType();
+      if (isUnknownType(QT)) {
+        SmallString<100> Buf;
+        llvm::raw_svector_ostream Os(Buf);
+
+        const std::string TypeName = QT.getAsString();
+        Os << "Member variable ";
+        printQuotedName(Os, Member);
+        Os << " uses a forward declared type '" << TypeName << "'";
+
+        const SourceLocation SrcLocToReport = Member->getBeginLoc();
+        PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+        auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+        Report->addRange(Member->getSourceRange());
+        Report->setDeclWithIssue(DeclWithIssue);
+        BR->emitReport(std::move(Report));
+      }
+    }
+  }
+
+  void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))
+      return;
+
+    auto QT = V->getType();
+    if (!isUnknownType(QT))
+      return;
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    const std::string TypeName = QT.getAsString();
+    Os << "Local variable ";
+    printQuotedQualifiedName(Os, V);
+    Os << " uses a forward declared type '" << TypeName << "'";
+
+    const SourceLocation SrcLocToReport = V->getBeginLoc();
+    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(V->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
+
+  void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    if (auto *F = CE->getDirectCallee()) {
+      // Skip the first argument for overloaded member operators (e. g. lambda
+      // or std::function call operator).
+      unsigned ArgIdx =
+          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+
+      for (auto P = F->param_begin();
+           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
+        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
+    }
+  }
+
+  void visitConstructExpr(const CXXConstructExpr *CE,
+                          const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    if (auto *F = CE->getConstructor()) {
+      // Skip the first argument for overloaded member operators (e. g. lambda
+      // or std::function call operator).
+      unsigned ArgIdx =
+          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+
+      for (auto P = F->param_begin();
+           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
+        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
+    }
+  }
+
+  void visitObjCMessageExpr(const ObjCMessageExpr *E,
+                            const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
+      return;
+
+    if (auto *Receiver = E->getInstanceReceiver()->IgnoreParenCasts()) {
+      if (isUnknownType(E->getReceiverType()))
+        reportUnknownRecieverType(Receiver, DeclWithIssue);
+    }
+
+    auto *MethodDecl = E->getMethodDecl();
+    if (!MethodDecl)
+      return;
+
+    auto ArgCount = E->getNumArgs();
+    for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
+      visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);
+  }
+
+  void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
+                    const Decl *DeclWithIssue) const {
+    auto *ArgExpr = Arg->IgnoreParenCasts();
+    if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) {
+      auto *InnerCallee = InnerCE->getDirectCallee();
+      if (InnerCallee && InnerCallee->isInStdNamespace() &&
+          safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
+        ArgExpr = InnerCE->getArg(0);
+        if (ArgExpr)
+          ArgExpr = ArgExpr->IgnoreParenCasts();
+      }
+    }
+    if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
+        isa<CXXDefaultArgExpr>(ArgExpr))
+      return;
+    if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
+      if (auto *ValDecl = DRE->getDecl()) {
+        if (isa<ParmVarDecl>(ValDecl))
+          return;
+      }
+    }
+
+    QualType ArgType = Param->getType();
+    if (!isUnknownType(ArgType))
+      return;
+
+    reportUnknownArgType(Arg, Param, DeclWithIssue);
+  }
+
+  void reportUnknownArgType(const Expr *CallArg, const ParmVarDecl *Param,
+                            const Decl *DeclWithIssue) const {
+    assert(CallArg);
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    const std::string paramName = safeGetName(Param);
+    const std::string TypeName = Param->getType().getAsString();
+    Os << "Call argument";
+    if (!paramName.empty()) {
+      Os << " for parameter ";
+      printQuotedQualifiedName(Os, Param);
+    }
+    Os << " uses a forward declared type '" << TypeName << "'";
+
+    const SourceLocation SrcLocToReport = CallArg->getExprLoc();
+    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(CallArg->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
----------------
rniwa wrote:

Sure, done.

https://github.com/llvm/llvm-project/pull/130554
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to