summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/unnecessarygetstr.cxx
blob: c80877a78554d8991bfdbc058c1ad37295a95451 (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
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * 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/.
 */

#ifndef LO_CLANG_SHARED_PLUGINS

#include <cassert>
#include <stack>

#include "check.hxx"
#include "plugin.hxx"
#include "config_clang.h"

// Find matches of
//
//   foo(s.getStr())
//
// (for the rtl string classes) that can be written as just
//
//   foo(s)
//
// and warn about them, which prevents constructing unnecessary temporaries.

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

    bool VisitCallExpr(const CallExpr* callExpr)
    {
        if (ignoreLocation(callExpr))
            return true;
        const FunctionDecl* func = callExpr->getDirectCallee();
        if (!func)
            return true;
        if (loplugin::DeclCheck(func)
                .Function("createFromAscii")
                .Class("OUString")
                .Namespace("rtl")
                .GlobalNamespace())
        {
            checkForGetStr(callExpr->getArg(0), "OUString::createFromAscii",
                           /*isOStringConstructor*/ false);
        }
        return true;
    }

    bool VisitCXXConstructExpr(const CXXConstructExpr* constructExpr)
    {
        if (ignoreLocation(constructExpr))
            return true;
        auto tc = loplugin::TypeCheck(constructExpr->getType());
        if (tc.ClassOrStruct("basic_string").StdNamespace())
        {
            if (constructExpr->getNumArgs() == 1 || constructExpr->getNumArgs() == 2)
                checkForGetStr(constructExpr->getArg(0), "string constructor",
                               /*isOStringConstructor*/ false);
        }
        else if (tc.ClassOrStruct("basic_string_view").StdNamespace())
        {
            if (constructExpr->getNumArgs() == 1)
                checkForGetStr(constructExpr->getArg(0), "string_view constructor",
                               /*isOStringConstructor*/ false);
        }
        else if (tc.Class("OString").Namespace("rtl").GlobalNamespace())
        {
            if (constructExpr->getNumArgs() == 1 || constructExpr->getNumArgs() == 2)
                checkForGetStr(constructExpr->getArg(0), "OString constructor",
                               /*isOStringConstructor*/ true);
        }
        else if (tc.Class("OUString").Namespace("rtl").GlobalNamespace())
        {
            if (constructExpr->getNumArgs() == 2)
                checkForGetStr(constructExpr->getArg(0), "OUString constructor",
                               /*isOStringConstructor*/ false);
        }
        return true;
    }

    bool preRun() override
    {
        if (!compiler.getLangOpts().CPlusPlus)
            return false;
        std::string fn(handler.getMainFileName());
        loplugin::normalizeDotDotInFilePath(fn);
        if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/"))
            return false;
        return true;
    }

private:
    void checkForGetStr(const Expr* arg, const char* msg, bool isOStringConstructor)
    {
        auto e = dyn_cast<CXXMemberCallExpr>(arg->IgnoreImplicit());
        if (!e)
            return;
        auto const t = e->getObjectType();
        auto const tc2 = loplugin::TypeCheck(t);
        if (tc2.Class("OString").Namespace("rtl").GlobalNamespace()
            || tc2.Class("OUString").Namespace("rtl").GlobalNamespace()
            || tc2.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()
            || tc2.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
            || tc2.ClassOrStruct("StringNumber").Namespace("rtl").GlobalNamespace())
        {
            if (loplugin::DeclCheck(e->getMethodDecl()).Function("getStr"))
            {
                StringRef fileName = getFilenameOfLocation(
                    compiler.getSourceManager().getSpellingLoc(e->getBeginLoc()));
                if (!loplugin::hasPathnamePrefix(fileName, SRCDIR "/include/rtl/"))
                    report(DiagnosticsEngine::Warning,
                           "unnecessary call to 'getStr' when passing to %0", e->getExprLoc())
                        << msg << e->getSourceRange();
            }
        }
        // we do need to use c_str() when passing to an OString
        else if (!isOStringConstructor && tc2.Class("basic_string").StdNamespace())
        {
            if (loplugin::DeclCheck(e->getMethodDecl()).Function("c_str"))
                report(DiagnosticsEngine::Warning, "unnecessary call to 'c_str' when passing to %0",
                       e->getExprLoc())
                    << msg << e->getSourceRange();
        }
    }
    void run() override
    {
        if (preRun())
        {
            TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
        }
    }
};

loplugin::Plugin::Registration<UnnecessaryGetStr> unnecessarygetstr("unnecessarygetstr");
}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */