d1a2b80b9d
...as discussed in the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html> "Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at <https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html> "Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up compilerplugins/clang/ accordingly Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
174 lines
5.2 KiB
C++
174 lines
5.2 KiB
C++
/* -*- 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: */
|