summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/staticconstfield.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/staticconstfield.cxx')
-rw-r--r--compilerplugins/clang/staticconstfield.cxx174
1 files changed, 174 insertions, 0 deletions
diff --git a/compilerplugins/clang/staticconstfield.cxx b/compilerplugins/clang/staticconstfield.cxx
new file mode 100644
index 000000000..74aad462f
--- /dev/null
+++ b/compilerplugins/clang/staticconstfield.cxx
@@ -0,0 +1,174 @@
+/* -*- 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 "compat.hxx"
+#include <iostream>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+/**
+ Look for fields which are const, which can be made static const
+*/
+namespace
+{
+class StaticConstField : public loplugin::FilteringPlugin<StaticConstField>
+{
+public:
+ explicit StaticConstField(loplugin::InstantiationData const& data)
+ : loplugin::FilteringPlugin<StaticConstField>(data)
+ {
+ }
+
+ void run() override;
+
+ bool TraverseConstructorInitializer(CXXCtorInitializer* init);
+ bool TraverseCXXConstructorDecl(CXXConstructorDecl* decl);
+
+private:
+ struct Data
+ {
+ std::vector<CXXCtorInitializer const*> inits;
+ std::string value;
+ };
+ std::unordered_map<FieldDecl const*, Data> m_potentials;
+ std::unordered_set<FieldDecl const*> m_excluded;
+ CXXConstructorDecl* m_currentConstructor = nullptr;
+};
+
+void StaticConstField::run()
+{
+ std::string fn = handler.getMainFileName().str();
+ loplugin::normalizeDotDotInFilePath(fn);
+
+ // unusual case where a user constructor sets a field to one value, and a copy constructor sets it to a different value
+ if (fn == SRCDIR "/sw/source/core/attr/hints.cxx")
+ return;
+ if (fn == SRCDIR "/oox/source/core/contexthandler2.cxx")
+ return;
+
+ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+
+ for (auto const& pair : m_potentials)
+ {
+ report(DiagnosticsEngine::Error, "const field can be static", pair.first->getLocation())
+ << pair.first->getSourceRange();
+ for (CXXCtorInitializer const* init : pair.second.inits)
+ if (pair.first->getLocation() != init->getSourceLocation())
+ report(DiagnosticsEngine::Note, "init here", init->getSourceLocation())
+ << init->getSourceRange();
+ }
+}
+
+bool StaticConstField::TraverseCXXConstructorDecl(CXXConstructorDecl* decl)
+{
+ auto prev = m_currentConstructor;
+ m_currentConstructor = decl;
+ bool ret = FilteringPlugin::TraverseCXXConstructorDecl(decl);
+ m_currentConstructor = prev;
+ return ret;
+}
+
+bool StaticConstField::TraverseConstructorInitializer(CXXCtorInitializer* init)
+{
+ if (!init->getSourceLocation().isValid() || ignoreLocation(init->getSourceLocation()))
+ return true;
+ if (!init->getMember())
+ return true;
+ if (!init->getInit())
+ return true;
+ if (!m_currentConstructor || m_currentConstructor->isCopyOrMoveConstructor())
+ return true;
+ if (!m_currentConstructor->getParent()->isCompleteDefinition())
+ return true;
+ if (m_excluded.find(init->getMember()) != m_excluded.end())
+ return true;
+ auto type = init->getMember()->getType();
+ auto tc = loplugin::TypeCheck(type);
+ if (!tc.Const())
+ return true;
+
+ bool found = false;
+ std::string value;
+ auto const initexpr = init->getInit()->IgnoreImplicit();
+ if (tc.Const().Class("OUString").Namespace("rtl").GlobalNamespace()
+ || tc.Const().Class("OString").Namespace("rtl").GlobalNamespace())
+ {
+ if (auto constructExpr = dyn_cast<CXXConstructExpr>(initexpr))
+ {
+ if (constructExpr->getNumArgs() >= 1
+ && isa<clang::StringLiteral>(constructExpr->getArg(0)))
+ {
+ value = dyn_cast<clang::StringLiteral>(constructExpr->getArg(0))->getString().str();
+ found = true;
+ }
+ }
+ }
+ else if (type->isFloatingType())
+ {
+ APFloat x1(0.0f);
+ if (initexpr->EvaluateAsFloat(x1, compiler.getASTContext()))
+ {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ x1.print(os);
+ value = os.str();
+ found = true;
+ }
+ }
+ // ignore this, it seems to trigger an infinite recursion
+ else if (isa<UnaryExprOrTypeTraitExpr>(initexpr))
+ ;
+ // ignore this, calling EvaluateAsInt on it will crash clang
+ else if (initexpr->isValueDependent())
+ ;
+ else
+ {
+ APSInt x1;
+ if (compat::EvaluateAsInt(initexpr, x1, compiler.getASTContext()))
+ {
+ value = compat::toString(x1, 10);
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ m_potentials.erase(init->getMember());
+ m_excluded.insert(init->getMember());
+ return true;
+ }
+
+ auto findIt = m_potentials.find(init->getMember());
+ if (findIt != m_potentials.end())
+ {
+ if (findIt->second.value != value)
+ {
+ m_potentials.erase(findIt);
+ m_excluded.insert(init->getMember());
+ }
+ else
+ findIt->second.inits.push_back(init);
+ }
+ else
+ {
+ Data& data = m_potentials[init->getMember()];
+ data.inits.push_back(init);
+ data.value = value;
+ }
+
+ return true;
+}
+
+loplugin::Plugin::Registration<StaticConstField> X("staticconstfield", true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */