| //=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This defines LLVMConventionsChecker, a bunch of small little checks |
| // for checking specific coding conventions in the LLVM/Clang codebase. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/StmtVisitor.h" |
| #include "clang/Checker/Checkers/LocalCheckers.h" |
| #include "clang/Checker/BugReporter/BugReporter.h" |
| #include <string> |
| #include <llvm/ADT/StringRef.h> |
| |
| using namespace clang; |
| |
| //===----------------------------------------------------------------------===// |
| // Generic type checking routines. |
| //===----------------------------------------------------------------------===// |
| |
| static bool IsStringRef(QualType T) { |
| const RecordType *RT = T->getAs<RecordType>(); |
| if (!RT) |
| return false; |
| |
| return llvm::StringRef(QualType(RT, 0).getAsString()) == |
| "class llvm::StringRef"; |
| } |
| |
| static bool IsStdString(QualType T) { |
| if (const QualifiedNameType *QT = T->getAs<QualifiedNameType>()) |
| T = QT->getNamedType(); |
| |
| const TypedefType *TT = T->getAs<TypedefType>(); |
| if (!TT) |
| return false; |
| |
| const TypedefDecl *TD = TT->getDecl(); |
| const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(TD->getDeclContext()); |
| if (!ND) |
| return false; |
| const IdentifierInfo *II = ND->getIdentifier(); |
| if (!II || II->getName() != "std") |
| return false; |
| |
| DeclarationName N = TD->getDeclName(); |
| return llvm::StringRef(N.getAsString()) == "string"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // CHECK: a llvm::StringRef should not be bound to a temporary std::string whose |
| // lifetime is shorter than the StringRef's. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { |
| BugReporter &BR; |
| public: |
| StringRefCheckerVisitor(BugReporter &br) : BR(br) {} |
| void VisitChildren(Stmt *S) { |
| for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; |
| I != E; ++I) |
| if (Stmt *child = *I) |
| Visit(child); |
| } |
| void VisitStmt(Stmt *S) { VisitChildren(S); } |
| void VisitDeclStmt(DeclStmt *DS); |
| private: |
| void VisitVarDecl(VarDecl *VD); |
| void CheckStringRefBoundtoTemporaryString(VarDecl *VD); |
| }; |
| } // end anonymous namespace |
| |
| static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { |
| StringRefCheckerVisitor walker(BR); |
| walker.Visit(D->getBody()); |
| } |
| |
| void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { |
| VisitChildren(S); |
| |
| for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) |
| if (VarDecl *VD = dyn_cast<VarDecl>(*I)) |
| VisitVarDecl(VD); |
| } |
| |
| void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { |
| Expr *Init = VD->getInit(); |
| if (!Init) |
| return; |
| |
| // Pattern match for: |
| // llvm::StringRef x = call() (where call returns std::string) |
| if (!IsStringRef(VD->getType())) |
| return; |
| CXXExprWithTemporaries *Ex1 = dyn_cast<CXXExprWithTemporaries>(Init); |
| if (!Ex1) |
| return; |
| CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); |
| if (!Ex2 || Ex2->getNumArgs() != 1) |
| return; |
| ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); |
| if (!Ex3) |
| return; |
| CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); |
| if (!Ex4 || Ex4->getNumArgs() != 1) |
| return; |
| ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); |
| if (!Ex5) |
| return; |
| CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); |
| if (!Ex6 || !IsStdString(Ex6->getType())) |
| return; |
| |
| // Okay, badness! Report an error. |
| BR.EmitBasicReport("StringRef should not be bound to temporary " |
| "std::string that it outlives", "LLVM Conventions", |
| VD->getLocStart(), Init->getSourceRange()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Entry point for all checks. |
| //===----------------------------------------------------------------------===// |
| |
| void clang::CheckLLVMConventions(const Decl *D, BugReporter &BR) { |
| CheckStringRefAssignedTemporary(D, BR); |
| } |