bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / redundantinline.cxx
blob3bda99e397cb553673b8d2c2645da0ece393846e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
11 #include <cassert>
13 #include "plugin.hxx"
15 namespace {
17 class RedundantInline:
18 public loplugin::FilteringRewritePlugin<RedundantInline>
20 public:
21 explicit RedundantInline(loplugin::InstantiationData const & data):
22 FilteringRewritePlugin(data) {}
24 void run() override {
25 if (preRun())
26 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
29 bool VisitFunctionDecl(FunctionDecl const * decl) {
30 if (ignoreLocation(decl)) {
31 return true;
33 if (!decl->isInlineSpecified()) {
34 return true;
36 handleImplicitInline(decl) || handleNonExternalLinkage(decl);
37 return true;
40 private:
41 bool removeInline(FunctionDecl const * decl, SourceLocation * inlineLoc) {
42 assert(inlineLoc != nullptr);
43 assert(inlineLoc->isInvalid());
44 unsigned n = {}; // avoid -Werror=maybe-uninitialized
45 auto end = Lexer::getLocForEndOfToken(
46 compiler.getSourceManager().getExpansionLoc(compat::getEndLoc(decl)), 0,
47 compiler.getSourceManager(), compiler.getLangOpts());
48 assert(end.isValid());
49 for (auto loc = compiler.getSourceManager().getExpansionLoc(
50 compat::getBeginLoc(decl));
51 loc != end; loc = loc.getLocWithOffset(std::max<unsigned>(n, 1)))
53 n = Lexer::MeasureTokenLength(
54 loc, compiler.getSourceManager(), compiler.getLangOpts());
55 StringRef s(compiler.getSourceManager().getCharacterData(loc), n);
56 //TODO: see compilerplugins/clang/override.cxx:
57 if (s.startswith("\\\n")) {
58 s = s.drop_front(2);
60 if (s == "inline") {
61 if (!compiler.getSourceManager().isMacroArgExpansion(loc)) {
62 *inlineLoc = loc;
64 break;
65 } else if (s == "#") {
66 // Hard to pick the right 'inline' in code like
68 // #if 1
69 // static
70 // #else
71 // inline
72 // #endif
73 // inline void f() {}
75 // so just give up once a preprocessing directive is seen:
76 break;
79 if (rewriter != nullptr && inlineLoc->isValid()) {
80 for (auto loc = inlineLoc->getLocWithOffset(
81 std::max<unsigned>(n, 1));;)
83 assert(loc != end);
84 unsigned n2 = Lexer::MeasureTokenLength(
85 loc, compiler.getSourceManager(), compiler.getLangOpts());
86 StringRef s(
87 compiler.getSourceManager().getCharacterData(loc), n2);
88 //TODO: see compilerplugins/clang/override.cxx:
89 if (s.startswith("\\\n")) {
90 s = s.drop_front(2);
92 if (!s.empty()) {
93 break;
95 n2 = std::max<unsigned>(n2, 1);
96 n += n2;
97 loc = loc.getLocWithOffset(n2);
99 if (removeText(*inlineLoc, n, RewriteOptions(RemoveLineIfEmpty))) {
100 return true;
103 return false;
106 SourceLocation unwindTo(SourceLocation const & loc, StringRef name) {
107 if (!loc.isMacroID()) {
108 return SourceLocation();
110 auto l = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
111 return
112 (Lexer::getImmediateMacroName(
113 loc, compiler.getSourceManager(), compiler.getLangOpts())
114 == name)
115 ? l : unwindTo(l, name);
118 bool isInMacroExpansion(FunctionDecl const * decl, StringRef name) {
119 auto loc = unwindTo(compat::getBeginLoc(decl), name);
120 return loc.isValid() && loc == unwindTo(compat::getEndLoc(decl), name);
123 bool handleImplicitInline(FunctionDecl const * decl) {
124 if (!(decl->doesThisDeclarationHaveABody() || decl->isExplicitlyDefaulted())
125 || !(decl->getLexicalDeclContext()->isRecord() || decl->isConstexpr()))
127 return false;
129 if (isInMacroExpansion(decl, "Q_OBJECT")) {
130 return true;
132 SourceLocation inlineLoc;
133 if (!removeInline(decl, &inlineLoc)) {
134 report(
135 DiagnosticsEngine::Warning,
136 "function definition redundantly declared 'inline'",
137 inlineLoc.isValid() ? inlineLoc : compat::getBeginLoc(decl))
138 << decl->getSourceRange();
140 return true;
143 bool handleNonExternalLinkage(FunctionDecl const * decl) {
144 if (decl->getLinkageInternal() >= ModuleLinkage) {
145 return false;
147 if (!compiler.getSourceManager().isInMainFile(decl->getLocation())) {
148 // There *may* be benefit to "static inline" in include files (esp. in C code, where an
149 // inline function with external linkage still requires an external definition), so
150 // just ignore those for now:
151 return true;
153 if (isInMacroExpansion(decl, "G_DEFINE_TYPE")
154 || isInMacroExpansion(decl, "G_DEFINE_TYPE_WITH_CODE")
155 || isInMacroExpansion(decl, "G_DEFINE_TYPE_WITH_PRIVATE"))
157 return true;
159 SourceLocation inlineLoc;
160 if (!removeInline(decl, &inlineLoc)) {
161 report(
162 DiagnosticsEngine::Warning,
163 "function has no external linkage but is explicitly declared 'inline'",
164 inlineLoc.isValid() ? inlineLoc : compat::getBeginLoc(decl))
165 << decl->getSourceRange();
167 return true;
171 loplugin::Plugin::Registration<RedundantInline> redundantinline("redundantinline");
173 } // namespace
175 #endif // LO_CLANG_SHARED_PLUGINS
177 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */