summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/store/optmove.cxx
blob: 51b5a4b84fa1fbf1309f5063d6a6dd0cb0f442a1 (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
/* -*- 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 "plugin.hxx"
#include "check.hxx"

#include <string>
#include <set>

/**
 * This plugin is unfinished, abandoned because it did not find anything interesting.
 *
 * Look for variables that are
 * (a) copied from
 * (b) never used after the copy
 * (c) have move operators
 *
 * The intention being to find places where we can move data (e.g. in containers) instead of copying.
*/

namespace
{
class OptMove : public loplugin::FilteringPlugin<OptMove>
{
public:
    explicit OptMove(loplugin::InstantiationData const& data)
        : FilteringPlugin(data)
    {
    }

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

        for (auto const& pair : m_Candidates)
        {
            //auto varDecl = pair.first;
            auto candidate = pair.second;
            if (!candidate.canUseExpr)
                continue;
            report(DiagnosticsEngine::Warning, "can std::move value instead of copy",
                   candidate.canUseExpr->getSourceRange().getBegin())
                << candidate.canUseExpr->getSourceRange();
            //varDecl->dump();
        }
    }

    bool VisitVarDecl(const VarDecl*);
    bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr*);
    bool VisitDeclRefExpr(const DeclRefExpr*);
    bool VisitFunctionDecl(const FunctionDecl* f)
    {
        if (f->getIdentifier() && f->getName() == "foo")
            f->dump();
        return true;
    }

private:
    struct Candidate
    {
        const DeclRefExpr* operatorArg1 = nullptr;
        const Expr* canUseExpr = nullptr;
    };
    std::map<const VarDecl*, Candidate> m_Candidates;
};

bool OptMove::VisitVarDecl(const VarDecl* varDecl)
{
    if (ignoreLocation(varDecl))
        return true;
    if (varDecl->hasGlobalStorage())
        return true;
    if (varDecl->getLinkageAndVisibility().getLinkage() == ExternalLinkage)
        return true;
    if (!varDecl->getType()->isRecordType())
        return true;

    auto cxxRecord = dyn_cast<CXXRecordDecl>(varDecl->getType()->getAsRecordDecl());
    if (!cxxRecord || !cxxRecord->hasDefinition() || !cxxRecord->hasMoveAssignment())
        return true;
    // ignore our simpler types for now, I'm after bigger game
    auto typeName = cxxRecord->getName();
    if (typeName.contains("Reference") || typeName.contains("Color") || typeName.contains("VclPtr")
        || typeName.contains("OString") || typeName.contains("OUString")
        || typeName.contains("Rectangle") || typeName.contains("Size")
        || typeName.contains("Selection") || typeName.contains("Point")
        || typeName.contains("strong_int"))
        return true;
    m_Candidates.emplace(varDecl, Candidate());

    if (!varDecl->hasInit())
        return true;
    auto cons = dyn_cast<CXXConstructExpr>(varDecl->getInit());
    if (!cons || !cons->getConstructor()->isCopyConstructor())
        return true;
    auto arg1 = dyn_cast<DeclRefExpr>(cons->getArg(0)->IgnoreImplicit());
    if (!arg1)
        return true;
    auto varDecl1 = dyn_cast<VarDecl>(arg1->getDecl());
    if (!varDecl1)
        return true;
    auto it = m_Candidates.find(varDecl1);
    if (it == m_Candidates.end())
        return true;
    it->second.operatorArg1 = arg1;
    it->second.canUseExpr = cons;
    return true;
}

bool OptMove::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const* cxxOperatorCallExpr)
{
    if (ignoreLocation(cxxOperatorCallExpr))
        return true;
    auto op = cxxOperatorCallExpr->getOperator();
    if (op != OO_Equal)
        return true;
    auto arg0 = dyn_cast<DeclRefExpr>(cxxOperatorCallExpr->getArg(0)->IgnoreImplicit());
    auto arg1 = dyn_cast<DeclRefExpr>(cxxOperatorCallExpr->getArg(1)->IgnoreImplicit());
    if (!arg0 || !arg1)
        return true;
    auto varDecl0 = dyn_cast<VarDecl>(arg0->getDecl());
    auto varDecl1 = dyn_cast<VarDecl>(arg1->getDecl());
    if (!varDecl0 || !varDecl1)
        return true;
    auto cxxMethodDecl = dyn_cast_or_null<CXXMethodDecl>(cxxOperatorCallExpr->getDirectCallee());
    if (!cxxMethodDecl || !cxxMethodDecl->isCopyAssignmentOperator())
        return true;
    auto it = m_Candidates.find(varDecl1);
    if (it == m_Candidates.end())
        return true;
    it->second.operatorArg1 = arg1;
    it->second.canUseExpr = cxxOperatorCallExpr;
    return true;
}

bool OptMove::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
{
    if (ignoreLocation(declRefExpr))
        return true;
    auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
    if (!varDecl)
        return true;
    auto it = m_Candidates.find(varDecl);
    if (it == m_Candidates.end())
        return true;
    if (it->second.operatorArg1 == declRefExpr)
        return true;
    m_Candidates.erase(it);
    return true;
}

loplugin::Plugin::Registration<OptMove> noexceptmove("optmove");
}

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