overrideparam.cxx 6.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* -*- 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 <set>

#include "plugin.hxx"
#include "check.hxx"

/*
Andrea Gelmini's avatar
Andrea Gelmini committed
17
  Find overridden methods that :
Andrea Gelmini's avatar
Andrea Gelmini committed
18
  (a) declare default params in different places to their super-method(s)
19 20 21 22 23 24 25 26 27

  Still TODO
  (b) Find places where the values of the default parameters are different
  (c) Find places where the names of the parameters differ
*/

namespace {

class OverrideParam:
28
    public loplugin::FilteringPlugin<OverrideParam>
29 30
{
public:
31
    explicit OverrideParam(loplugin::InstantiationData const & data):
32
        FilteringPlugin(data) {}
33 34 35 36 37 38 39 40 41 42 43 44

    virtual void run() override;

    bool VisitCXXMethodDecl(const CXXMethodDecl *);

private:
    bool hasSameDefaultParams(const ParmVarDecl * parmVarDecl, const ParmVarDecl * superParmVarDecl);
};

void OverrideParam::run()
{
    /*
45
    StringRef fn(handler.getMainFileName());
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    if (fn == SRCDIR "/include/svx/checklbx.hxx")
         return;
*/
    TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}

bool OverrideParam::VisitCXXMethodDecl(const CXXMethodDecl * methodDecl) {
    if (ignoreLocation(methodDecl)) {
        return true;
    }
    if (methodDecl->isThisDeclarationADefinition() || methodDecl->size_overridden_methods() == 0) {
        return true;
    }
    loplugin::DeclCheck dc(methodDecl);
    // there is an InsertEntry override here which causes trouble if I modify it
    if (dc.Function("InsertEntry").Class("SvxCheckListBox").GlobalNamespace()) {
        return true;
    }
    // This class is overriding ShowCursor(bool) AND declaring a ShowCursor() method.
    // Adding a default param causes 'ambiguous override'.
    if (dc.Function("ShowCursor").Class("ScTabViewShell").GlobalNamespace()) {
        return true;
    }

    for(auto superMethodIt = methodDecl->begin_overridden_methods();
        superMethodIt != methodDecl->end_overridden_methods(); ++superMethodIt)
    {
        const CXXMethodDecl * superMethodDecl = *superMethodIt;
        if (ignoreLocation(superMethodDecl)) {
            continue;
        }
        int i = 0;
78
        for (const ParmVarDecl *superParmVarDecl : superMethodDecl->parameters()) {
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
            const ParmVarDecl *parmVarDecl = methodDecl->getParamDecl(i);
            if (parmVarDecl->hasDefaultArg() && !superParmVarDecl->hasDefaultArg()) {
                report(
                    DiagnosticsEngine::Warning,
                    "overridden method declaration has default arg, but super-method does not",
                    parmVarDecl->getSourceRange().getBegin())
                    << parmVarDecl->getSourceRange();
                report(
                    DiagnosticsEngine::Note,
                    "original param here",
                    superParmVarDecl->getSourceRange().getBegin())
                    << superParmVarDecl->getSourceRange();
            }
            else if (!parmVarDecl->hasDefaultArg() && superParmVarDecl->hasDefaultArg()) {
                report(
                    DiagnosticsEngine::Warning,
                    "overridden method declaration has no default arg, but super-method does",
                    parmVarDecl->getSourceRange().getBegin())
                    << parmVarDecl->getSourceRange();
                report(
                    DiagnosticsEngine::Note,
                    "original param here",
                    superParmVarDecl->getSourceRange().getBegin())
                    << superParmVarDecl->getSourceRange();
            }
            else if (parmVarDecl->hasDefaultArg() && superParmVarDecl->hasDefaultArg()
                && !hasSameDefaultParams(parmVarDecl, superParmVarDecl)) {
                report(
                    DiagnosticsEngine::Warning,
                    "overridden method declaration has different default param to super-method",
                    parmVarDecl->getSourceRange().getBegin())
                    << parmVarDecl->getSourceRange();
                report(
                    DiagnosticsEngine::Note,
                    "original param here",
                    superParmVarDecl->getSourceRange().getBegin())
                    << superParmVarDecl->getSourceRange();
            }
            /* do nothing for now, will enable this in a later commit
            if (methodDecl->isThisDeclarationADefinition() && parmVarDecl->getName().empty()) {
                // ignore this - means the param is unused
            }
            else if (superParmVarDecl->getName().empty()) {
                // ignore, nothing reasonable I can do
            }
            else if (superParmVarDecl->getName() != parmVarDecl->getName()) {
                report(
                    DiagnosticsEngine::Warning,
                    "overridden method declaration uses different name for parameter",
                    parmVarDecl->getSourceRange().getBegin())
                    << parmVarDecl->getSourceRange();
                report(
                    DiagnosticsEngine::Note,
                    "original param here",
                    superParmVarDecl->getSourceRange().getBegin())
                    << superParmVarDecl->getSourceRange();
            }*/
            ++i;
        }
    }
    return true;
}

bool OverrideParam::hasSameDefaultParams(const ParmVarDecl * parmVarDecl, const ParmVarDecl * superParmVarDecl)
{
    // don't know what this means, but it prevents a clang crash
    if (parmVarDecl->hasUninstantiatedDefaultArg() || superParmVarDecl->hasUninstantiatedDefaultArg()) {
146
        return true;
147
    }
148 149 150 151
    return
        checkIdenticalDefaultArguments(
            parmVarDecl->getDefaultArg(), superParmVarDecl->getDefaultArg())
        != IdenticalDefaultArgumentsResult::No;
152 153 154 155 156 157 158 159 160 161 162 163 164
        // for one, Clang 3.8 doesn't have EvaluateAsFloat; for another, since
        // <http://llvm.org/viewvc/llvm-project?view=revision&revision=291318>
        // "PR23135: Don't instantiate constexpr functions referenced in
        // unevaluated operands where possible", default args are not
        // necessarily evaluated, so the above calls to EvaluateAsInt etc. may
        // fail (as they do e.g. for SfxViewShell::SetPrinter and derived
        // classes' 'SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL' arg,
        // include/sfx2/viewsh.hxx; what appears would help is to call
        // 'compiler.getSema().MarkDeclarationsReferencedInExpr()' on
        // defaultArgExpr and superDefaultArgExpr before any of the calls to
        // EvaluateAsInt etc., cf. the implementation of
        // Sema::CheckCXXDefaultArgExpr in Clang's lib/Sema/SemaExpr.cpp, but
        // that would probably have unwanted side-effects)
165 166 167 168 169 170 171
}

loplugin::Plugin::Registration< OverrideParam > X("overrideparam");

}

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