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