staticmethods.cxx 9.45 KB
Newer Older
1 2 3 4 5 6 7 8 9
/* -*- 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/.
 */

10 11
#include "clang/AST/Attr.h"

12
#include "check.hxx"
13
#include "plugin.hxx"
14 15 16 17 18 19 20

/*
  Look for member functions that can be static
*/
namespace {

class StaticMethods:
21
    public loplugin::FilteringPlugin<StaticMethods>
22 23 24 25
{
private:
    bool bVisitedThis;
public:
26
    explicit StaticMethods(loplugin::InstantiationData const & data): FilteringPlugin(data), bVisitedThis(false) {}
27 28 29 30 31 32 33 34 35 36 37

    void run() override
    { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }

    bool TraverseCXXMethodDecl(const CXXMethodDecl * decl);

    bool VisitCXXThisExpr(const CXXThisExpr *) { bVisitedThis = true; return true; }
    // these two indicate that we hit something that makes our analysis unreliable
    bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *) { bVisitedThis = true; return true; }
    bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *) { bVisitedThis = true; return true; }
private:
38
    StringRef getFilename(SourceLocation loc);
39 40
};

41
bool BaseCheckNotTestFixtureSubclass(const CXXRecordDecl *BaseDefinition) {
42
    if (loplugin::TypeCheck(BaseDefinition).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()) {
43 44 45 46 47 48 49 50 51 52 53
        return false;
    }
    return true;
}

bool isDerivedFromTestFixture(const CXXRecordDecl *decl) {
    if (!decl->hasDefinition())
        return false;
    if (// not sure what hasAnyDependentBases() does,
        // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
        !decl->hasAnyDependentBases() &&
54
        !decl->forallBases(BaseCheckNotTestFixtureSubclass, true)) {
55 56 57 58 59
        return true;
    }
    return false;
}

60
StringRef StaticMethods::getFilename(SourceLocation loc)
61 62
{
    SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc);
63
    return getFileNameOfSpellingLoc(spellingLocation);
64 65
}

66
bool startsWith(const std::string& rStr, const char* pSubStr) {
Noel Grandin's avatar
Noel Grandin committed
67 68
    return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
}
69 70 71 72 73

bool StaticMethods::TraverseCXXMethodDecl(const CXXMethodDecl * pCXXMethodDecl) {
    if (ignoreLocation(pCXXMethodDecl)) {
        return true;
    }
74
    if (!pCXXMethodDecl->isInstance() || pCXXMethodDecl->isVirtual() || !pCXXMethodDecl->doesThisDeclarationHaveABody() || pCXXMethodDecl->isLateTemplateParsed()) {
75 76 77 78 79 80 81 82
        return true;
    }
    if (pCXXMethodDecl->getOverloadedOperator() != OverloadedOperatorKind::OO_None || pCXXMethodDecl->hasAttr<OverrideAttr>()) {
        return true;
    }
    if (isa<CXXConstructorDecl>(pCXXMethodDecl) || isa<CXXDestructorDecl>(pCXXMethodDecl) || isa<CXXConversionDecl>(pCXXMethodDecl)) {
        return true;
    }
83
    if (isInUnoIncludeFile(pCXXMethodDecl)) {
84 85
        return true;
    }
86
    if (pCXXMethodDecl->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
87 88 89 90 91 92 93
        return true;

    // the CppUnit stuff uses macros and methods that can't be changed
    if (isDerivedFromTestFixture(pCXXMethodDecl->getParent())) {
        return true;
    }
    // don't mess with the backwards compatibility stuff
94
    if (loplugin::isSamePathname(getFilename(compat::getBeginLoc(pCXXMethodDecl)), SRCDIR "/cppuhelper/source/compat.cxx")) {
95 96 97
        return true;
    }
    // the DDE has a dummy implementation on Linux and a real one on Windows
98
    auto aFilename = getFilename(compat::getBeginLoc(pCXXMethodDecl->getCanonicalDecl()));
99
    if (loplugin::isSamePathname(aFilename, SRCDIR "/include/svl/svdde.hxx")) {
100 101
        return true;
    }
Stephan Bergmann's avatar
Stephan Bergmann committed
102
    auto cdc = loplugin::DeclCheck(pCXXMethodDecl->getParent());
103 104
    // special case having something to do with static initialisation
    // sal/osl/all/utility.cxx
Stephan Bergmann's avatar
Stephan Bergmann committed
105
    if (cdc.Class("OGlobalTimer").Namespace("osl").GlobalNamespace()) {
106 107
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
108
    // leave the TopLeft() method alone for consistency with the other "corner" methods
Stephan Bergmann's avatar
Stephan Bergmann committed
109
    if (cdc.Class("BitmapInfoAccess").GlobalNamespace()) {
Noel Grandin's avatar
Noel Grandin committed
110 111 112
        return true;
    }
    // the unotools and svl config code stuff is doing weird stuff with a reference-counted statically allocated pImpl class
113
    if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/unotools/")) {
Noel Grandin's avatar
Noel Grandin committed
114 115
        return true;
    }
116
    if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/svl/")) {
Noel Grandin's avatar
Noel Grandin committed
117 118
        return true;
    }
119
    if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/framework/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/framework/")) {
Noel Grandin's avatar
Noel Grandin committed
120 121 122
        return true;
    }
    // there is some odd stuff happening here I don't fully understand, leave it for now
123
    if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/canvas/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/canvas/")) {
Noel Grandin's avatar
Noel Grandin committed
124 125
        return true;
    }
Andrea Gelmini's avatar
Andrea Gelmini committed
126
    // classes that have static data and some kind of weird reference-counting trick in its constructor
Stephan Bergmann's avatar
Stephan Bergmann committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    if (cdc.Class("LinguOptions").GlobalNamespace()
        || (cdc.Class("EditableExtendedColorConfig").Namespace("svtools")
            .GlobalNamespace())
        || (cdc.Class("ExtendedColorConfig").Namespace("svtools")
            .GlobalNamespace())
        || cdc.Class("SvtMiscOptions").GlobalNamespace()
        || cdc.Class("SvtAccessibilityOptions").GlobalNamespace()
        || cdc.Class("ColorConfig").Namespace("svtools").GlobalNamespace()
        || cdc.Class("SvtOptionsDrawinglayer").GlobalNamespace()
        || cdc.Class("SvtMenuOptions").GlobalNamespace()
        || cdc.Class("SvtToolPanelOptions").GlobalNamespace()
        || cdc.Class("SvtSlideSorterBarOptions").GlobalNamespace()
        || (cdc.Class("SharedResources").Namespace("connectivity")
            .GlobalNamespace())
        || (cdc.Class("OParseContextClient").Namespace("svxform")
            .GlobalNamespace())
        || cdc.Class("OLimitedFormats").Namespace("frm").GlobalNamespace())
Noel Grandin's avatar
Noel Grandin committed
144
    {
Noel Grandin's avatar
Noel Grandin committed
145 146
        return true;
    }
Stephan Bergmann's avatar
Stephan Bergmann committed
147
    auto fdc = loplugin::DeclCheck(pCXXMethodDecl);
Noel Grandin's avatar
Noel Grandin committed
148
    // only empty on Linux, not on windows
Stephan Bergmann's avatar
Stephan Bergmann committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    if ((fdc.Function("GetVisualRepresentationInNativeFormat_Impl")
         .Class("OleEmbeddedObject").GlobalNamespace())
        || (fdc.Function("GetRidOfComponent").Class("OleEmbeddedObject")
            .GlobalNamespace())
        || (fdc.Function("isProfileLocked").Class("ProfileAccess")
            .Namespace("mozab").Namespace("connectivity").GlobalNamespace())
        || cdc.Class("SbxDecimal").GlobalNamespace()
        || fdc.Function("Call").Class("SbiDllMgr").GlobalNamespace()
        || fdc.Function("FreeDll").Class("SbiDllMgr").GlobalNamespace()
        || (fdc.Function("InitializeDde").Class("SfxApplication")
            .GlobalNamespace())
        || (fdc.Function("RemoveDdeTopic").Class("SfxApplication")
            .GlobalNamespace())
        || (fdc.Function("ReleaseData").Class("ScannerManager")
            .GlobalNamespace()))
    {
Noel Grandin's avatar
Noel Grandin committed
165 166
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
167
    // debugging stuff
Stephan Bergmann's avatar
Stephan Bergmann committed
168 169 170
    if (fdc.Function("dump").Class("InternalData").Namespace("chart")
        .GlobalNamespace())
    {
Noel Grandin's avatar
Noel Grandin committed
171 172
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
173
    // used in a function-pointer-table
Stephan Bergmann's avatar
Stephan Bergmann committed
174 175 176 177 178 179 180 181 182 183
    if ((cdc.Class("SbiRuntime").GlobalNamespace()
         && startsWith(pCXXMethodDecl->getNameAsString(), "Step"))
        || (cdc.Class("OoxFormulaParserImpl").Namespace("xls").Namespace("oox")
            .GlobalNamespace())
        || cdc.Class("SwTableFormula").GlobalNamespace()
        || (cdc.Class("BiffFormulaParserImpl").Namespace("xls").Namespace("oox")
            .GlobalNamespace())
        || (fdc.Function("Read_F_Shape").Class("SwWW8ImplReader")
            .GlobalNamespace())
        || (fdc.Function("Read_Majority").Class("SwWW8ImplReader")
184 185
            .GlobalNamespace())
        || fdc.Function("Ignore").Class("SwWrtShell").GlobalNamespace())
Stephan Bergmann's avatar
Stephan Bergmann committed
186
    {
Noel Grandin's avatar
Noel Grandin committed
187 188
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
189
    // have no idea why this can't be static, but 'make check' fails with it so...
Stephan Bergmann's avatar
Stephan Bergmann committed
190 191 192
    if (fdc.Function("resolveRelationshipsOfTypeFromOfficeDoc").Class("Shape")
        .Namespace("drawingml").Namespace("oox").GlobalNamespace())
    {
Noel Grandin's avatar
Noel Grandin committed
193 194
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
195
    // template magic
Stephan Bergmann's avatar
Stephan Bergmann committed
196 197 198 199 200
    if (fdc.Function("getValue").Class("ColumnBatch").GlobalNamespace()
        || cdc.Class("TitleImpl").GlobalNamespace()
        || (fdc.Function("getDefaultPropertyName").Class("DefaultReturnHelper")
            .Namespace("vba").Namespace("ooo").GlobalNamespace()))
    {
Noel Grandin's avatar
Noel Grandin committed
201 202
        return true;
    }
Noel Grandin's avatar
Noel Grandin committed
203
    // depends on config options
Stephan Bergmann's avatar
Stephan Bergmann committed
204 205 206
    if ((fdc.Function("autoInstallFontLangSupport").Class("PrintFontManager")
         .Namespace("psp").GlobalNamespace())
        || fdc.Function("AllocateFrame").Class("GtkSalFrame").GlobalNamespace()
Stephan Bergmann's avatar
Stephan Bergmann committed
207
        || (fdc.Function("TriggerPaintEvent").Class("GtkSalFrame")
Stephan Bergmann's avatar
Stephan Bergmann committed
208 209
            .GlobalNamespace()))
    {
Noel Grandin's avatar
Noel Grandin committed
210 211
        return true;
    }
212 213 214 215 216 217 218 219 220

    bVisitedThis = false;
    TraverseStmt(pCXXMethodDecl->getBody());
    if (bVisitedThis) {
        return true;
    }

    report(
        DiagnosticsEngine::Warning,
Stephan Bergmann's avatar
Stephan Bergmann committed
221
        "this member function can be declared static",
222
        pCXXMethodDecl->getCanonicalDecl()->getLocation())
223
      << pCXXMethodDecl->getCanonicalDecl()->getSourceRange();
224 225 226 227 228 229 230
    FunctionDecl const * def;
    if (pCXXMethodDecl->isDefined(def)
        && def != pCXXMethodDecl->getCanonicalDecl())
    {
        report(DiagnosticsEngine::Note, "defined here:", def->getLocation())
            << def->getSourceRange();
    }
231 232 233
    return true;
}

234
loplugin::Plugin::Registration<StaticMethods> X("staticmethods");
235 236 237 238

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */