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 23using namespace clang; 24using namespace ento; 25 26//===----------------------------------------------------------------------===// 27// Generic type checking routines. 28//===----------------------------------------------------------------------===// 29 30static 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. 41static 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 51static 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 (!InNamespace(TD, "std")) 62 return false; 63 64 return TD->getName() == "string"; 65} 66 67static bool IsClangType(const RecordDecl *RD) { 68 return RD->getName() == "Type" && InNamespace(RD, "clang"); 69} 70 71static bool IsClangDecl(const RecordDecl *RD) { 72 return RD->getName() == "Decl" && InNamespace(RD, "clang"); 73} 74 75static bool IsClangStmt(const RecordDecl *RD) { 76 return RD->getName() == "Stmt" && InNamespace(RD, "clang"); 77} 78 79static bool IsClangAttr(const RecordDecl *RD) { 80 return RD->getName() == "Attr" && InNamespace(RD, "clang"); 81} 82 83static 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 97static 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 116namespace { 117class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { 118 BugReporter &BR; 119 const Decl *DeclWithIssue; 120public: 121 StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br) 122 : BR(br), DeclWithIssue(declWithIssue) {} 123 void VisitChildren(Stmt *S) { 124 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; 125 I != E; ++I) 126 if (Stmt *child = *I) 127 Visit(child); 128 } 129 void VisitStmt(Stmt *S) { VisitChildren(S); } 130 void VisitDeclStmt(DeclStmt *DS); 131private: 132 void VisitVarDecl(VarDecl *VD); 133}; 134} // end anonymous namespace 135 136static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { 137 StringRefCheckerVisitor walker(D, BR); 138 walker.Visit(D->getBody()); 139} 140 141void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { 142 VisitChildren(S); 143 144 for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) 145 if (VarDecl *VD = dyn_cast<VarDecl>(*I)) 146 VisitVarDecl(VD); 147} 148 149void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { 150 Expr *Init = VD->getInit(); 151 if (!Init) 152 return; 153 154 // Pattern match for: 155 // StringRef x = call() (where call returns std::string) 156 if (!IsLLVMStringRef(VD->getType())) 157 return; 158 ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); 159 if (!Ex1) 160 return; 161 CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); 162 if (!Ex2 || Ex2->getNumArgs() != 1) 163 return; 164 ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); 165 if (!Ex3) 166 return; 167 CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); 168 if (!Ex4 || Ex4->getNumArgs() != 1) 169 return; 170 ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); 171 if (!Ex5) 172 return; 173 CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); 174 if (!Ex6 || !IsStdString(Ex6->getType())) 175 return; 176 177 // Okay, badness! Report an error. 178 const char *desc = "StringRef should not be bound to temporary " 179 "std::string that it outlives"; 180 PathDiagnosticLocation VDLoc = 181 PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); 182 BR.EmitBasicReport(DeclWithIssue, desc, "LLVM Conventions", desc, 183 VDLoc, Init->getSourceRange()); 184} 185 186//===----------------------------------------------------------------------===// 187// CHECK: Clang AST nodes should not have fields that can allocate 188// memory. 189//===----------------------------------------------------------------------===// 190 191static bool AllocatesMemory(QualType T) { 192 return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); 193} 194 195// This type checking could be sped up via dynamic programming. 196static bool IsPartOfAST(const CXXRecordDecl *R) { 197 if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) 198 return true; 199 200 for (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(), 201 E = R->bases_end(); I!=E; ++I) { 202 CXXBaseSpecifier BS = *I; 203 QualType T = BS.getType(); 204 if (const RecordType *baseT = T->getAs<RecordType>()) { 205 CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); 206 if (IsPartOfAST(baseD)) 207 return true; 208 } 209 } 210 211 return false; 212} 213 214namespace { 215class ASTFieldVisitor { 216 SmallVector<FieldDecl*, 10> FieldChain; 217 const CXXRecordDecl *Root; 218 BugReporter &BR; 219public: 220 ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br) 221 : Root(root), BR(br) {} 222 223 void Visit(FieldDecl *D); 224 void ReportError(QualType T); 225}; 226} // end anonymous namespace 227 228static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR) { 229 if (!IsPartOfAST(R)) 230 return; 231 232 for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); 233 I != E; ++I) { 234 ASTFieldVisitor walker(R, BR); 235 walker.Visit(*I); 236 } 237} 238 239void ASTFieldVisitor::Visit(FieldDecl *D) { 240 FieldChain.push_back(D); 241 242 QualType T = D->getType(); 243 244 if (AllocatesMemory(T)) 245 ReportError(T); 246 247 if (const RecordType *RT = T->getAs<RecordType>()) { 248 const RecordDecl *RD = RT->getDecl()->getDefinition(); 249 for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); 250 I != E; ++I) 251 Visit(*I); 252 } 253 254 FieldChain.pop_back(); 255} 256 257void ASTFieldVisitor::ReportError(QualType T) { 258 SmallString<1024> buf; 259 llvm::raw_svector_ostream os(buf); 260 261 os << "AST class '" << Root->getName() << "' has a field '" 262 << FieldChain.front()->getName() << "' that allocates heap memory"; 263 if (FieldChain.size() > 1) { 264 os << " via the following chain: "; 265 bool isFirst = true; 266 for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), 267 E=FieldChain.end(); I!=E; ++I) { 268 if (!isFirst) 269 os << '.'; 270 else 271 isFirst = false; 272 os << (*I)->getName(); 273 } 274 } 275 os << " (type " << FieldChain.back()->getType().getAsString() << ")"; 276 os.flush(); 277 278 // Note that this will fire for every translation unit that uses this 279 // class. This is suboptimal, but at least scan-build will merge 280 // duplicate HTML reports. In the future we need a unified way of merging 281 // duplicate reports across translation units. For C++ classes we cannot 282 // just report warnings when we see an out-of-line method definition for a 283 // class, as that heuristic doesn't always work (the complete definition of 284 // the class may be in the header file, for example). 285 PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( 286 FieldChain.front(), BR.getSourceManager()); 287 BR.EmitBasicReport(Root, "AST node allocates heap memory", "LLVM Conventions", 288 os.str(), L); 289} 290 291//===----------------------------------------------------------------------===// 292// LLVMConventionsChecker 293//===----------------------------------------------------------------------===// 294 295namespace { 296class LLVMConventionsChecker : public Checker< 297 check::ASTDecl<CXXRecordDecl>, 298 check::ASTCodeBody > { 299public: 300 void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, 301 BugReporter &BR) const { 302 if (R->isCompleteDefinition()) 303 CheckASTMemory(R, BR); 304 } 305 306 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, 307 BugReporter &BR) const { 308 CheckStringRefAssignedTemporary(D, BR); 309 } 310}; 311} 312 313void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { 314 mgr.registerChecker<LLVMConventionsChecker>(); 315} 316