1 //=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This defines LLVMConventionsChecker, a bunch of small little checks 11 // for checking specific coding conventions in the LLVM/Clang codebase. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "ClangSACheckers.h" 16 #include "clang/AST/DeclTemplate.h" 17 #include "clang/AST/StmtVisitor.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "llvm/ADT/SmallString.h" 21 #include "llvm/Support/raw_ostream.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 //===----------------------------------------------------------------------===// 27 // Generic type checking routines. 28 //===----------------------------------------------------------------------===// 29 30 static bool IsLLVMStringRef(QualType T) { 31 const RecordType *RT = T->getAs<RecordType>(); 32 if (!RT) 33 return false; 34 35 return StringRef(QualType(RT, 0).getAsString()) == 36 "class StringRef"; 37 } 38 39 /// Check whether the declaration is semantically inside the top-level 40 /// namespace named by ns. 41 static bool InNamespace(const Decl *D, StringRef NS) { 42 const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); 43 if (!ND) 44 return false; 45 const IdentifierInfo *II = ND->getIdentifier(); 46 if (!II || !II->getName().equals(NS)) 47 return false; 48 return isa<TranslationUnitDecl>(ND->getDeclContext()); 49 } 50 51 static bool IsStdString(QualType T) { 52 if (const ElaboratedType *QT = T->getAs<ElaboratedType>()) 53 T = QT->getNamedType(); 54 55 const TypedefType *TT = T->getAs<TypedefType>(); 56 if (!TT) 57 return false; 58 59 const TypedefNameDecl *TD = TT->getDecl(); 60 61 if (!TD->isInStdNamespace()) 62 return false; 63 64 return TD->getName() == "string"; 65 } 66 67 static bool IsClangType(const RecordDecl *RD) { 68 return RD->getName() == "Type" && InNamespace(RD, "clang"); 69 } 70 71 static bool IsClangDecl(const RecordDecl *RD) { 72 return RD->getName() == "Decl" && InNamespace(RD, "clang"); 73 } 74 75 static bool IsClangStmt(const RecordDecl *RD) { 76 return RD->getName() == "Stmt" && InNamespace(RD, "clang"); 77 } 78 79 static bool IsClangAttr(const RecordDecl *RD) { 80 return RD->getName() == "Attr" && InNamespace(RD, "clang"); 81 } 82 83 static bool IsStdVector(QualType T) { 84 const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); 85 if (!TS) 86 return false; 87 88 TemplateName TM = TS->getTemplateName(); 89 TemplateDecl *TD = TM.getAsTemplateDecl(); 90 91 if (!TD || !InNamespace(TD, "std")) 92 return false; 93 94 return TD->getName() == "vector"; 95 } 96 97 static bool IsSmallVector(QualType T) { 98 const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); 99 if (!TS) 100 return false; 101 102 TemplateName TM = TS->getTemplateName(); 103 TemplateDecl *TD = TM.getAsTemplateDecl(); 104 105 if (!TD || !InNamespace(TD, "llvm")) 106 return false; 107 108 return TD->getName() == "SmallVector"; 109 } 110 111 //===----------------------------------------------------------------------===// 112 // CHECK: a StringRef should not be bound to a temporary std::string whose 113 // lifetime is shorter than the StringRef's. 114 //===----------------------------------------------------------------------===// 115 116 namespace { 117 class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { 118 const Decl *DeclWithIssue; 119 BugReporter &BR; 120 const CheckerBase *Checker; 121 122 public: 123 StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br, 124 const CheckerBase *checker) 125 : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {} 126 void VisitChildren(Stmt *S) { 127 for (Stmt *Child : S->children()) 128 if (Child) 129 Visit(Child); 130 } 131 void VisitStmt(Stmt *S) { VisitChildren(S); } 132 void VisitDeclStmt(DeclStmt *DS); 133 private: 134 void VisitVarDecl(VarDecl *VD); 135 }; 136 } // end anonymous namespace 137 138 static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR, 139 const CheckerBase *Checker) { 140 StringRefCheckerVisitor walker(D, BR, Checker); 141 walker.Visit(D->getBody()); 142 } 143 144 void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { 145 VisitChildren(S); 146 147 for (auto *I : S->decls()) 148 if (VarDecl *VD = dyn_cast<VarDecl>(I)) 149 VisitVarDecl(VD); 150 } 151 152 void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { 153 Expr *Init = VD->getInit(); 154 if (!Init) 155 return; 156 157 // Pattern match for: 158 // StringRef x = call() (where call returns std::string) 159 if (!IsLLVMStringRef(VD->getType())) 160 return; 161 ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); 162 if (!Ex1) 163 return; 164 CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); 165 if (!Ex2 || Ex2->getNumArgs() != 1) 166 return; 167 ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); 168 if (!Ex3) 169 return; 170 CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); 171 if (!Ex4 || Ex4->getNumArgs() != 1) 172 return; 173 ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); 174 if (!Ex5) 175 return; 176 CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); 177 if (!Ex6 || !IsStdString(Ex6->getType())) 178 return; 179 180 // Okay, badness! Report an error. 181 const char *desc = "StringRef should not be bound to temporary " 182 "std::string that it outlives"; 183 PathDiagnosticLocation VDLoc = 184 PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); 185 BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc, 186 VDLoc, Init->getSourceRange()); 187 } 188 189 //===----------------------------------------------------------------------===// 190 // CHECK: Clang AST nodes should not have fields that can allocate 191 // memory. 192 //===----------------------------------------------------------------------===// 193 194 static bool AllocatesMemory(QualType T) { 195 return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); 196 } 197 198 // This type checking could be sped up via dynamic programming. 199 static bool IsPartOfAST(const CXXRecordDecl *R) { 200 if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) 201 return true; 202 203 for (const auto &BS : R->bases()) { 204 QualType T = BS.getType(); 205 if (const RecordType *baseT = T->getAs<RecordType>()) { 206 CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); 207 if (IsPartOfAST(baseD)) 208 return true; 209 } 210 } 211 212 return false; 213 } 214 215 namespace { 216 class ASTFieldVisitor { 217 SmallVector<FieldDecl*, 10> FieldChain; 218 const CXXRecordDecl *Root; 219 BugReporter &BR; 220 const CheckerBase *Checker; 221 222 public: 223 ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br, 224 const CheckerBase *checker) 225 : Root(root), BR(br), Checker(checker) {} 226 227 void Visit(FieldDecl *D); 228 void ReportError(QualType T); 229 }; 230 } // end anonymous namespace 231 232 static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR, 233 const CheckerBase *Checker) { 234 if (!IsPartOfAST(R)) 235 return; 236 237 for (auto *I : R->fields()) { 238 ASTFieldVisitor walker(R, BR, Checker); 239 walker.Visit(I); 240 } 241 } 242 243 void ASTFieldVisitor::Visit(FieldDecl *D) { 244 FieldChain.push_back(D); 245 246 QualType T = D->getType(); 247 248 if (AllocatesMemory(T)) 249 ReportError(T); 250 251 if (const RecordType *RT = T->getAs<RecordType>()) { 252 const RecordDecl *RD = RT->getDecl()->getDefinition(); 253 for (auto *I : RD->fields()) 254 Visit(I); 255 } 256 257 FieldChain.pop_back(); 258 } 259 260 void ASTFieldVisitor::ReportError(QualType T) { 261 SmallString<1024> buf; 262 llvm::raw_svector_ostream os(buf); 263 264 os << "AST class '" << Root->getName() << "' has a field '" 265 << FieldChain.front()->getName() << "' that allocates heap memory"; 266 if (FieldChain.size() > 1) { 267 os << " via the following chain: "; 268 bool isFirst = true; 269 for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), 270 E=FieldChain.end(); I!=E; ++I) { 271 if (!isFirst) 272 os << '.'; 273 else 274 isFirst = false; 275 os << (*I)->getName(); 276 } 277 } 278 os << " (type " << FieldChain.back()->getType().getAsString() << ")"; 279 280 // Note that this will fire for every translation unit that uses this 281 // class. This is suboptimal, but at least scan-build will merge 282 // duplicate HTML reports. In the future we need a unified way of merging 283 // duplicate reports across translation units. For C++ classes we cannot 284 // just report warnings when we see an out-of-line method definition for a 285 // class, as that heuristic doesn't always work (the complete definition of 286 // the class may be in the header file, for example). 287 PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( 288 FieldChain.front(), BR.getSourceManager()); 289 BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory", 290 "LLVM Conventions", os.str(), L); 291 } 292 293 //===----------------------------------------------------------------------===// 294 // LLVMConventionsChecker 295 //===----------------------------------------------------------------------===// 296 297 namespace { 298 class LLVMConventionsChecker : public Checker< 299 check::ASTDecl<CXXRecordDecl>, 300 check::ASTCodeBody > { 301 public: 302 void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, 303 BugReporter &BR) const { 304 if (R->isCompleteDefinition()) 305 CheckASTMemory(R, BR, this); 306 } 307 308 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 309 BugReporter &BR) const { 310 CheckStringRefAssignedTemporary(D, BR, this); 311 } 312 }; 313 } 314 315 void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { 316 mgr.registerChecker<LLVMConventionsChecker>(); 317 } 318