new loplugin inlineablemethods
look for methods that are: (*) non-virtual (*) only called once (*) only called from inside their own class (*) small i.e. < 40 characters which are candidates for just having their code inlined Change-Id: I0e9e8125d140282cdcdd2a77374059b17b2fcd7d
This commit is contained in:
332
compilerplugins/clang/inlineablemethods.cxx
Normal file
332
compilerplugins/clang/inlineablemethods.cxx
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
/* -*- 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 <cassert>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
#include "clang/AST/Attr.h"
|
||||||
|
|
||||||
|
#include "plugin.hxx"
|
||||||
|
#include "compat.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
Methods that are only called from inside their own class, and are only called from one spot
|
||||||
|
|
||||||
|
|
||||||
|
TODO if a method has only one call-site, and that call site is inside a constructor
|
||||||
|
then it's probably worth inlining, since it's probably an old method that was intended to be shared amongst
|
||||||
|
multiple constructors
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct MyFuncInfo
|
||||||
|
{
|
||||||
|
std::string access;
|
||||||
|
std::string returnType;
|
||||||
|
std::string nameAndParams;
|
||||||
|
std::string sourceLocation;
|
||||||
|
|
||||||
|
};
|
||||||
|
bool operator < (const MyFuncInfo &lhs, const MyFuncInfo &rhs)
|
||||||
|
{
|
||||||
|
return std::tie(lhs.returnType, lhs.nameAndParams)
|
||||||
|
< std::tie(rhs.returnType, rhs.nameAndParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to limit the voluminous output a little
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, MyFuncInfo> calledFromMap;
|
||||||
|
static std::set<MyFuncInfo> definitionSet;
|
||||||
|
static std::set<MyFuncInfo> calledFromOutsideSet;
|
||||||
|
static std::set<MyFuncInfo> largeFunctionSet;
|
||||||
|
static std::set<MyFuncInfo> addressOfSet;
|
||||||
|
|
||||||
|
|
||||||
|
class InlineableMethods:
|
||||||
|
public RecursiveASTVisitor<InlineableMethods>, public loplugin::Plugin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit InlineableMethods(InstantiationData const & data): Plugin(data) {}
|
||||||
|
|
||||||
|
virtual void run() override
|
||||||
|
{
|
||||||
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
||||||
|
|
||||||
|
// dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
|
||||||
|
// writing to the same logfile
|
||||||
|
|
||||||
|
std::string output;
|
||||||
|
for (const MyFuncInfo & s : definitionSet)
|
||||||
|
output += "definition:\t" + s.access + "\t" + s.returnType + "\t" + s.nameAndParams + "\t" + s.sourceLocation + "\n";
|
||||||
|
for (const MyFuncInfo & s : calledFromOutsideSet)
|
||||||
|
output += "outside:\t" + s.returnType + "\t" + s.nameAndParams + "\n";
|
||||||
|
for (const std::pair<std::string,MyFuncInfo> & s : calledFromMap)
|
||||||
|
output += "calledFrom:\t" + s.first
|
||||||
|
+ "\t" + s.second.returnType + "\t" + s.second.nameAndParams + "\n";
|
||||||
|
for (const MyFuncInfo & s : largeFunctionSet)
|
||||||
|
output += "large:\t" + s.returnType + "\t" + s.nameAndParams + "\n";
|
||||||
|
for (const MyFuncInfo & s : addressOfSet)
|
||||||
|
output += "addrof:\t" + s.returnType + "\t" + s.nameAndParams + "\n";
|
||||||
|
ofstream myfile;
|
||||||
|
myfile.open( SRCDIR "/loplugin.inlineablemethods.log", ios::app | ios::out);
|
||||||
|
myfile << output;
|
||||||
|
myfile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldVisitTemplateInstantiations () const { return true; }
|
||||||
|
bool shouldVisitImplicitCode() const { return true; }
|
||||||
|
|
||||||
|
bool VisitFunctionDecl( const FunctionDecl* );
|
||||||
|
bool VisitDeclRefExpr( const DeclRefExpr* );
|
||||||
|
bool VisitMemberExpr( const MemberExpr* );
|
||||||
|
// interception methods for FunctionDecl and all its subclasses
|
||||||
|
bool TraverseFunctionDecl( FunctionDecl* );
|
||||||
|
bool TraverseCXXMethodDecl( CXXMethodDecl* );
|
||||||
|
bool TraverseCXXConstructorDecl( CXXConstructorDecl* );
|
||||||
|
bool TraverseCXXConversionDecl( CXXConversionDecl* );
|
||||||
|
bool TraverseCXXDestructorDecl( CXXDestructorDecl* );
|
||||||
|
|
||||||
|
private:
|
||||||
|
MyFuncInfo niceName(const FunctionDecl* functionDecl);
|
||||||
|
std::string toString(SourceLocation loc);
|
||||||
|
void functionTouchedFromExpr( const FunctionDecl* calleeFunctionDecl, const Expr* expr );
|
||||||
|
bool isCalleeFunctionInteresting( const FunctionDecl* );
|
||||||
|
void logCalledFrom(SourceLocation calleeSite, const FunctionDecl* functionDecl);
|
||||||
|
|
||||||
|
// I use traverse and a member variable because I cannot find a reliable way of walking back up the AST tree using the parentStmt() stuff
|
||||||
|
// TODO doesn't cope with nested functions
|
||||||
|
const FunctionDecl* mpTraversingFunction = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
MyFuncInfo InlineableMethods::niceName(const FunctionDecl* functionDecl)
|
||||||
|
{
|
||||||
|
if (functionDecl->getInstantiatedFromMemberFunction())
|
||||||
|
functionDecl = functionDecl->getInstantiatedFromMemberFunction();
|
||||||
|
else if (functionDecl->getClassScopeSpecializationPattern())
|
||||||
|
functionDecl = functionDecl->getClassScopeSpecializationPattern();
|
||||||
|
// workaround clang-3.5 issue
|
||||||
|
#if CLANG_VERSION >= 30600
|
||||||
|
else if (functionDecl->getTemplateInstantiationPattern())
|
||||||
|
functionDecl = functionDecl->getTemplateInstantiationPattern();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MyFuncInfo aInfo;
|
||||||
|
switch (functionDecl->getAccess())
|
||||||
|
{
|
||||||
|
case AS_public: aInfo.access = "public"; break;
|
||||||
|
case AS_private: aInfo.access = "private"; break;
|
||||||
|
case AS_protected: aInfo.access = "protected"; break;
|
||||||
|
default: aInfo.access = "unknown"; break;
|
||||||
|
}
|
||||||
|
if (!isa<CXXConstructorDecl>(functionDecl)) {
|
||||||
|
aInfo.returnType = compat::getReturnType(*functionDecl).getCanonicalType().getAsString();
|
||||||
|
} else {
|
||||||
|
aInfo.returnType = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isa<CXXMethodDecl>(functionDecl)) {
|
||||||
|
const CXXRecordDecl* recordDecl = dyn_cast<CXXMethodDecl>(functionDecl)->getParent();
|
||||||
|
aInfo.nameAndParams += recordDecl->getQualifiedNameAsString();
|
||||||
|
aInfo.nameAndParams += "::";
|
||||||
|
}
|
||||||
|
aInfo.nameAndParams += functionDecl->getNameAsString() + "(";
|
||||||
|
bool bFirst = true;
|
||||||
|
for (const ParmVarDecl *pParmVarDecl : compat::parameters(*functionDecl)) {
|
||||||
|
if (bFirst)
|
||||||
|
bFirst = false;
|
||||||
|
else
|
||||||
|
aInfo.nameAndParams += ",";
|
||||||
|
aInfo.nameAndParams += pParmVarDecl->getType().getCanonicalType().getAsString();
|
||||||
|
}
|
||||||
|
aInfo.nameAndParams += ")";
|
||||||
|
if (isa<CXXMethodDecl>(functionDecl) && dyn_cast<CXXMethodDecl>(functionDecl)->isConst()) {
|
||||||
|
aInfo.nameAndParams += " const";
|
||||||
|
}
|
||||||
|
|
||||||
|
aInfo.sourceLocation = toString( functionDecl->getLocation() );
|
||||||
|
|
||||||
|
return aInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InlineableMethods::toString(SourceLocation loc)
|
||||||
|
{
|
||||||
|
SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( loc );
|
||||||
|
StringRef name = compiler.getSourceManager().getFilename(expansionLoc);
|
||||||
|
std::string sourceLocation = std::string(name.substr(strlen(SRCDIR)+1)) + ":" + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
|
||||||
|
normalizeDotDotInFilePath(sourceLocation);
|
||||||
|
return sourceLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineableMethods::VisitFunctionDecl( const FunctionDecl* functionDecl )
|
||||||
|
{
|
||||||
|
const FunctionDecl* canonicalFunctionDecl = functionDecl->getCanonicalDecl();
|
||||||
|
if (!isCalleeFunctionInteresting(canonicalFunctionDecl)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
definitionSet.insert(niceName(canonicalFunctionDecl));
|
||||||
|
|
||||||
|
if (functionDecl->doesThisDeclarationHaveABody()) {
|
||||||
|
bool bLargeFunction = false;
|
||||||
|
if (const CompoundStmt* compoundStmt = dyn_cast<CompoundStmt>(functionDecl->getBody())) {
|
||||||
|
if (compoundStmt->size() > 1) {
|
||||||
|
bLargeFunction = true;
|
||||||
|
}
|
||||||
|
if (!bLargeFunction) {
|
||||||
|
auto s1 = compiler.getSourceManager().getCharacterData(compoundStmt->getLBracLoc());
|
||||||
|
auto s2 = compiler.getSourceManager().getCharacterData(compoundStmt->getRBracLoc());
|
||||||
|
bLargeFunction = (s2 - s1) > 40;
|
||||||
|
// any function that uses a parameter more than once
|
||||||
|
if (!bLargeFunction) {
|
||||||
|
StringRef bodyText(s1, s2-s1);
|
||||||
|
for (const ParmVarDecl* param : compat::parameters(*functionDecl)) {
|
||||||
|
StringRef name = param->getName();
|
||||||
|
if (name.empty())
|
||||||
|
continue;
|
||||||
|
size_t idx = bodyText.find(name);
|
||||||
|
if (idx != StringRef::npos && bodyText.find(name, idx+1) != StringRef::npos) {
|
||||||
|
bLargeFunction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bLargeFunction) {
|
||||||
|
largeFunctionSet.insert(niceName(canonicalFunctionDecl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineableMethods::TraverseFunctionDecl( FunctionDecl* p )
|
||||||
|
{
|
||||||
|
mpTraversingFunction = p;
|
||||||
|
bool ret = RecursiveASTVisitor::TraverseFunctionDecl(p);
|
||||||
|
mpTraversingFunction = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bool InlineableMethods::TraverseCXXMethodDecl( CXXMethodDecl* p )
|
||||||
|
{
|
||||||
|
mpTraversingFunction = p;
|
||||||
|
bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(p);
|
||||||
|
mpTraversingFunction = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bool InlineableMethods::TraverseCXXConstructorDecl( CXXConstructorDecl* p )
|
||||||
|
{
|
||||||
|
mpTraversingFunction = p;
|
||||||
|
bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(p);
|
||||||
|
mpTraversingFunction = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bool InlineableMethods::TraverseCXXConversionDecl( CXXConversionDecl* p )
|
||||||
|
{
|
||||||
|
mpTraversingFunction = p;
|
||||||
|
bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(p);
|
||||||
|
mpTraversingFunction = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bool InlineableMethods::TraverseCXXDestructorDecl( CXXDestructorDecl* p )
|
||||||
|
{
|
||||||
|
mpTraversingFunction = p;
|
||||||
|
bool ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(p);
|
||||||
|
mpTraversingFunction = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineableMethods::VisitMemberExpr( const MemberExpr* memberExpr )
|
||||||
|
{
|
||||||
|
const FunctionDecl* functionDecl = dyn_cast<FunctionDecl>(memberExpr->getMemberDecl());
|
||||||
|
if (functionDecl) {
|
||||||
|
functionTouchedFromExpr(functionDecl, memberExpr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineableMethods::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
|
||||||
|
{
|
||||||
|
const FunctionDecl* functionDecl = dyn_cast<FunctionDecl>(declRefExpr->getDecl());
|
||||||
|
if (functionDecl) {
|
||||||
|
functionTouchedFromExpr(functionDecl, declRefExpr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineableMethods::functionTouchedFromExpr( const FunctionDecl* calleeFunctionDecl, const Expr* expr )
|
||||||
|
{
|
||||||
|
if (!mpTraversingFunction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const FunctionDecl* canonicalFunctionDecl = calleeFunctionDecl->getCanonicalDecl();
|
||||||
|
if (!isCalleeFunctionInteresting(canonicalFunctionDecl)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logCalledFrom(expr->getLocStart(), canonicalFunctionDecl);
|
||||||
|
|
||||||
|
if (const UnaryOperator* unaryOp = dyn_cast_or_null<UnaryOperator>(parentStmt(expr))) {
|
||||||
|
if (unaryOp->getOpcode() == UO_AddrOf) {
|
||||||
|
addressOfSet.insert(niceName(canonicalFunctionDecl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
|
||||||
|
const CXXMethodDecl* callsiteParentMethodDecl = dyn_cast<CXXMethodDecl>(mpTraversingFunction);
|
||||||
|
if (!callsiteParentMethodDecl
|
||||||
|
|| calleeMethodDecl->getParent() != callsiteParentMethodDecl->getParent())
|
||||||
|
{
|
||||||
|
calledFromOutsideSet.insert(niceName(canonicalFunctionDecl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InlineableMethods::logCalledFrom(SourceLocation calleeLoc, const FunctionDecl* functionDecl)
|
||||||
|
{
|
||||||
|
functionDecl = functionDecl->getCanonicalDecl();
|
||||||
|
while (functionDecl->getTemplateInstantiationPattern())
|
||||||
|
functionDecl = functionDecl->getTemplateInstantiationPattern();
|
||||||
|
calledFromMap.emplace(toString(calleeLoc), niceName(functionDecl));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InlineableMethods::isCalleeFunctionInteresting(const FunctionDecl* functionDecl)
|
||||||
|
{
|
||||||
|
// ignore stuff that forms part of the stable URE interface
|
||||||
|
if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(
|
||||||
|
functionDecl->getNameInfo().getLoc()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isa<CXXDestructorDecl>(functionDecl)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (functionDecl->isDeleted() || functionDecl->isDefaulted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isa<CXXConstructorDecl>(functionDecl) && dyn_cast<CXXConstructorDecl>(functionDecl)->isCopyConstructor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!functionDecl->getLocation().isValid() || ignoreLocation(functionDecl)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
|
||||||
|
if (!methodDecl || methodDecl->isVirtual()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
loplugin::Plugin::Registration< InlineableMethods > X("inlineablemethods", true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
148
compilerplugins/clang/inlineablemethods.py
Executable file
148
compilerplugins/clang/inlineablemethods.py
Executable file
@@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import io
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
# globals
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
definitionSet = set() # set of tuple(return_type, name_and_params)
|
||||||
|
definitionToSourceLocationMap = dict()
|
||||||
|
calledFromDict = dict()
|
||||||
|
calledFromOutsideSet = set()
|
||||||
|
largeFunctionSet = set() # set of tuple(return_type, name_and_params)
|
||||||
|
addressOfSet = set() # set of tuple(return_type, name_and_params)
|
||||||
|
|
||||||
|
# clang does not always use exactly the same numbers in the type-parameter vars it generates
|
||||||
|
# so I need to substitute them to ensure we can match correctly.
|
||||||
|
normalizeTypeParamsRegex = re.compile(r"type-parameter-\d+-\d+")
|
||||||
|
def normalizeTypeParams( line ):
|
||||||
|
return normalizeTypeParamsRegex.sub("type-parameter-?-?", line)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
# primary input loop
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# The parsing here is designed to avoid grabbing stuff which is mixed in from gbuild.
|
||||||
|
# I have not yet found a way of suppressing the gbuild output.
|
||||||
|
with io.open("loplugin.inlineablemethods.log", "rb", buffering=1024*1024) as txt:
|
||||||
|
for line in txt:
|
||||||
|
tokens = line.strip().split("\t")
|
||||||
|
if tokens[0] == "definition:":
|
||||||
|
access = tokens[1]
|
||||||
|
returnType = tokens[2]
|
||||||
|
nameAndParams = tokens[3]
|
||||||
|
sourceLocation = tokens[4]
|
||||||
|
funcInfo = (normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams))
|
||||||
|
definitionSet.add(funcInfo)
|
||||||
|
definitionToSourceLocationMap[funcInfo] = sourceLocation
|
||||||
|
elif tokens[0] == "calledFrom:":
|
||||||
|
calleeLocation = tokens[1]
|
||||||
|
returnType = tokens[2]
|
||||||
|
nameAndParams = tokens[3]
|
||||||
|
funcInfo = (normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams))
|
||||||
|
if not funcInfo in calledFromDict:
|
||||||
|
calledFromDict[funcInfo] = set()
|
||||||
|
calledFromDict[funcInfo].add(calleeLocation)
|
||||||
|
elif tokens[0] == "outside:":
|
||||||
|
returnType = tokens[1]
|
||||||
|
nameAndParams = tokens[2]
|
||||||
|
calledFromOutsideSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
|
||||||
|
elif tokens[0] == "large:":
|
||||||
|
returnType = tokens[1]
|
||||||
|
nameAndParams = tokens[2]
|
||||||
|
largeFunctionSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
|
||||||
|
elif tokens[0] == "addrof:":
|
||||||
|
returnType = tokens[1]
|
||||||
|
nameAndParams = tokens[2]
|
||||||
|
addressOfSet.add((normalizeTypeParams(returnType), normalizeTypeParams(nameAndParams)))
|
||||||
|
else:
|
||||||
|
print( "unknown line: " + line)
|
||||||
|
|
||||||
|
# Invert the definitionToSourceLocationMap.
|
||||||
|
# If we see more than one method at the same sourceLocation, it's being autogenerated as part of a template
|
||||||
|
# and we should just ignore it.
|
||||||
|
sourceLocationToDefinitionMap = {}
|
||||||
|
for k, v in definitionToSourceLocationMap.iteritems():
|
||||||
|
sourceLocationToDefinitionMap[v] = sourceLocationToDefinitionMap.get(v, [])
|
||||||
|
sourceLocationToDefinitionMap[v].append(k)
|
||||||
|
for k, definitions in sourceLocationToDefinitionMap.iteritems():
|
||||||
|
if len(definitions) > 1:
|
||||||
|
for d in definitions:
|
||||||
|
definitionSet.remove(d)
|
||||||
|
|
||||||
|
def isOtherConstness( d, callSet ):
|
||||||
|
method = d[0] + " " + d[1]
|
||||||
|
# if this method is const, and there is a non-const variant of it, and the non-const variant is in use, then leave it alone
|
||||||
|
if d[0].startswith("const ") and d[1].endswith(" const"):
|
||||||
|
if ((d[0][6:],d[1][:-6]) in callSet):
|
||||||
|
return True
|
||||||
|
elif method.endswith(" const"):
|
||||||
|
method2 = method[:len(method)-6] # strip off " const"
|
||||||
|
if ((d[0],method2) in callSet):
|
||||||
|
return True
|
||||||
|
if method.endswith(" const") and ("::iterator" in method):
|
||||||
|
method2 = method[:len(method)-6] # strip off " const"
|
||||||
|
method2 = method2.replace("::const_iterator", "::iterator")
|
||||||
|
if ((d[0],method2) in callSet):
|
||||||
|
return True
|
||||||
|
# if this method is non-const, and there is a const variant of it, and the const variant is in use, then leave it alone
|
||||||
|
if (not method.endswith(" const")) and ((d[0],"const " + method + " const") in callSet):
|
||||||
|
return True
|
||||||
|
if (not method.endswith(" const")) and ("::iterator" in method):
|
||||||
|
method2 = method.replace("::iterator", "::const_iterator") + " const"
|
||||||
|
if ((d[0],method2) in callSet):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# sort the results using a "natural order" so sequences like [item1,item2,item10] sort nicely
|
||||||
|
def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
|
||||||
|
return [int(text) if text.isdigit() else text.lower()
|
||||||
|
for text in re.split(_nsre, s)]
|
||||||
|
def sort_set_by_natural_key(s):
|
||||||
|
return sorted(s, key=lambda v: natural_sort_key(v[1]))
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
# Methods that are only called from inside their own class, and are only called from one spot
|
||||||
|
# --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
tmp4set = set()
|
||||||
|
for d in definitionSet:
|
||||||
|
if d in calledFromOutsideSet:
|
||||||
|
continue
|
||||||
|
if isOtherConstness(d, calledFromOutsideSet):
|
||||||
|
continue
|
||||||
|
if d not in definitionToSourceLocationMap:
|
||||||
|
print("warning, method has no location: " + d[0] + " " + d[1])
|
||||||
|
continue
|
||||||
|
# ignore external code
|
||||||
|
if definitionToSourceLocationMap[d].startswith("external/"):
|
||||||
|
continue
|
||||||
|
# ignore constructors, calledFromOutsideSet does not provide accurate info for them
|
||||||
|
tokens = d[1].split("(")[0].split("::")
|
||||||
|
if len(tokens)>1 and tokens[-2] == tokens[-1]:
|
||||||
|
continue
|
||||||
|
# ignore large methods, which make the code clearer by being out of line
|
||||||
|
if d in largeFunctionSet:
|
||||||
|
continue
|
||||||
|
# ignore methods whose address we take
|
||||||
|
if d in addressOfSet:
|
||||||
|
continue
|
||||||
|
# ignore unused methods, leave them to the dedicated analysis
|
||||||
|
if d not in calledFromDict:
|
||||||
|
continue
|
||||||
|
# ignore methods called from more than one site
|
||||||
|
if len(calledFromDict[d]) > 1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
method = d[0] + " " + d[1]
|
||||||
|
tmp4set.add((method, definitionToSourceLocationMap[d]))
|
||||||
|
|
||||||
|
# print output, sorted by name and line number
|
||||||
|
with open("loplugin.inlineablemethods.report", "wt") as f:
|
||||||
|
for t in sort_set_by_natural_key(tmp4set):
|
||||||
|
f.write(t[1] + "\n")
|
||||||
|
f.write(" " + t[0] + "\n")
|
Reference in New Issue
Block a user