From 184b3383bf2ade55a49c797d49b920102dca62ea Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sun, 14 Feb 2010 02:45:18 +0000 Subject: Add new static analyzer for checking LLVM coding conventions: -analyzer-check-llvm-conventions Currently these checks are intended to be largely syntactical, but may get more sophisticated over time. As an initial foray into this brave new world, emit a static analyzer warning when binding a temporary 'std::string' to an 'llvm::StringRef' where the lifetime of the 'std::string' does not outlive the 'llvm::StringRef'. llvm-svn: 96147 --- clang/lib/Checker/LLVMConventionsChecker.cpp | 127 +++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 clang/lib/Checker/LLVMConventionsChecker.cpp (limited to 'clang/lib/Checker/LLVMConventionsChecker.cpp') diff --git a/clang/lib/Checker/LLVMConventionsChecker.cpp b/clang/lib/Checker/LLVMConventionsChecker.cpp new file mode 100644 index 00000000000..82f6d2c7bde --- /dev/null +++ b/clang/lib/Checker/LLVMConventionsChecker.cpp @@ -0,0 +1,127 @@ +//=== 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 +#include + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Check if an llvm::StringRef is bound to temporary std::string whose lifetime +// is shorter than the StringRef's. +//===----------------------------------------------------------------------===// + +namespace { +class StringRefCheckerVisitor : public StmtVisitor { + 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); +}; +} // end anonymous namespace + +static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { + StringRefCheckerVisitor walker(BR); + walker.Visit(D->getBody()); +} + +void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { + for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) + if (VarDecl *VD = dyn_cast(*I)) + VisitVarDecl(VD); +} + +static bool IsStringRef(QualType T) { + const RecordType *RT = T->getAs(); + 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()) + T = QT->getNamedType(); + + const TypedefType *TT = T->getAs(); + if (!TT) + return false; + + const TypedefDecl *TD = TT->getDecl(); + const NamespaceDecl *ND = dyn_cast(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"; +} + +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(Init); + if (!Ex1) + return; + CXXConstructExpr *Ex2 = dyn_cast(Ex1->getSubExpr()); + if (!Ex2 || Ex2->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex3 = dyn_cast(Ex2->getArg(0)); + if (!Ex3) + return; + CXXConstructExpr *Ex4 = dyn_cast(Ex3->getSubExpr()); + if (!Ex4 || Ex4->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex5 = dyn_cast(Ex4->getArg(0)); + if (!Ex5) + return; + CXXBindTemporaryExpr *Ex6 = dyn_cast(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); +} -- cgit v1.2.3