cid#1555694 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / compilerplugins / clang / staticconstfield.cxx
blob74aad462fa3ca2f343ef4a632704ce962d45973d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "plugin.hxx"
11 #include "check.hxx"
12 #include "compat.hxx"
13 #include <iostream>
14 #include <unordered_map>
15 #include <unordered_set>
16 #include <vector>
18 /**
19 Look for fields which are const, which can be made static const
21 namespace
23 class StaticConstField : public loplugin::FilteringPlugin<StaticConstField>
25 public:
26 explicit StaticConstField(loplugin::InstantiationData const& data)
27 : loplugin::FilteringPlugin<StaticConstField>(data)
31 void run() override;
33 bool TraverseConstructorInitializer(CXXCtorInitializer* init);
34 bool TraverseCXXConstructorDecl(CXXConstructorDecl* decl);
36 private:
37 struct Data
39 std::vector<CXXCtorInitializer const*> inits;
40 std::string value;
42 std::unordered_map<FieldDecl const*, Data> m_potentials;
43 std::unordered_set<FieldDecl const*> m_excluded;
44 CXXConstructorDecl* m_currentConstructor = nullptr;
47 void StaticConstField::run()
49 std::string fn = handler.getMainFileName().str();
50 loplugin::normalizeDotDotInFilePath(fn);
52 // unusual case where a user constructor sets a field to one value, and a copy constructor sets it to a different value
53 if (fn == SRCDIR "/sw/source/core/attr/hints.cxx")
54 return;
55 if (fn == SRCDIR "/oox/source/core/contexthandler2.cxx")
56 return;
58 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
60 for (auto const& pair : m_potentials)
62 report(DiagnosticsEngine::Error, "const field can be static", pair.first->getLocation())
63 << pair.first->getSourceRange();
64 for (CXXCtorInitializer const* init : pair.second.inits)
65 if (pair.first->getLocation() != init->getSourceLocation())
66 report(DiagnosticsEngine::Note, "init here", init->getSourceLocation())
67 << init->getSourceRange();
71 bool StaticConstField::TraverseCXXConstructorDecl(CXXConstructorDecl* decl)
73 auto prev = m_currentConstructor;
74 m_currentConstructor = decl;
75 bool ret = FilteringPlugin::TraverseCXXConstructorDecl(decl);
76 m_currentConstructor = prev;
77 return ret;
80 bool StaticConstField::TraverseConstructorInitializer(CXXCtorInitializer* init)
82 if (!init->getSourceLocation().isValid() || ignoreLocation(init->getSourceLocation()))
83 return true;
84 if (!init->getMember())
85 return true;
86 if (!init->getInit())
87 return true;
88 if (!m_currentConstructor || m_currentConstructor->isCopyOrMoveConstructor())
89 return true;
90 if (!m_currentConstructor->getParent()->isCompleteDefinition())
91 return true;
92 if (m_excluded.find(init->getMember()) != m_excluded.end())
93 return true;
94 auto type = init->getMember()->getType();
95 auto tc = loplugin::TypeCheck(type);
96 if (!tc.Const())
97 return true;
99 bool found = false;
100 std::string value;
101 auto const initexpr = init->getInit()->IgnoreImplicit();
102 if (tc.Const().Class("OUString").Namespace("rtl").GlobalNamespace()
103 || tc.Const().Class("OString").Namespace("rtl").GlobalNamespace())
105 if (auto constructExpr = dyn_cast<CXXConstructExpr>(initexpr))
107 if (constructExpr->getNumArgs() >= 1
108 && isa<clang::StringLiteral>(constructExpr->getArg(0)))
110 value = dyn_cast<clang::StringLiteral>(constructExpr->getArg(0))->getString().str();
111 found = true;
115 else if (type->isFloatingType())
117 APFloat x1(0.0f);
118 if (initexpr->EvaluateAsFloat(x1, compiler.getASTContext()))
120 std::string s;
121 llvm::raw_string_ostream os(s);
122 x1.print(os);
123 value = os.str();
124 found = true;
127 // ignore this, it seems to trigger an infinite recursion
128 else if (isa<UnaryExprOrTypeTraitExpr>(initexpr))
130 // ignore this, calling EvaluateAsInt on it will crash clang
131 else if (initexpr->isValueDependent())
133 else
135 APSInt x1;
136 if (compat::EvaluateAsInt(initexpr, x1, compiler.getASTContext()))
138 value = compat::toString(x1, 10);
139 found = true;
143 if (!found)
145 m_potentials.erase(init->getMember());
146 m_excluded.insert(init->getMember());
147 return true;
150 auto findIt = m_potentials.find(init->getMember());
151 if (findIt != m_potentials.end())
153 if (findIt->second.value != value)
155 m_potentials.erase(findIt);
156 m_excluded.insert(init->getMember());
158 else
159 findIt->second.inits.push_back(init);
161 else
163 Data& data = m_potentials[init->getMember()];
164 data.inits.push_back(init);
165 data.value = value;
168 return true;
171 loplugin::Plugin::Registration<StaticConstField> X("staticconstfield", true);
174 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */