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-exception 6 // 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 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 29 class 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 35 public: 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't 45 // visit template instantiations or lambda classes. We 46 // 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 as 163 // 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) || isRetainPtr(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. lambda 214 // 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. lambda 232 // 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 if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) { 267 auto *InnerCallee = InnerCE->getDirectCallee(); 268 if (InnerCallee && InnerCallee->isInStdNamespace() && 269 safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) { 270 ArgExpr = InnerCE->getArg(0); 271 if (ArgExpr) 272 ArgExpr = ArgExpr->IgnoreParenCasts(); 273 } 274 } 275 if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) || 276 isa<CXXDefaultArgExpr>(ArgExpr)) 277 return; 278 if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) { 279 if (auto *ValDecl = DRE->getDecl()) { 280 if (isa<ParmVarDecl>(ValDecl)) 281 return; 282 } 283 } 284 285 QualType ArgType = Param->getType(); 286 if (!isUnknownType(ArgType)) 287 return; 288 289 reportUnknownArgType(Arg, Param, DeclWithIssue); 290 } 291 292 void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param, 293 const Decl *DeclWithIssue) const { 294 assert(CA); 295 296 SmallString<100> Buf; 297 llvm::raw_svector_ostream Os(Buf); 298 299 const std::string paramName = safeGetName(Param); 300 Os << "Call argument"; 301 if (!paramName.empty()) { 302 Os << " for parameter "; 303 printQuotedQualifiedName(Os, Param); 304 } 305 306 reportBug(CA->getExprLoc(), CA->getSourceRange(), DeclWithIssue, Os.str(), 307 Param->getType()); 308 } 309 310 void reportUnknownRecieverType(const Expr *Receiver, 311 const Decl *DeclWithIssue) const { 312 assert(Receiver); 313 reportBug(Receiver->getExprLoc(), Receiver->getSourceRange(), DeclWithIssue, 314 "Receiver", Receiver->getType()); 315 } 316 317 void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange, 318 const Decl *DeclWithIssue, const StringRef &Description, 319 QualType Type) const { 320 SmallString<100> Buf; 321 llvm::raw_svector_ostream Os(Buf); 322 323 const std::string TypeName = Type.getAsString(); 324 Os << Description << " uses a forward declared type '" << TypeName << "'"; 325 326 assert(BR && "expected nonnull BugReporter"); 327 PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager()); 328 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 329 Report->addRange(SrcRange); 330 Report->setDeclWithIssue(DeclWithIssue); 331 BR->emitReport(std::move(Report)); 332 } 333 }; 334 335 } // namespace 336 337 void ento::registerForwardDeclChecker(CheckerManager &Mgr) { 338 Mgr.registerChecker<ForwardDeclChecker>(); 339 } 340 341 bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) { 342 return true; 343 } 344