2015-11-06 09:23:33 +02:00
|
|
|
/* -*- 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 <string>
|
|
|
|
#include <iostream>
|
2016-11-07 15:59:37 +02:00
|
|
|
#include <unordered_map>
|
2015-11-06 09:23:33 +02:00
|
|
|
|
|
|
|
#include "plugin.hxx"
|
2016-11-07 15:59:37 +02:00
|
|
|
#include "check.hxx"
|
2015-11-06 09:23:33 +02:00
|
|
|
#include "clang/AST/CXXInheritance.h"
|
|
|
|
|
|
|
|
// Idea from tml.
|
2016-11-07 15:59:37 +02:00
|
|
|
// Check for OUString/char[] variables that are
|
2015-11-06 09:23:33 +02:00
|
|
|
// (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<OnceVar>, public loplugin::Plugin
|
|
|
|
{
|
|
|
|
public:
|
2016-11-07 15:59:37 +02:00
|
|
|
explicit OnceVar(InstantiationData const & data): Plugin(data) {}
|
2015-11-06 09:23:33 +02:00
|
|
|
|
|
|
|
virtual void run() override {
|
2016-11-07 15:59:37 +02:00
|
|
|
// ignore some files with problematic macros
|
|
|
|
std::string fn( compiler.getSourceManager().getFileEntryForID(
|
|
|
|
compiler.getSourceManager().getMainFileID())->getName() );
|
|
|
|
normalizeDotDotInFilePath(fn);
|
|
|
|
// TODO not possible here, need to figure out how to ignore cases where we index
|
|
|
|
// into the string
|
|
|
|
if (fn == SRCDIR "/vcl/source/filter/ixpm/xpmread.cxx")
|
|
|
|
return;
|
|
|
|
if (fn == SRCDIR "/sc/source/filter/excel/xiescher.cxx")
|
|
|
|
return;
|
|
|
|
// all the constants are nicely lined up at the top of the file, seems
|
|
|
|
// a pity to just inline a handful.
|
|
|
|
if (fn == SRCDIR "/sc/source/ui/docshell/docsh.cxx")
|
|
|
|
return;
|
|
|
|
if (fn == SRCDIR "/sw/source/core/text/EnhancedPDFExportHelper.cxx")
|
|
|
|
return;
|
|
|
|
if (fn == SRCDIR "/svgio/source/svgreader/svgtoken.cxx")
|
|
|
|
return;
|
|
|
|
// TODO explicit length array
|
|
|
|
if (fn == SRCDIR "/sal/qa/osl/file/osl_File.cxx")
|
|
|
|
return;
|
|
|
|
|
2015-11-06 09:23:33 +02:00
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
2016-11-07 15:59:37 +02:00
|
|
|
|
|
|
|
for (auto it = maVarUsesMap.cbegin(); it != maVarUsesMap.cend(); ++it)
|
|
|
|
{
|
|
|
|
if (it->second == 1)
|
|
|
|
{
|
|
|
|
report(DiagnosticsEngine::Warning,
|
|
|
|
"string/char[] 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];
|
|
|
|
}
|
|
|
|
}
|
2015-11-06 09:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitDeclRefExpr( const DeclRefExpr* );
|
|
|
|
|
|
|
|
private:
|
|
|
|
StringRef getFilename(SourceLocation loc);
|
2016-11-07 15:59:37 +02:00
|
|
|
|
|
|
|
struct SourceLocationHash
|
|
|
|
{
|
|
|
|
size_t operator()( SourceLocation const & sl ) const
|
|
|
|
{
|
|
|
|
return sl.getRawEncoding();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
std::unordered_map<SourceLocation, int, SourceLocationHash> maVarUsesMap;
|
|
|
|
std::unordered_map<SourceLocation, SourceRange, SourceLocationHash> maVarDeclSourceRangeMap;
|
|
|
|
std::unordered_map<SourceLocation, SourceRange, SourceLocationHash> maVarUseSourceRangeMap;
|
2015-11-06 09:23:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
StringRef OnceVar::getFilename(SourceLocation loc)
|
|
|
|
{
|
|
|
|
SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc);
|
|
|
|
StringRef name { compiler.getSourceManager().getFilename(spellingLocation) };
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnceVar::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
|
|
|
|
{
|
2016-11-07 15:59:37 +02:00
|
|
|
if (ignoreLocation(declRefExpr)) {
|
2015-11-06 09:23:33 +02:00
|
|
|
return true;
|
2016-11-07 15:59:37 +02:00
|
|
|
}
|
2015-11-06 09:23:33 +02:00
|
|
|
const Decl* decl = declRefExpr->getDecl();
|
|
|
|
if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const VarDecl * varDecl = dyn_cast<VarDecl>(decl)->getCanonicalDecl();
|
2016-11-07 15:59:37 +02:00
|
|
|
// ignore stuff in header files (which should really not be there, but anyhow)
|
|
|
|
if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation())) {
|
2015-11-06 09:23:33 +02:00
|
|
|
return true;
|
|
|
|
}
|
2016-11-07 15:59:37 +02:00
|
|
|
if (!varDecl->hasInit()) {
|
2015-11-06 09:23:33 +02:00
|
|
|
return true;
|
|
|
|
}
|
2016-11-07 15:59:37 +02:00
|
|
|
if (const StringLiteral* stringLit = dyn_cast<StringLiteral>(varDecl->getInit())) {
|
|
|
|
// ignore long literals, helps to make the code more legible
|
|
|
|
if (stringLit->getLength() > 40) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ok
|
|
|
|
} else {
|
|
|
|
const CXXConstructExpr* constructExpr = dyn_cast<CXXConstructExpr>(varDecl->getInit());
|
|
|
|
if (!constructExpr || constructExpr->getNumArgs() != 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const StringLiteral* stringLit2 = dyn_cast<StringLiteral>(varDecl->getInit());
|
|
|
|
if (!stringLit2) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ignore long literals, helps to make the code more legible
|
|
|
|
if (stringLit2->getLength() > 40) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-11-06 09:23:33 +02:00
|
|
|
}
|
|
|
|
SourceLocation loc = varDecl->getLocation();
|
|
|
|
|
|
|
|
// ignore cases like:
|
|
|
|
// const OUString("xxx") xxx;
|
|
|
|
// rtl_something(xxx.pData);
|
|
|
|
// and
|
|
|
|
// foo(&xxx);
|
|
|
|
// where we cannot inline the declaration.
|
2016-11-07 15:59:37 +02:00
|
|
|
auto const tc = loplugin::TypeCheck(varDecl->getType());
|
|
|
|
if (tc.Class("OUString").Namespace("rtl").GlobalNamespace()
|
|
|
|
&& (isa<MemberExpr>(parentStmt(declRefExpr))
|
|
|
|
|| isa<UnaryOperator>(parentStmt(declRefExpr))))
|
|
|
|
{
|
2015-11-06 09:23:33 +02:00
|
|
|
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: */
|