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