summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/overrideparam.cxx
blob: 820127e1343df79fc70ab5585200629539ddc12d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
78
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* -*- 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 "compat.hxx"
#include "check.hxx"

/*
  Find overridden methods that :
  (a) declare default params in different palces to their super-method(s)

  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:
    public RecursiveASTVisitor<OverrideParam>, public loplugin::Plugin
{
public:
    explicit OverrideParam(loplugin::InstantiationData const & data):
        Plugin(data) {}

    virtual void run() override;

    bool VisitCXXMethodDecl(const CXXMethodDecl *);

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

void OverrideParam::run()
{
    /*
    StringRef fn( compiler.getSourceManager().getFileEntryForID(
                      compiler.getSourceManager().getMainFileID())->getName() );
    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;
        for (const ParmVarDecl *superParmVarDecl : compat::parameters(*superMethodDecl)) {
            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()) {
        return true;
    }
    return
        checkIdenticalDefaultArguments(
            parmVarDecl->getDefaultArg(), superParmVarDecl->getDefaultArg())
        != IdenticalDefaultArgumentsResult::No;
        // 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)
}

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

}

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