/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include "plugin.hxx" #include "compat.hxx" #include "clang/AST/CXXInheritance.h" // Idea from tml. // Check for OUString variables that are // (1) initialised from a string literal // (2) only used in one spot // In which case, we might as well inline it. namespace { class OnceVar: public RecursiveASTVisitor, public loplugin::Plugin { public: explicit OnceVar(InstantiationData const & data): Plugin(data) {} virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } bool TraverseFunctionDecl( FunctionDecl* stmt ); bool VisitDeclRefExpr( const DeclRefExpr* ); private: bool mbChecking; std::map maVarUsesMap; std::map maVarDeclSourceRangeMap; std::map maVarUseSourceRangeMap; StringRef getFilename(SourceLocation loc); }; StringRef OnceVar::getFilename(SourceLocation loc) { SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc); StringRef name { compiler.getSourceManager().getFilename(spellingLocation) }; return name; } bool OnceVar::TraverseFunctionDecl(FunctionDecl * decl) { if (ignoreLocation(decl)) { return true; } if (!decl->hasBody()) { return true; } if ( decl != decl->getCanonicalDecl() ) { return true; } // some places, it makes the code worse StringRef aFileName = getFilename(decl->getLocStart()); if (aFileName.startswith(SRCDIR "/sal/qa/osl/module/osl_Module.cxx")) { return true; } maVarUsesMap.clear(); maVarDeclSourceRangeMap.clear(); mbChecking = true; TraverseStmt(decl->getBody()); mbChecking = false; for (auto it = maVarUsesMap.cbegin(); it != maVarUsesMap.cend(); ++it) { if (it->second == 1) { report(DiagnosticsEngine::Warning, "OUString var used only once, should be inlined", it->first) << maVarDeclSourceRangeMap[it->first]; report(DiagnosticsEngine::Note, "to this spot", maVarUseSourceRangeMap[it->first].getBegin()) << maVarUseSourceRangeMap[it->first]; } } return true; } bool OnceVar::VisitDeclRefExpr( const DeclRefExpr* declRefExpr ) { if (!mbChecking) return true; const Decl* decl = declRefExpr->getDecl(); if (!isa(decl) || isa(decl)) { return true; } const VarDecl * varDecl = dyn_cast(decl)->getCanonicalDecl(); if (!varDecl->hasInit() || varDecl->hasGlobalStorage()) { return true; } if (varDecl->getType().getUnqualifiedType().getAsString().find("OUString") == std::string::npos) { return true; } const CXXConstructExpr* constructExpr = dyn_cast(varDecl->getInit()); if (!constructExpr || constructExpr->getNumArgs() < 1) { return true; } if (!isa(constructExpr->getArg(0))) { return true; } SourceLocation loc = varDecl->getLocation(); // ignore cases like: // const OUString("xxx") xxx; // rtl_something(xxx.pData); // and // foo(&xxx); // where we cannot inline the declaration. if (isa(parentStmt(declRefExpr)) || isa(parentStmt(declRefExpr))) { maVarUsesMap[loc] = 2; return true; } if (maVarUsesMap.find(loc) == maVarUsesMap.end()) { maVarUsesMap[loc] = 1; maVarDeclSourceRangeMap[loc] = varDecl->getSourceRange(); maVarUseSourceRangeMap[loc] = declRefExpr->getSourceRange(); } else { maVarUsesMap[loc]++; } return true; } loplugin::Plugin::Registration< OnceVar > X("oncevar"); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */