diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2020-08-17 21:41:33 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2020-08-18 14:48:51 +0200 |
commit | 16d9d1d1122bbf9f6dc0748b41826a59a8551716 (patch) | |
tree | 0252fe6ebf630f9023c2840713bfc1906bcc36dd /compilerplugins | |
parent | e98694bd787ead49b08e8490999cae0122626d40 (diff) |
new loplugin:unusedvarsglobal
I copied and modified the unusedfields plugin to find unused
variables.
Change-Id: I18ed6daa202da70a2e564e4fe4760aed7ce070ba
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100891
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/unusedvarsglobal.cxx | 968 | ||||
-rwxr-xr-x | compilerplugins/clang/unusedvarsglobal.py | 118 | ||||
-rw-r--r-- | compilerplugins/clang/unusedvarsglobal.untouched.results | 280 |
3 files changed, 1366 insertions, 0 deletions
diff --git a/compilerplugins/clang/unusedvarsglobal.cxx b/compilerplugins/clang/unusedvarsglobal.cxx new file mode 100644 index 000000000000..e5ca57c4ba5a --- /dev/null +++ b/compilerplugins/clang/unusedvarsglobal.cxx @@ -0,0 +1,968 @@ +/* -*- 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/. + */ + +#if !defined _WIN32 //TODO, #include <sys/file.h> + +#include <cassert> +#include <string> +#include <iostream> +#include <fstream> +#include <unordered_set> +#include <vector> +#include <algorithm> +#include <sys/file.h> +#include <unistd.h> + +#include "config_clang.h" + +#include "plugin.hxx" +#include "compat.hxx" +#include "check.hxx" + +#if CLANG_VERSION >= 110000 +#include "clang/AST/ParentMapContext.h" +#endif + +/** +This performs two analyses: + (1) look for unused fields + (2) look for fields that are write-only + +We dmp a list of calls to methods, and a list of field definitions. +Then we will post-process the 2 lists and find the set of unused methods. + +Be warned that it produces around 5G of log file. + +The process goes something like this: + $ make check + $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='UnusedVarsGlobal' check + $ ./compilerplugins/clang/UnusedVarsGlobal.py + +and then + $ for dir in *; do make FORCE_COMPILE_ALL=1 UPDATE_FILES=$dir COMPILER_PLUGIN_TOOL='UnusedVarsGlobalremove' $dir; done +to auto-remove the method declarations + +Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around +to get it to work :-) + +*/ + +namespace +{ +struct MyVarInfo +{ + std::string fieldName; + std::string fieldType; + std::string sourceLocation; +}; +bool operator<(const MyVarInfo& lhs, const MyVarInfo& rhs) +{ + return std::tie(lhs.sourceLocation, lhs.fieldName) + < std::tie(rhs.sourceLocation, rhs.fieldName); +} + +// try to limit the voluminous output a little +static std::set<MyVarInfo> readFromSet; +static std::set<MyVarInfo> writeToSet; +static std::set<MyVarInfo> definitionSet; + +/** + * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything. + */ +class CallerWrapper +{ + const CallExpr* m_callExpr; + const CXXConstructExpr* m_cxxConstructExpr; + +public: + CallerWrapper(const CallExpr* callExpr) + : m_callExpr(callExpr) + , m_cxxConstructExpr(nullptr) + { + } + CallerWrapper(const CXXConstructExpr* cxxConstructExpr) + : m_callExpr(nullptr) + , m_cxxConstructExpr(cxxConstructExpr) + { + } + unsigned getNumArgs() const + { + return m_callExpr ? m_callExpr->getNumArgs() : m_cxxConstructExpr->getNumArgs(); + } + const Expr* getArg(unsigned i) const + { + return m_callExpr ? m_callExpr->getArg(i) : m_cxxConstructExpr->getArg(i); + } +}; +class CalleeWrapper +{ + const FunctionDecl* m_calleeFunctionDecl = nullptr; + const CXXConstructorDecl* m_cxxConstructorDecl = nullptr; + const FunctionProtoType* m_functionPrototype = nullptr; + +public: + explicit CalleeWrapper(const FunctionDecl* calleeFunctionDecl) + : m_calleeFunctionDecl(calleeFunctionDecl) + { + } + explicit CalleeWrapper(const CXXConstructExpr* cxxConstructExpr) + : m_cxxConstructorDecl(cxxConstructExpr->getConstructor()) + { + } + explicit CalleeWrapper(const FunctionProtoType* functionPrototype) + : m_functionPrototype(functionPrototype) + { + } + unsigned getNumParams() const + { + if (m_calleeFunctionDecl) + return m_calleeFunctionDecl->getNumParams(); + else if (m_cxxConstructorDecl) + return m_cxxConstructorDecl->getNumParams(); + else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end()) + // FunctionProtoType will assert if we call getParamTypes() and it has no params + return 0; + else + return m_functionPrototype->getParamTypes().size(); + } + const QualType getParamType(unsigned i) const + { + if (m_calleeFunctionDecl) + return m_calleeFunctionDecl->getParamDecl(i)->getType(); + else if (m_cxxConstructorDecl) + return m_cxxConstructorDecl->getParamDecl(i)->getType(); + else + return m_functionPrototype->getParamTypes()[i]; + } + std::string getNameAsString() const + { + if (m_calleeFunctionDecl) + return m_calleeFunctionDecl->getNameAsString(); + else if (m_cxxConstructorDecl) + return m_cxxConstructorDecl->getNameAsString(); + else + return ""; + } + CXXMethodDecl const* getAsCXXMethodDecl() const + { + if (m_calleeFunctionDecl) + return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl); + return nullptr; + } +}; + +class UnusedVarsGlobal : public loplugin::FilteringPlugin<UnusedVarsGlobal> +{ +public: + explicit UnusedVarsGlobal(loplugin::InstantiationData const& data) + : FilteringPlugin(data) + { + } + + virtual void run() override; + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + + bool VisitVarDecl(const VarDecl*); + bool VisitDeclRefExpr(const DeclRefExpr*); + bool TraverseCXXMethodDecl(CXXMethodDecl*); + bool TraverseFunctionDecl(FunctionDecl*); + bool TraverseIfStmt(IfStmt*); + +private: + MyVarInfo niceName(const VarDecl*); + void checkIfReadFrom(const VarDecl* fieldDecl, const DeclRefExpr* declRefExpr); + void checkIfWrittenTo(const VarDecl* fieldDecl, const DeclRefExpr* declRefExpr); + bool isSomeKindOfZero(const Expr* arg); + bool checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl); + bool IsPassedByNonConst(const VarDecl* fieldDecl, const Stmt* child, CallerWrapper callExpr, + CalleeWrapper calleeFunctionDecl); + llvm::Optional<CalleeWrapper> getCallee(CallExpr const*); + + // For reasons I do not understand, parentFunctionDecl() is not reliable, so + // we store the parent function on the way down the AST. + FunctionDecl* insideFunctionDecl = nullptr; + std::vector<VarDecl const*> insideConditionalCheckOfVarSet; +}; + +void UnusedVarsGlobal::run() +{ + 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 MyVarInfo& s : readFromSet) + output += "read:\t" + s.sourceLocation + "\t" + s.fieldName + "\n"; + for (const MyVarInfo& s : writeToSet) + output += "write:\t" + s.sourceLocation + "\t" + s.fieldName + "\n"; + for (const MyVarInfo& s : definitionSet) + output + += "definition:\t" + s.fieldName + "\t" + s.fieldType + "\t" + s.sourceLocation + "\n"; + std::ofstream myfile; + myfile.open(WORKDIR "/loplugin.unusedvarsglobal.log", std::ios::app | std::ios::out); + myfile << output; + myfile.close(); +} + +MyVarInfo UnusedVarsGlobal::niceName(const VarDecl* varDecl) +{ + MyVarInfo aInfo; + + aInfo.fieldName = varDecl->getNameAsString(); + // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need + size_t idx = aInfo.fieldName.find(SRCDIR); + if (idx != std::string::npos) + { + aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), ""); + } + aInfo.fieldType = varDecl->getType().getAsString(); + + SourceLocation expansionLoc + = compiler.getSourceManager().getExpansionLoc(varDecl->getLocation()); + StringRef name = getFilenameOfLocation(expansionLoc); + aInfo.sourceLocation + = std::string(name.substr(strlen(SRCDIR) + 1)) + ":" + + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc)); + loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation); + + return aInfo; +} + +bool UnusedVarsGlobal::VisitVarDecl(const VarDecl* varDecl) +{ + varDecl = varDecl->getCanonicalDecl(); + if (isa<ParmVarDecl>(varDecl)) + return true; + if (varDecl->isConstexpr()) + return true; + if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl)) + return true; + // ignore stuff that forms part of the stable URE interface + if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation()))) + return true; + + auto initExpr = varDecl->getAnyInitializer(); + if (initExpr && !isSomeKindOfZero(initExpr)) + writeToSet.insert(niceName(varDecl)); + + definitionSet.insert(niceName(varDecl)); + return true; +} + +bool UnusedVarsGlobal::VisitDeclRefExpr(const DeclRefExpr* declRefExpr) +{ + const Decl* decl = declRefExpr->getDecl(); + const VarDecl* varDecl = dyn_cast<VarDecl>(decl); + if (!varDecl) + return true; + if (isa<ParmVarDecl>(varDecl)) + return true; + if (varDecl->isConstexpr()) + return true; + varDecl = varDecl->getCanonicalDecl(); + if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl)) + return true; + // ignore stuff that forms part of the stable URE interface + if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation()))) + return true; + checkIfReadFrom(varDecl, declRefExpr); + checkIfWrittenTo(varDecl, declRefExpr); + return true; +} + +/** + Does the expression being used to initialise a field value evaluate to + the same as a default value? + */ +bool UnusedVarsGlobal::isSomeKindOfZero(const Expr* arg) +{ + assert(arg); + if (arg->isValueDependent()) + return false; + if (arg->getType().isNull()) + return false; + if (isa<CXXDefaultArgExpr>(arg)) + arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr(); + arg = arg->IgnoreParenCasts(); + // ignore this, it seems to trigger an infinite recursion + if (isa<UnaryExprOrTypeTraitExpr>(arg)) + return false; + if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(arg)) + return cxxConstructExpr->getConstructor()->isDefaultConstructor(); + APSInt x1; + if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext())) + return x1 == 0; + if (isa<CXXNullPtrLiteralExpr>(arg)) + return true; + if (isa<MaterializeTemporaryExpr>(arg)) + { + const CXXBindTemporaryExpr* strippedArg + = dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts()); + if (strippedArg) + { + auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr()); + if (temp->getNumArgs() == 0) + { + if (loplugin::TypeCheck(temp->getType()) + .Class("OUString") + .Namespace("rtl") + .GlobalNamespace()) + return true; + if (loplugin::TypeCheck(temp->getType()) + .Class("OString") + .Namespace("rtl") + .GlobalNamespace()) + return true; + return false; + } + } + } + + // Get the expression contents. + // This helps us find params which are always initialised with something like "OUString()". + SourceManager& SM = compiler.getSourceManager(); + SourceLocation startLoc = compat::getBeginLoc(arg); + SourceLocation endLoc = compat::getEndLoc(arg); + const char* p1 = SM.getCharacterData(startLoc); + const char* p2 = SM.getCharacterData(endLoc); + if (!p1 || !p2 || (p2 - p1) < 0 || (p2 - p1) > 40) + return false; + unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts()); + std::string s(p1, p2 - p1 + n); + // strip linefeed and tab characters so they don't interfere with the parsing of the log file + std::replace(s.begin(), s.end(), '\r', ' '); + std::replace(s.begin(), s.end(), '\n', ' '); + std::replace(s.begin(), s.end(), '\t', ' '); + + // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing + if (s == "OUString()") + return true; + else if (s == "OString()") + return true; + else if (s == "aEmptyOUStr") //sw + return true; + else if (s == "EMPTY_OUSTRING") //sc + return true; + else if (s == "GetEmptyOUString()") //sc + return true; + return false; +} + +static char easytolower(char in) +{ + if (in <= 'Z' && in >= 'A') + return in - ('Z' - 'z'); + return in; +} + +bool startswith(const std::string& rStr, const char* pSubStr) +{ + return rStr.compare(0, strlen(pSubStr), pSubStr) == 0; +} + +bool UnusedVarsGlobal::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl) +{ + auto copy2 = insideFunctionDecl; + insideFunctionDecl = cxxMethodDecl; + bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl); + insideFunctionDecl = copy2; + return ret; +} + +bool UnusedVarsGlobal::TraverseFunctionDecl(FunctionDecl* functionDecl) +{ + auto copy2 = insideFunctionDecl; + insideFunctionDecl = functionDecl; + bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl); + insideFunctionDecl = copy2; + return ret; +} + +bool UnusedVarsGlobal::TraverseIfStmt(IfStmt* ifStmt) +{ + VarDecl const* varDecl = nullptr; + Expr const* cond = ifStmt->getCond()->IgnoreParenImpCasts(); + + if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(cond)) + { + if (auto cxxConvert = dyn_cast_or_null<CXXConversionDecl>(memberCallExpr->getMethodDecl())) + { + if (cxxConvert->getConversionType()->isBooleanType()) + if (auto declRefExpr = dyn_cast<DeclRefExpr>( + memberCallExpr->getImplicitObjectArgument()->IgnoreParenImpCasts())) + if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))) + insideConditionalCheckOfVarSet.push_back(varDecl); + } + } + else if (auto declRefExpr = dyn_cast<DeclRefExpr>(cond)) + { + if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))) + insideConditionalCheckOfVarSet.push_back(varDecl); + } + + bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt); + if (varDecl) + insideConditionalCheckOfVarSet.pop_back(); + return ret; +} + +void UnusedVarsGlobal::checkIfReadFrom(const VarDecl* varDecl, const DeclRefExpr* declRefExpr) +{ + auto parentsRange = compiler.getASTContext().getParents(*declRefExpr); + const Stmt* child = declRefExpr; + const Stmt* parent + = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>(); + // walk up the tree until we find something interesting + bool bPotentiallyReadFrom = false; + bool bDump = false; + auto walkUp = [&]() { + child = parent; + auto parentsRange = compiler.getASTContext().getParents(*parent); + parent = parentsRange.begin() == parentsRange.end() ? nullptr + : parentsRange.begin()->get<Stmt>(); + }; + do + { + if (!parent) + { + // check if we're inside a CXXCtorInitializer or a VarDecl + auto parentsRange = compiler.getASTContext().getParents(*child); + if (parentsRange.begin() != parentsRange.end()) + { + const Decl* decl = parentsRange.begin()->get<Decl>(); + if (decl && (isa<CXXConstructorDecl>(decl) || isa<VarDecl>(decl))) + bPotentiallyReadFrom = true; + } + if (!bPotentiallyReadFrom) + return; + break; + } + if (isa<CXXReinterpretCastExpr>(parent)) + { + // once we see one of these, there is not much useful we can know + bPotentiallyReadFrom = true; + break; + } + else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent) + || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent) + || isa<ExprWithCleanups>(parent)) + { + walkUp(); + } + else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) + { + UnaryOperator::Opcode op = unaryOperator->getOpcode(); + if (declRefExpr->getType()->isArrayType() && op == UO_Deref) + { + // ignore, deref'ing an array does not count as a read + } + else if (op == UO_AddrOf || op == UO_Deref || op == UO_Plus || op == UO_Minus + || op == UO_Not || op == UO_LNot) + { + bPotentiallyReadFrom = true; + break; + } + /* The following are technically reads, but from a code-sense they're more of a write/modify, so + ignore them to find interesting fields that only modified, not usefully read: + UO_PreInc / UO_PostInc / UO_PreDec / UO_PostDec + But we still walk up in case the result of the expression is used in a read sense. + */ + walkUp(); + } + else if (auto caseStmt = dyn_cast<CaseStmt>(parent)) + { + bPotentiallyReadFrom = caseStmt->getLHS() == child || caseStmt->getRHS() == child; + break; + } + else if (auto ifStmt = dyn_cast<IfStmt>(parent)) + { + bPotentiallyReadFrom = ifStmt->getCond() == child; + break; + } + else if (auto doStmt = dyn_cast<DoStmt>(parent)) + { + bPotentiallyReadFrom = doStmt->getCond() == child; + break; + } + else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) + { + if (arraySubscriptExpr->getIdx() == child) + { + bPotentiallyReadFrom = true; + break; + } + walkUp(); + } + else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) + { + BinaryOperator::Opcode op = binaryOp->getOpcode(); + const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign + || op == BO_RemAssign || op == BO_AddAssign + || op == BO_SubAssign || op == BO_ShlAssign + || op == BO_ShrAssign || op == BO_AndAssign + || op == BO_XorAssign || op == BO_OrAssign; + if (binaryOp->getLHS() == child && assignmentOp) + break; + else + { + bPotentiallyReadFrom = true; + break; + } + } + else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) + { + auto op = operatorCallExpr->getOperator(); + const bool assignmentOp = op == OO_Equal || op == OO_StarEqual || op == OO_SlashEqual + || op == OO_PercentEqual || op == OO_PlusEqual + || op == OO_MinusEqual || op == OO_LessLessEqual + || op == OO_AmpEqual || op == OO_CaretEqual + || op == OO_PipeEqual; + if (operatorCallExpr->getArg(0) == child && assignmentOp) + break; + else if (op == OO_GreaterGreaterEqual && operatorCallExpr->getArg(1) == child) + break; // this is a write-only call + else + { + bPotentiallyReadFrom = true; + break; + } + } + else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) + { + bool bWriteOnlyCall = false; + const CXXMethodDecl* callee = cxxMemberCallExpr->getMethodDecl(); + if (callee) + { + const Expr* tmp = dyn_cast<Expr>(child); + if (tmp->isBoundMemberFunction(compiler.getASTContext())) + { + tmp = dyn_cast<MemberExpr>(tmp)->getBase(); + } + if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp) + { + // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute + // which we could scatter around. + std::string name = callee->getNameAsString(); + std::transform(name.begin(), name.end(), name.begin(), easytolower); + if (startswith(name, "emplace") || name == "insert" || name == "erase" + || name == "remove" || name == "remove_if" || name == "sort" + || name == "push_back" || name == "pop_back" || name == "push_front" + || name == "pop_front" || name == "reserve" || name == "resize" + || name == "reset" || name == "clear" || name == "fill") + // write-only modifications to collections + bWriteOnlyCall = true; + else if (name == "dispose" || name == "disposeAndClear" || name == "swap") + // we're abusing the write-only analysis here to look for fields which don't have anything useful + // being done to them, so we're ignoring things like std::vector::clear, std::vector::swap, + // and VclPtr::disposeAndClear + bWriteOnlyCall = true; + } + } + if (!bWriteOnlyCall) + bPotentiallyReadFrom = true; + break; + } + else if (auto callExpr = dyn_cast<CallExpr>(parent)) + { + bool bWriteOnlyCall = false; + // check for calls to ReadXXX(foo) type methods, where foo is write-only + auto callee = getCallee(callExpr); + if (callee) + { + // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute + // which we could scatter around. + std::string name = callee->getNameAsString(); + std::transform(name.begin(), name.end(), name.begin(), easytolower); + if (startswith(name, "read")) + // this is a write-only call + bWriteOnlyCall = true; + } + if (!bWriteOnlyCall) + bPotentiallyReadFrom = true; + break; + } + else if (isa<ReturnStmt>(parent) || isa<CXXConstructExpr>(parent) + || isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent) + || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent) + || isa<ForStmt>(parent) || isa<InitListExpr>(parent) + || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent) + || isa<MaterializeTemporaryExpr>(parent)) + { + bPotentiallyReadFrom = true; + break; + } + else if (isa<CXXDeleteExpr>(parent) || isa<UnaryExprOrTypeTraitExpr>(parent) + || isa<CXXUnresolvedConstructExpr>(parent) || isa<CompoundStmt>(parent) + || isa<LabelStmt>(parent) || isa<CXXForRangeStmt>(parent) + || isa<CXXTypeidExpr>(parent) || isa<DefaultStmt>(parent) + || isa<GCCAsmStmt>(parent) || isa<LambdaExpr>(parent) // TODO + || isa<CXXDefaultArgExpr>(parent) || isa<AtomicExpr>(parent) + || isa<VAArgExpr>(parent) || isa<DeclRefExpr>(parent) +#if CLANG_VERSION >= 80000 + || isa<ConstantExpr>(parent) +#endif + || isa<SubstNonTypeTemplateParmExpr>(parent)) + { + break; + } + else + { + bPotentiallyReadFrom = true; + bDump = true; + break; + } + } while (true); + + if (bDump) + { + report(DiagnosticsEngine::Warning, "oh dear, what can the matter be?", + compat::getBeginLoc(declRefExpr)) + << declRefExpr->getSourceRange(); + report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent)) + << parent->getSourceRange(); + parent->dump(); + declRefExpr->dump(); + } + + if (bPotentiallyReadFrom) + readFromSet.insert(niceName(varDecl)); +} + +void UnusedVarsGlobal::checkIfWrittenTo(const VarDecl* varDecl, const DeclRefExpr* declRefExpr) +{ + // if we're inside a block that looks like + // if (varDecl) + // ... + // then writes to this field don't matter, because unless we find another write to this field, this field is dead + if (std::find(insideConditionalCheckOfVarSet.begin(), insideConditionalCheckOfVarSet.end(), + varDecl) + != insideConditionalCheckOfVarSet.end()) + return; + + auto parentsRange = compiler.getASTContext().getParents(*declRefExpr); + const Stmt* child = declRefExpr; + const Stmt* parent + = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>(); + // walk up the tree until we find something interesting + bool bPotentiallyWrittenTo = false; + bool bDump = false; + auto walkUp = [&]() { + child = parent; + auto parentsRange = compiler.getASTContext().getParents(*parent); + parent = parentsRange.begin() == parentsRange.end() ? nullptr + : parentsRange.begin()->get<Stmt>(); + }; + do + { + if (!parent) + { + // check if we have an expression like + // int& r = m_field; + auto parentsRange = compiler.getASTContext().getParents(*child); + if (parentsRange.begin() != parentsRange.end()) + { + auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>()); + // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement, + // which is of type 'T&&' and also an l-value-ref ? + if (varDecl && !varDecl->isImplicit() + && loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst()) + { + bPotentiallyWrittenTo = true; + } + } + break; + } + if (isa<CXXReinterpretCastExpr>(parent)) + { + // once we see one of these, there is not much useful we can know + bPotentiallyWrittenTo = true; + break; + } + else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent) + || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent) + || isa<ExprWithCleanups>(parent)) + { + walkUp(); + } + else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) + { + UnaryOperator::Opcode op = unaryOperator->getOpcode(); + if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc + || op == UO_PreDec) + { + bPotentiallyWrittenTo = true; + } + break; + } + else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) + { + if (arraySubscriptExpr->getIdx() == child) + break; + walkUp(); + } + else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) + { + auto callee = getCallee(operatorCallExpr); + if (callee) + { + // if calling a non-const operator on the field + auto calleeMethodDecl = callee->getAsCXXMethodDecl(); + if (calleeMethodDecl && operatorCallExpr->getArg(0) == child) + { + if (!calleeMethodDecl->isConst()) + bPotentiallyWrittenTo + = checkForWriteWhenUsingCollectionType(calleeMethodDecl); + } + else if (IsPassedByNonConst(varDecl, child, operatorCallExpr, *callee)) + { + bPotentiallyWrittenTo = true; + } + } + else + bPotentiallyWrittenTo = true; // conservative, could improve + break; + } + else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) + { + const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl(); + if (calleeMethodDecl) + { + // if calling a non-const method on the field + const Expr* tmp = dyn_cast<Expr>(child); + if (tmp->isBoundMemberFunction(compiler.getASTContext())) + { + tmp = dyn_cast<MemberExpr>(tmp)->getBase(); + } + if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp) + { + if (!calleeMethodDecl->isConst()) + bPotentiallyWrittenTo + = checkForWriteWhenUsingCollectionType(calleeMethodDecl); + break; + } + else if (IsPassedByNonConst(varDecl, child, cxxMemberCallExpr, + CalleeWrapper(calleeMethodDecl))) + bPotentiallyWrittenTo = true; + } + else + bPotentiallyWrittenTo = true; // can happen in templates + break; + } + else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent)) + { + if (IsPassedByNonConst(varDecl, child, cxxConstructExpr, + CalleeWrapper(cxxConstructExpr))) + bPotentiallyWrittenTo = true; + break; + } + else if (auto callExpr = dyn_cast<CallExpr>(parent)) + { + auto callee = getCallee(callExpr); + if (callee) + { + if (IsPassedByNonConst(varDecl, child, callExpr, *callee)) + bPotentiallyWrittenTo = true; + } + else + bPotentiallyWrittenTo = true; // conservative, could improve + break; + } + else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) + { + BinaryOperator::Opcode op = binaryOp->getOpcode(); + const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign + || op == BO_RemAssign || op == BO_AddAssign + || op == BO_SubAssign || op == BO_ShlAssign + || op == BO_ShrAssign || op == BO_AndAssign + || op == BO_XorAssign || op == BO_OrAssign; + if (assignmentOp) + { + if (binaryOp->getLHS() == child) + bPotentiallyWrittenTo = true; + else if (loplugin::TypeCheck(binaryOp->getLHS()->getType()) + .LvalueReference() + .NonConst()) + // if the LHS is a non-const reference, we could write to the field later on + bPotentiallyWrittenTo = true; + } + break; + } + else if (isa<ReturnStmt>(parent)) + { + if (insideFunctionDecl) + { + auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType()); + if (tc.LvalueReference().NonConst()) + bPotentiallyWrittenTo = true; + } + break; + } + else if (isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent) + || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent) + || isa<ForStmt>(parent) || isa<InitListExpr>(parent) + || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent) + || isa<MaterializeTemporaryExpr>(parent) || isa<IfStmt>(parent) + || isa<DoStmt>(parent) || isa<CXXDeleteExpr>(parent) + || isa<UnaryExprOrTypeTraitExpr>(parent) || isa<CXXUnresolvedConstructExpr>(parent) + || isa<CompoundStmt>(parent) || isa<LabelStmt>(parent) + || isa<CXXForRangeStmt>(parent) || isa<CXXTypeidExpr>(parent) + || isa<DefaultStmt>(parent) || isa<GCCAsmStmt>(parent) +#if CLANG_VERSION >= 80000 + || isa<ConstantExpr>(parent) +#endif + || isa<AtomicExpr>(parent) || isa<CXXDefaultArgExpr>(parent) + || isa<VAArgExpr>(parent) || isa<DeclRefExpr>(parent) + || isa<SubstNonTypeTemplateParmExpr>(parent) || isa<LambdaExpr>(parent)) // TODO + { + break; + } + else + { + bPotentiallyWrittenTo = true; + bDump = true; + break; + } + } while (true); + + if (bDump) + { + report(DiagnosticsEngine::Warning, "oh dear, what can the matter be? writtenTo=%0", + compat::getBeginLoc(declRefExpr)) + << bPotentiallyWrittenTo << declRefExpr->getSourceRange(); + if (parent) + { + report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent)) + << parent->getSourceRange(); + parent->dump(); + } + declRefExpr->dump(); + varDecl->getType()->dump(); + } + + if (bPotentiallyWrittenTo) + writeToSet.insert(niceName(varDecl)); +} + +// return true if this not a collection type, or if it is a collection type, and we might be writing to it +bool UnusedVarsGlobal::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl) +{ + auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent()); + bool listLike = false, setLike = false, mapLike = false, cssSequence = false; + if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace() + || tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace()) + { + listLike = true; + } + else if (tc.Class("set").StdNamespace() || tc.Class("unordered_set").StdNamespace()) + { + setLike = true; + } + else if (tc.Class("map").StdNamespace() || tc.Class("unordered_map").StdNamespace()) + { + mapLike = true; + } + else if (tc.Class("Sequence") + .Namespace("uno") + .Namespace("star") + .Namespace("sun") + .Namespace("com") + .GlobalNamespace()) + { + cssSequence = true; + } + else + return true; + + if (calleeMethodDecl->isOverloadedOperator()) + { + auto oo = calleeMethodDecl->getOverloadedOperator(); + if (oo == OO_Equal) + return true; + // This is operator[]. We only care about things that add elements to the collection. + // if nothing modifies the size of the collection, then nothing useful + // is stored in it. + if (listLike) + return false; + return true; + } + + auto name = calleeMethodDecl->getName(); + if (listLike || setLike || mapLike) + { + if (name == "reserve" || name == "shrink_to_fit" || name == "clear" || name == "erase" + || name == "pop_back" || name == "pop_front" || name == "front" || name == "back" + || name == "data" || name == "remove" || name == "remove_if" || name == "unique" + || name == "sort" || name == "begin" || name == "end" || name == "rbegin" + || name == "rend" || name == "at" || name == "find" || name == "equal_range" + || name == "lower_bound" || name == "upper_bound") + return false; + } + if (cssSequence) + { + if (name == "getArray" || name == "begin" || name == "end") + return false; + } + + return true; +} + +bool UnusedVarsGlobal::IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child, + CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl) +{ + unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams()); + // if it's an array, passing it by value to a method typically means the + // callee takes a pointer and can modify the array + if (varDecl->getType()->isConstantArrayType()) + { + for (unsigned i = 0; i < len; ++i) + if (callExpr.getArg(i) == child) + if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst()) + return true; + } + else + { + for (unsigned i = 0; i < len; ++i) + if (callExpr.getArg(i) == child) + if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)) + .LvalueReference() + .NonConst()) + return true; + } + return false; +} + +llvm::Optional<CalleeWrapper> UnusedVarsGlobal::getCallee(CallExpr const* callExpr) +{ + FunctionDecl const* functionDecl = callExpr->getDirectCallee(); + if (functionDecl) + return CalleeWrapper(functionDecl); + + // Extract the functionprototype from a type + clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr(); + if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>()) + { + if (auto prototype = pointerType->getPointeeType() + ->getUnqualifiedDesugaredType() + ->getAs<FunctionProtoType>()) + { + return CalleeWrapper(prototype); + } + } + + return llvm::Optional<CalleeWrapper>(); +} + +loplugin::Plugin::Registration<UnusedVarsGlobal> X("unusedvarsglobal", false); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/compilerplugins/clang/unusedvarsglobal.py b/compilerplugins/clang/unusedvarsglobal.py new file mode 100755 index 000000000000..1231edf9bd18 --- /dev/null +++ b/compilerplugins/clang/unusedvarsglobal.py @@ -0,0 +1,118 @@ +#!/usr/bin/python2 + +import sys +import re +import io + +definitionSet = set() +readFromSet = set() +writeToSet = set() +defToTypeMap = dict() + +def parseFieldInfo( tokens ): + return (tokens[1].strip(), tokens[2].strip()) + +with io.open("workdir/loplugin.unusedvarsglobal.log", "rb", buffering=1024*1024) as txt: + for line in txt: + try: + tokens = line.strip().split("\t") + if tokens[0] == "definition:": + srcLoc = tokens[3] + # ignore external source code + if (srcLoc.startswith("external/")): + continue + # ignore build folder + if (srcLoc.startswith("workdir/")): + continue + varname = tokens[1].strip() + vartype = tokens[2].strip() + if vartype.startswith("const "): + vartype = vartype[6:] + if vartype.startswith("class "): + vartype = vartype[6:] + if vartype.startswith("struct "): + vartype = vartype[7:] + if vartype.startswith("::"): + vartype = vartype[2:] + fieldInfo = (srcLoc, varname) + definitionSet.add(fieldInfo) + defToTypeMap[fieldInfo] = vartype + elif tokens[0] == "read:": + if len(tokens) == 3: + readFromSet.add(parseFieldInfo(tokens)) + elif tokens[0] == "write:": + if len(tokens) == 3: + writeToSet.add(parseFieldInfo(tokens)) + else: + print( "unknown line: " + line) + except IndexError: + print "problem with line " + line.strip() + raise + +definitionSet2 = set() +for d in definitionSet: + varname = d[1] + vartype = defToTypeMap[d] + if len(varname) == 0: + continue + if varname.startswith("autoRegister"): # auto-generated CPPUNIT stuff + continue + if vartype in ["css::uno::ContextLayer", "SolarMutexGuard", "SolarMutexReleaser", "OpenGLZone"]: + continue + if vartype in ["PreDefaultWinNoOpenGLZone", "SchedulerGuard", "SkiaZone", "OpenGLVCLContextZone"]: + continue + if vartype in ["SwXDispatchProviderInterceptor::DispatchMutexLock_Impl", "SfxObjectShellLock", "OpenCLZone"]: + continue + if vartype in ["OpenCLInitialZone", "pyuno::PyThreadDetach", "SortRefUpdateSetter", "oglcanvas::TransformationPreserver"]: + continue + if vartype in ["StackHack"]: + continue + definitionSet2.add(d) + +# Calculate untouched +untouchedSet = set() +for d in definitionSet2: + if d in readFromSet or d in writeToSet: + continue + varname = d[1] + if len(varname) == 0: + continue + untouchedSet.add(d) + +writeonlySet = set() +for d in definitionSet2: + if d in readFromSet or d in untouchedSet: + continue + writeonlySet.add(d) + +readonlySet = set() +for d in definitionSet2: + if d in writeToSet or d in untouchedSet: + continue + readonlySet.add(d) + +# 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)] + +# sort results by name and line number +tmp1list = sorted(untouchedSet, key=lambda v: natural_sort_key(v[0])) +tmp2list = sorted(writeonlySet, key=lambda v: natural_sort_key(v[0])) +tmp3list = sorted(readonlySet, key=lambda v: natural_sort_key(v[0])) + +# print out the results +with open("compilerplugins/clang/unusedvarsglobal.untouched.results", "wt") as f: + for t in tmp1list: + f.write( t[0] + "\n" ) + f.write( " " + defToTypeMap[t] + " " + t[1] + "\n" ) +with open("compilerplugins/clang/unusedvarsglobal.writeonly.results", "wt") as f: + for t in tmp2list: + f.write( t[0] + "\n" ) + f.write( " " + defToTypeMap[t] + " " + t[1] + "\n" ) +with open("compilerplugins/clang/unusedvarsglobal.readonly.results", "wt") as f: + for t in tmp3list: + f.write( t[0] + "\n" ) + f.write( " " + defToTypeMap[t] + " " + t[1] + "\n" ) + + diff --git a/compilerplugins/clang/unusedvarsglobal.untouched.results b/compilerplugins/clang/unusedvarsglobal.untouched.results new file mode 100644 index 000000000000..f8fcbf0f32a7 --- /dev/null +++ b/compilerplugins/clang/unusedvarsglobal.untouched.results @@ -0,0 +1,280 @@ +basic/source/classes/sbxmod.cxx:1714 + (anonymous namespace)::ErrorHdlResetter aErrHdl +canvas/workben/canvasdemo.cxx:672 + (anonymous namespace)::DemoApp aApp +chart2/source/view/main/DrawModelWrapper.cxx:82 + E3dObjFactory aObjFactory +connectivity/source/drivers/evoab2/EApi.h:52 + gconstpointer (*)(EContact *, EContactField) e_contact_get_const +connectivity/source/drivers/postgresql/pq_baseresultset.hxx:52 + sal_Int32 BASERESULTSET_CURSOR_NAME +connectivity/source/drivers/postgresql/pq_preparedstatement.hxx:55 + sal_Int32 PREPARED_STATEMENT_CURSOR_NAME +connectivity/source/drivers/postgresql/pq_statement.hxx:53 + sal_Int32 STATEMENT_CURSOR_NAME +cppu/source/uno/check.cxx:315 + (anonymous namespace)::BinaryCompatible_Impl aTest +dbaccess/source/filter/xml/xmlfilter.cxx:230 + dbaxml::(anonymous namespace)::FocusWindowWaitGuard aWindowFocusGuard +dbaccess/source/ui/inc/TypeInfo.hxx:31 + sal_uInt16 TYPE_UNKNOWN +desktop/source/app/sofficemain.cxx:71 + desktop::Desktop aDesktop +desktop/source/splash/unxsplash.hxx:33 + desktop::UnxSplashScreen * m_pINSTANCE +desktop/source/splash/unxsplash.hxx:35 + osl::Mutex m_aMutex +desktop/unx/source/splashx.c:301 + uint32_t tmp +desktop/unx/source/splashx.c:315 + uint32_t tmp +desktop/unx/source/splashx.c:334 + uint32_t tmp +desktop/unx/source/splashx.c:343 + uint32_t tmp +drawinglayer/source/tools/emfppen.hxx:31 + sal_uInt32 EmfPlusLineJoinTypeMiter +drawinglayer/source/tools/emfppen.hxx:36 + sal_Int32 EmfPlusLineStyleSolid +extensions/source/scanner/sane.cxx:712 + int __d0 +extensions/source/scanner/sane.cxx:712 + int __d1 +filter/source/pdf/pdffilter.cxx:222 + (anonymous namespace)::FocusWindowWaitCursor aCur +framework/source/services/ContextChangeEventMultiplexer.cxx:381 + framework::(anonymous namespace)::Hook g_hook +framework/source/uiconfiguration/windowstateconfiguration.cxx:63 + sal_Int16 PROPERTY_LOCKED +hwpfilter/source/nodes.h:92 + int count +i18nlangtag/source/languagetag/languagetag.cxx:848 + Runner aRunner +include/editeng/flditem.hxx:43 + sal_Int32 UNKNOWN_FIELD +include/oox/helper/helper.hxx:87 + sal_Int16 API_LINE_SOLID +include/oox/helper/helper.hxx:92 + sal_Int16 API_LINE_NONE +include/oox/helper/helper.hxx:98 + sal_Int16 API_ESCAPE_NONE +include/oox/ole/axcontrol.hxx:123 + sal_Int32 AX_PICALIGN_TOPLEFT +include/vcl/EnumContext.hxx:139 + sal_Int32 OptimalMatch +lotuswordpro/source/filter/LotusWordProImportFilter.cxx:74 + uno::Reference<XDocumentHandler> xHandler +lotuswordpro/source/filter/lwpsilverbullet.hxx:71 + sal_uInt16 NUMCHAR_none +oox/source/dump/oledumper.cxx:163 + sal_uInt16 OLEPROP_TYPE_SIMPLE +oox/source/ole/olehelper.cxx:67 + sal_uInt32 OLE_COLORTYPE_CLIENT +pyuno/source/loader/pyuno_loader.cxx:240 + pyuno_loader::(anonymous namespace)::PythonInit s_Init +pyuno/source/module/pyuno.cxx:1505 + pyuno::Runtime runtime +pyuno/source/module/pyuno_gc.cxx:45 + pyuno::(anonymous namespace)::StaticDestructorGuard guard +pyuno/source/module/pyuno_struct.cxx:251 + pyuno::Runtime runtime +sal/osl/all/utility.cxx:45 + osl::(anonymous namespace)::OGlobalTimer aGlobalTimer +sal/qa/osl/file/osl_File.cxx:5199 + (anonymous namespace)::GlobalObject theGlobalObject +sal/qa/osl/pipe/osl_Pipe.cxx:875 + osl_StreamPipe::(anonymous namespace)::Pipe_DataSink_Thread myDataSinkThread +sal/qa/OStringBuffer/rtl_String_Const.h:118 + sal_Int32 kTestStr25Len +sal/rtl/cmdargs.cxx:46 + (anonymous namespace)::ArgHolder argHolder +salhelper/qa/test_api.cxx:171 + salhelper::ORealDynamicLoader * p +sc/qa/unit/tiledrendering/tiledrendering.cxx:612 + (anonymous namespace)::ViewCallback aView2 +sc/qa/unit/tiledrendering/tiledrendering.cxx:651 + (anonymous namespace)::ViewCallback aView2 +sc/qa/unit/tiledrendering/tiledrendering.cxx:746 + (anonymous namespace)::ViewCallback aView1 +sc/qa/unit/tiledrendering/tiledrendering.cxx:758 + (anonymous namespace)::ViewCallback aView2 +sc/qa/unit/tiledrendering/tiledrendering.cxx:780 + (anonymous namespace)::ViewCallback aView1 +sc/qa/unit/tiledrendering/tiledrendering.cxx:1152 + (anonymous namespace)::ViewCallback aView1 +sc/qa/unit/tiledrendering/tiledrendering.cxx:1158 + (anonymous namespace)::ViewCallback aView2 +sc/qa/unit/tiledrendering/tiledrendering.cxx:1216 + (anonymous namespace)::ViewCallback aView1 +sc/qa/unit/tiledrendering/tiledrendering.cxx:1222 + (anonymous namespace)::ViewCallback aView2 +sc/qa/unit/ucalc_sort.cxx:1027 + SortRefNoUpdateSetter aUpdateSet +sc/qa/unit/ucalc_sort.cxx:1336 + SortRefNoUpdateSetter aUpdateSet +sc/qa/unit/ucalc_sort.cxx:1474 + SortRefNoUpdateSetter aUpdateSet +sc/qa/unit/ucalc_sort.cxx:1674 + SortRefNoUpdateSetter aUpdateSet +sc/source/filter/inc/biffhelper.hxx:401 + sal_uInt16 BIFF2_ID_DIMENSION +sc/source/filter/inc/biffhelper.hxx:539 + sal_uInt16 BIFF_ID_OBJEND +sc/source/filter/inc/biffhelper.hxx:575 + sal_uInt8 BIFF_DATATYPE_EMPTY +sc/source/filter/inc/biffhelper.hxx:581 + sal_uInt8 BIFF_BOOLERR_BOOL +sc/source/filter/inc/defnamesbuffer.hxx:37 + sal_Unicode BIFF_DEFNAME_CONSOLIDATEAREA +sc/source/filter/inc/formulabase.hxx:61 + sal_uInt8 BIFF_TOKID_NONE +sc/source/filter/inc/formulabase.hxx:96 + sal_uInt8 BIFF_TOKID_ARRAY +sc/source/filter/inc/formulabase.hxx:121 + sal_uInt8 BIFF_TOK_ARRAY_DOUBLE +sc/source/filter/inc/formulabase.hxx:139 + sal_uInt8 BIFF_TOK_ATTR_SPACE_SP +sc/source/filter/inc/xlchart.hxx:198 + sal_uInt16 EXC_CHFRBLOCK_FRAME_STANDARD +sc/source/filter/inc/xlchart.hxx:227 + sal_uInt16 EXC_CHSERIES_DATE +sc/source/filter/inc/xlchart.hxx:335 + sal_uInt8 EXC_CHLEGEND_CLOSE +sc/source/filter/inc/xlchart.hxx:386 + sal_uInt16 EXC_CHCHARTLINE_DROP +sc/source/filter/inc/xlchart.hxx:453 + sal_uInt16 EXC_CHDEFTEXT_TEXTLABEL +sc/source/filter/inc/xlchart.hxx:641 + sal_uInt16 EXC_CHFRAMEPOS_POINTS +sc/source/filter/inc/xlchart.hxx:682 + sal_uInt8 EXC_CHSERERR_END_BLANK +sc/source/filter/inc/xlconst.hxx:127 + sal_Int32 EXC_RK_DBL +sc/source/filter/inc/xlcontent.hxx:40 + sal_uInt16 EXC_FILEPASS_BIFF5 +sc/source/filter/inc/xlescher.hxx:99 + sal_uInt8 EXC_OBJ_ARROW_NONE +sc/source/filter/inc/xlescher.hxx:105 + sal_uInt8 EXC_OBJ_ARROW_NARROW +sc/source/filter/inc/xlescher.hxx:197 + sal_uInt16 EXC_OBJ_DROPDOWN_LISTBOX +sc/source/filter/inc/xlname.hxx:48 + sal_Unicode EXC_BUILTIN_CONSOLIDATEAREA +sc/source/filter/oox/externallinkbuffer.cxx:52 + sal_uInt16 BIFF12_EXTERNALBOOK_BOOK +sc/source/filter/oox/stylesbuffer.cxx:135 + sal_uInt8 BIFF12_COLOR_AUTO +sc/source/filter/oox/stylesbuffer.cxx:163 + sal_uInt16 BIFF12_DXF_FILL_PATTERN +sc/source/filter/oox/stylesbuffer.cxx:203 + sal_uInt8 BIFF_FONTUNDERL_NONE +sc/source/ui/vba/excelvbahelper.cxx:160 + ooo::vba::excel::(anonymous namespace)::PasteCellsWarningReseter resetWarningBox +sc/source/ui/vba/excelvbahelper.cxx:211 + ooo::vba::excel::(anonymous namespace)::PasteCellsWarningReseter resetWarningBox +sd/qa/unit/tiledrendering/tiledrendering.cxx:1462 + (anonymous namespace)::ViewCallback aView1 +sd/qa/unit/tiledrendering/tiledrendering.cxx:1915 + (anonymous namespace)::ViewCallback aView1 +sd/qa/unit/tiledrendering/tiledrendering.cxx:1923 + (anonymous namespace)::ViewCallback aView2 +sd/source/ui/animations/CustomAnimationDialog.hxx:29 + sal_Int32 nHandleSound +sd/source/ui/inc/MasterPageObserver.hxx:79 + osl::Mutex maMutex +sd/source/ui/sidebar/PanelFactory.cxx:47 + Reference<lang::XEventListener> mxControllerDisposeListener +sfx2/source/appl/shutdownicon.cxx:663 + sal_Int32 PROPHANDLE_TERMINATEVETOSTATE +sfx2/source/doc/objstor.cxx:573 + FontLockGuard aFontLockGuard +sfx2/source/view/lokhelper.cxx:127 + (anonymous namespace)::DisableCallbacks dc +soltools/cpp/cpp.h:239 + int Lflag +soltools/mkdepend/parse.c:40 + symhash * maininclist +soltools/mkdepend/pr.c:34 + int width +starmath/source/mathmlimport.cxx:2717 + uno::Reference<beans::XPropertySet> xInfoSet +svl/qa/unit/items/test_IndexedStyleSheets.cxx:74 + svl::IndexedStyleSheets iss +svtools/source/control/inettbc.cxx:87 + osl::Mutex * pDirMutex +svx/source/gallery2/galctrl.cxx:244 + GalleryProgress aProgress +svx/source/gengal/gengal.cxx:323 + (anonymous namespace)::GalApp aGalApp +sw/inc/calc.hxx:88 + char [] sCalc_Tdif +sw/inc/poolfmt.hxx:64 + sal_uInt16 POOL_FMT +sw/inc/swtypes.hxx:101 + SwPathFinder * pPathFinder +sw/inc/viewsh.hxx:167 + vcl::DeleteOnDeinit<VclPtr<vcl::Window> > mpCareWindow +sw/qa/extras/ooxmlexport/ooxmlexport8.cxx:405 + BorderTest borderTest +sw/qa/extras/rtfexport/rtfexport2.cxx:548 + BorderTest borderTest +sw/qa/extras/ww8export/ww8export.cxx:266 + BorderTest borderTest +sw/source/core/layout/paintfrm.cxx:3922 + (anonymous namespace)::BorderLinesGuard blg +sw/source/core/text/frmform.cxx:1801 + (anonymous namespace)::FormatLevel aLevel +sw/source/core/view/viewsh.cxx:706 + SwSaveSetLRUOfst aSaveLRU +sw/source/core/view/viewsh.cxx:983 + SwSaveSetLRUOfst aSaveLRU +sw/source/filter/basflt/shellio.cxx:733 + SwPauseThreadStarting aPauseThreadStarting +sw/source/filter/html/swhtml.cxx:5596 + (anonymous namespace)::FontCacheGuard aFontCacheGuard +sw/source/filter/ww8/rtfsdrexport.cxx:489 + char *[] pShapeTypes +sw/source/filter/ww8/ww8par.cxx:6264 + (anonymous namespace)::FontCacheGuard aFontCacheGuard +sw/source/filter/xml/xmlexp.cxx:98 + SwPauseThreadStarting aPauseThreadStarting +sw/source/uibase/inc/initui.hxx:33 + SwThesaurus * pThes +sw/source/uibase/lingu/hhcwrp.cxx:123 + (anonymous namespace)::SwKeepConversionDirectionStateContext aContext +sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx:326 + svx::sidebar::TreeNode aChild +vcl/backendtest/VisualBackendTest.cxx:745 + (anonymous namespace)::VisualBackendTestApp aApplication +vcl/inc/driverblocklist.hxx:96 + uint64_t allDriverVersions +vcl/qa/cppunit/BitmapScaleTest.cxx:288 + BitmapSymmetryCheck aBitmapSymmetryCheck +vcl/source/outdev/font.cxx:156 + (anonymous namespace)::UpdateFontsGuard aUpdateFontsGuard +vcl/source/uipreviewer/previewer.cxx:113 + (anonymous namespace)::UIPreviewApp aApp +vcl/unx/generic/app/saldata.cxx:314 + int __d0 +vcl/unx/generic/app/saldata.cxx:314 + int __d1 +vcl/unx/generic/app/saldata.cxx:315 + int __d1 +vcl/unx/generic/app/saldata.cxx:315 + int __d0 +vcl/workben/icontest.cxx:216 + (anonymous namespace)::IconTestApp aApp +vcl/workben/mtfdemo.cxx:162 + (anonymous namespace)::DemoMtfApp aApp +vcl/workben/vcldemo.cxx:2445 + (anonymous namespace)::DemoApp aApp +writerfilter/source/dmapper/DomainMapperTableHandler.cxx:825 + std::optional<PropertyMap::Property> oRowCellBorder +writerfilter/source/dmapper/TagLogger.hxx:35 + tools::SvRef<TagLogger> instance +xmloff/inc/PageMasterStyleMap.hxx:193 + XMLPropertyMapEntry [] aXMLPageMasterHeaderImportStyleMap +xmloff/inc/PageMasterStyleMap.hxx:194 + XMLPropertyMapEntry [] aXMLPageMasterFooterImportStyleMap +xmloff/inc/XMLChartPropertySetMapper.hxx:28 + XMLPropertyMapEntry [] aXMLChartPropMap |