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 | |
| 15 | #include "clang/AST/DeclCXX.h" |
| 16 | #include "clang/AST/StmtVisitor.h" |
| 17 | #include "clang/Checker/Checkers/LocalCheckers.h" |
| 18 | #include "clang/Checker/BugReporter/BugReporter.h" |
| 19 | #include <string> |
| 20 | #include <llvm/ADT/StringRef.h> |
| 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 | |
| 28 | static bool IsStringRef(QualType T) { |
| 29 | const RecordType *RT = T->getAs<RecordType>(); |
| 30 | if (!RT) |
| 31 | return false; |
| 32 | |
| 33 | return llvm::StringRef(QualType(RT, 0).getAsString()) == |
| 34 | "class llvm::StringRef"; |
| 35 | } |
| 36 | |
| 37 | static bool IsStdString(QualType T) { |
| 38 | if (const QualifiedNameType *QT = T->getAs<QualifiedNameType>()) |
| 39 | T = QT->getNamedType(); |
| 40 | |
| 41 | const TypedefType *TT = T->getAs<TypedefType>(); |
| 42 | if (!TT) |
| 43 | return false; |
| 44 | |
Ted Kremenek | 676ca15 | 2010-02-14 19:09:05 +0000 | [diff] [blame^] | 45 | const TypedefDecl *TD = TT->getDecl(); |
Ted Kremenek | 55abf16 | 2010-02-14 19:08:36 +0000 | [diff] [blame] | 46 | const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(TD->getDeclContext()); |
| 47 | if (!ND) |
| 48 | return false; |
| 49 | const IdentifierInfo *II = ND->getIdentifier(); |
| 50 | if (!II || II->getName() != "std") |
| 51 | return false; |
| 52 | |
| 53 | DeclarationName N = TD->getDeclName(); |
| 54 | return llvm::StringRef(N.getAsString()) == "string"; |
| 55 | } |
| 56 | |
| 57 | //===----------------------------------------------------------------------===// |
| 58 | // CHECK: a llvm::StringRef should not be bound to a temporary std::string whose |
| 59 | // lifetime is shorter than the StringRef's. |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 60 | //===----------------------------------------------------------------------===// |
| 61 | |
| 62 | namespace { |
| 63 | class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { |
| 64 | BugReporter &BR; |
| 65 | public: |
| 66 | StringRefCheckerVisitor(BugReporter &br) : BR(br) {} |
| 67 | void VisitChildren(Stmt *S) { |
| 68 | for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; |
| 69 | I != E; ++I) |
| 70 | if (Stmt *child = *I) |
| 71 | Visit(child); |
| 72 | } |
| 73 | void VisitStmt(Stmt *S) { VisitChildren(S); } |
| 74 | void VisitDeclStmt(DeclStmt *DS); |
| 75 | private: |
| 76 | void VisitVarDecl(VarDecl *VD); |
Ted Kremenek | 005f09b | 2010-02-14 19:08:43 +0000 | [diff] [blame] | 77 | void CheckStringRefBoundtoTemporaryString(VarDecl *VD); |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 78 | }; |
| 79 | } // end anonymous namespace |
| 80 | |
| 81 | static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { |
| 82 | StringRefCheckerVisitor walker(BR); |
| 83 | walker.Visit(D->getBody()); |
| 84 | } |
| 85 | |
| 86 | void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { |
Ted Kremenek | 005f09b | 2010-02-14 19:08:43 +0000 | [diff] [blame] | 87 | VisitChildren(S); |
| 88 | |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 89 | for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) |
| 90 | if (VarDecl *VD = dyn_cast<VarDecl>(*I)) |
| 91 | VisitVarDecl(VD); |
| 92 | } |
| 93 | |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 94 | void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { |
| 95 | Expr *Init = VD->getInit(); |
| 96 | if (!Init) |
Ted Kremenek | 676ca15 | 2010-02-14 19:09:05 +0000 | [diff] [blame^] | 97 | return; |
Ted Kremenek | 55abf16 | 2010-02-14 19:08:36 +0000 | [diff] [blame] | 98 | |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 99 | // Pattern match for: |
| 100 | // llvm::StringRef x = call() (where call returns std::string) |
| 101 | if (!IsStringRef(VD->getType())) |
| 102 | return; |
| 103 | CXXExprWithTemporaries *Ex1 = dyn_cast<CXXExprWithTemporaries>(Init); |
| 104 | if (!Ex1) |
| 105 | return; |
| 106 | CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); |
| 107 | if (!Ex2 || Ex2->getNumArgs() != 1) |
| 108 | return; |
| 109 | ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); |
| 110 | if (!Ex3) |
| 111 | return; |
| 112 | CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); |
| 113 | if (!Ex4 || Ex4->getNumArgs() != 1) |
| 114 | return; |
| 115 | ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); |
| 116 | if (!Ex5) |
| 117 | return; |
| 118 | CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); |
| 119 | if (!Ex6 || !IsStdString(Ex6->getType())) |
| 120 | return; |
Ted Kremenek | 55abf16 | 2010-02-14 19:08:36 +0000 | [diff] [blame] | 121 | |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 122 | // Okay, badness! Report an error. |
| 123 | BR.EmitBasicReport("StringRef should not be bound to temporary " |
| 124 | "std::string that it outlives", "LLVM Conventions", |
| 125 | VD->getLocStart(), Init->getSourceRange()); |
| 126 | } |
| 127 | |
| 128 | //===----------------------------------------------------------------------===// |
| 129 | // Entry point for all checks. |
| 130 | //===----------------------------------------------------------------------===// |
| 131 | |
Ted Kremenek | 676ca15 | 2010-02-14 19:09:05 +0000 | [diff] [blame^] | 132 | static void ScanCodeDecls(DeclContext *DC, BugReporter &BR) { |
| 133 | for (DeclContext::decl_iterator I=DC->decls_begin(), E=DC->decls_end(); |
| 134 | I!=E ; ++I) { |
| 135 | |
| 136 | Decl *D = *I; |
| 137 | if (D->getBody()) { |
| 138 | CheckStringRefAssignedTemporary(D, BR); |
| 139 | } |
| 140 | |
| 141 | if (DeclContext *DC_child = dyn_cast<DeclContext>(D)) |
| 142 | ScanCodeDecls(DC_child, BR); |
| 143 | } |
Ted Kremenek | 6dd66ed | 2010-02-14 02:45:18 +0000 | [diff] [blame] | 144 | } |
Ted Kremenek | 676ca15 | 2010-02-14 19:09:05 +0000 | [diff] [blame^] | 145 | |
| 146 | void clang::CheckLLVMConventions(TranslationUnitDecl &TU, |
| 147 | BugReporter &BR) { |
| 148 | ScanCodeDecls(&TU, BR); |
| 149 | } |
| 150 | |