1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
14 #include <unordered_map>
15 #include <unordered_set>
19 Look for fields which are const, which can be made static const
23 class StaticConstField
: public loplugin::FilteringPlugin
<StaticConstField
>
26 explicit StaticConstField(loplugin::InstantiationData
const& data
)
27 : loplugin::FilteringPlugin
<StaticConstField
>(data
)
33 bool TraverseConstructorInitializer(CXXCtorInitializer
* init
);
34 bool TraverseCXXConstructorDecl(CXXConstructorDecl
* decl
);
39 std::vector
<CXXCtorInitializer
const*> inits
;
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();
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")
55 if (fn
== SRCDIR
"/oox/source/core/contexthandler2.cxx")
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
;
80 bool StaticConstField::TraverseConstructorInitializer(CXXCtorInitializer
* init
)
82 if (!init
->getSourceLocation().isValid() || ignoreLocation(init
->getSourceLocation()))
84 if (!init
->getMember())
88 if (!m_currentConstructor
|| m_currentConstructor
->isCopyOrMoveConstructor())
90 if (!m_currentConstructor
->getParent()->isCompleteDefinition())
92 if (m_excluded
.find(init
->getMember()) != m_excluded
.end())
94 auto type
= init
->getMember()->getType();
95 auto tc
= loplugin::TypeCheck(type
);
101 auto const initexpr
= compat::IgnoreImplicit(init
->getInit());
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();
115 else if (type
->isFloatingType())
118 if (initexpr
->EvaluateAsFloat(x1
, compiler
.getASTContext()))
121 llvm::raw_string_ostream
os(s
);
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())
136 if (compat::EvaluateAsInt(initexpr
, x1
, compiler
.getASTContext()))
138 value
= x1
.toString(10);
145 m_potentials
.erase(init
->getMember());
146 m_excluded
.insert(init
->getMember());
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());
159 findIt
->second
.inits
.push_back(init
);
163 Data
& data
= m_potentials
[init
->getMember()];
164 data
.inits
.push_back(init
);
171 loplugin::Plugin::Registration
<StaticConstField
> X("staticconstfield", true);
174 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */