#include #include #include #include #include #include #include #include #include #include #include #include #include class Context { clang::ASTContext* m_pContext; public: Context() : m_pContext(nullptr) {} void setASTContext(clang::ASTContext& rContext) { m_pContext = &rContext; } clang::ASTContext* getASTContext() const { return m_pContext; } bool ignoreLocation(const clang::SourceLocation& rLocation) { clang::SourceLocation aLocation = m_pContext->getSourceManager().getExpansionLoc(rLocation); return m_pContext->getSourceManager().isInSystemHeader(aLocation); } clang::DiagnosticBuilder report(llvm::StringRef aString, clang::SourceLocation aLocation) const { clang::DiagnosticsEngine& rEngine = m_pContext->getDiagnostics(); return rEngine.Report(aLocation, rEngine.getDiagnosticIDs()->getCustomDiagID( clang::DiagnosticIDs::Level::Error, aString)); } }; /** * Finds auto usage which is not readable (type info not in the same line, nor * an iterator). */ class Visitor : public clang::RecursiveASTVisitor { Context& m_rContext; std::string m_aDeclRefName; public: Visitor(Context& rContext, clang::ASTContext& rASTContext) : m_rContext(rContext) { m_rContext.setASTContext(rASTContext); } bool hasTemplateArguments(clang::Expr* pExpr) { if (!pExpr) return false; auto pCallExpr = clang::dyn_cast(pExpr); if (!pCallExpr) { auto pExprWithCleanups = clang::dyn_cast(pExpr); if (!pExprWithCleanups) return false; pCallExpr = clang::dyn_cast( pExprWithCleanups->getSubExpr()); if (!pCallExpr) { auto pCXXConstructExpr = clang::dyn_cast( pExprWithCleanups->getSubExpr()); if (!pCXXConstructExpr || pCXXConstructExpr->getNumArgs() < 1) return false; auto pMaterializeTemporaryExpr = clang::dyn_cast( pCXXConstructExpr->getArg(0)); if (!pMaterializeTemporaryExpr) return false; auto pCXXBindTemporaryExpr = clang::dyn_cast( pMaterializeTemporaryExpr->GetTemporaryExpr()); if (!pCXXBindTemporaryExpr) return false; pCallExpr = clang::dyn_cast( pCXXBindTemporaryExpr->getSubExpr()); } } if (!pCallExpr || !pCallExpr->getCalleeDecl()) return false; auto pFunctionDecl = clang::dyn_cast(pCallExpr->getCalleeDecl()); if (!pFunctionDecl) return false; return pFunctionDecl->getTemplateSpecializationArgs() != nullptr; } bool VisitVarDecl(clang::VarDecl* pDecl) { if (m_rContext.ignoreLocation(pDecl->getLocation())) return true; clang::QualType aType = pDecl->getType(); std::string aTypeName = aType.getAsString(); if (aTypeName.find("iterator") != std::string::npos || aTypeName.find("Iterator") != std::string::npos) // Ignore iterators. return true; if (aTypeName.find("std::chrono::duration") != std::string::npos) // Unclear what to do here. return true; if (pDecl->hasInit()) { if (clang::isa(pDecl->getInit())) return true; auto pExprWithCleanups = clang::dyn_cast(pDecl->getInit()); if (pExprWithCleanups && clang::isa( pExprWithCleanups->getSubExpr())) return true; if (hasTemplateArguments(pDecl->getInit())) /* * Allow e.g. * auto pFoo = std::make_shared(); */ return true; } if (clang::isa(aType.getCanonicalType())) // Can't suggest what to spell out, ignore. return true; if (clang::isa(aType.getTypePtr())) m_rContext.report("harmful auto, consider spelling out %0 instead", pDecl->getLocation()) << pDecl->getSourceRange() << aType; return true; } }; class ASTConsumer : public clang::ASTConsumer { Context& m_rContext; public: ASTConsumer(Context& rContext) : m_rContext(rContext) {} virtual void HandleTranslationUnit(clang::ASTContext& rContext) { if (rContext.getDiagnostics().hasErrorOccurred()) return; Visitor aVisitor(m_rContext, rContext); aVisitor.TraverseDecl(rContext.getTranslationUnitDecl()); } }; class FrontendAction { Context& m_rContext; public: FrontendAction(Context& rContext) : m_rContext(rContext) {} std::unique_ptr newASTConsumer() { return llvm::make_unique(m_rContext); } }; int main(int argc, const char** argv) { llvm::cl::OptionCategory aCategory("find-harmful-auto options"); clang::tooling::CommonOptionsParser aParser(argc, argv, aCategory); clang::tooling::ClangTool aTool(aParser.getCompilations(), aParser.getSourcePathList()); Context aContext; FrontendAction aAction(aContext); std::unique_ptr pFactory = clang::tooling::newFrontendActionFactory(&aAction); return aTool.run(pFactory.get()); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */