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