update credits
[LibreOffice.git] / compilerplugins / clang / redundantinline.cxx
blobc9491445b64f43a56b3842c1d16b45e00e160b6b
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 "compat.hxx"
14 #include "plugin.hxx"
16 namespace {
18 class RedundantInline:
19 public loplugin::FilteringRewritePlugin<RedundantInline>
21 public:
22 explicit RedundantInline(loplugin::InstantiationData const & data):
23 FilteringRewritePlugin(data) {}
25 void run() override {
26 if (preRun())
27 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
30 bool VisitFunctionDecl(FunctionDecl const * decl) {
31 if (ignoreLocation(decl)) {
32 return true;
34 if (!decl->isInlineSpecified()) {
35 return true;
37 handleImplicitInline(decl) || handleNonExternalLinkage(decl);
38 return true;
41 private:
42 bool removeInline(FunctionDecl const * decl, SourceLocation * inlineLoc) {
43 assert(inlineLoc != nullptr);
44 assert(inlineLoc->isInvalid());
45 unsigned n = {}; // avoid -Werror=maybe-uninitialized
46 auto end = Lexer::getLocForEndOfToken(
47 compiler.getSourceManager().getExpansionLoc(decl->getEndLoc()), 0,
48 compiler.getSourceManager(), compiler.getLangOpts());
49 assert(end.isValid());
50 for (auto loc = compiler.getSourceManager().getExpansionLoc(
51 decl->getBeginLoc());
52 loc != end; loc = loc.getLocWithOffset(std::max<unsigned>(n, 1)))
54 n = Lexer::MeasureTokenLength(
55 loc, compiler.getSourceManager(), compiler.getLangOpts());
56 StringRef s(compiler.getSourceManager().getCharacterData(loc), n);
57 //TODO: see compilerplugins/clang/override.cxx:
58 if (compat::starts_with(s, "\\\n")) {
59 s = s.drop_front(2);
61 if (s == "inline") {
62 if (!compiler.getSourceManager().isMacroArgExpansion(loc)) {
63 *inlineLoc = loc;
65 break;
66 } else if (s == "#") {
67 // Hard to pick the right 'inline' in code like
69 // #if 1
70 // static
71 // #else
72 // inline
73 // #endif
74 // inline void f() {}
76 // so just give up once a preprocessing directive is seen:
77 break;
80 if (rewriter != nullptr && inlineLoc->isValid()) {
81 for (auto loc = inlineLoc->getLocWithOffset(
82 std::max<unsigned>(n, 1));;)
84 assert(loc != end);
85 unsigned n2 = Lexer::MeasureTokenLength(
86 loc, compiler.getSourceManager(), compiler.getLangOpts());
87 StringRef s(
88 compiler.getSourceManager().getCharacterData(loc), n2);
89 //TODO: see compilerplugins/clang/override.cxx:
90 if (compat::starts_with(s, "\\\n")) {
91 s = s.drop_front(2);
93 if (!s.empty()) {
94 break;
96 n2 = std::max<unsigned>(n2, 1);
97 n += n2;
98 loc = loc.getLocWithOffset(n2);
100 if (removeText(*inlineLoc, n, RewriteOptions(RemoveLineIfEmpty))) {
101 return true;
104 return false;
107 SourceLocation unwindTo(SourceLocation const & loc, StringRef name) {
108 if (!loc.isMacroID()) {
109 return SourceLocation();
111 auto l = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
112 return
113 (Lexer::getImmediateMacroName(
114 loc, compiler.getSourceManager(), compiler.getLangOpts())
115 == name)
116 ? l : unwindTo(l, name);
119 bool isInMacroExpansion(FunctionDecl const * decl, StringRef name) {
120 auto loc = unwindTo(decl->getBeginLoc(), name);
121 return loc.isValid() && loc == unwindTo(decl->getEndLoc(), name);
124 bool handleImplicitInline(FunctionDecl const * decl) {
125 if (!(decl->doesThisDeclarationHaveABody() || decl->isExplicitlyDefaulted())
126 || !(decl->getLexicalDeclContext()->isRecord() || decl->isConstexpr()))
128 return false;
130 if (isInMacroExpansion(decl, "Q_OBJECT")) {
131 return true;
133 SourceLocation inlineLoc;
134 if (!removeInline(decl, &inlineLoc)) {
135 report(
136 DiagnosticsEngine::Warning,
137 "function definition redundantly declared 'inline'",
138 inlineLoc.isValid() ? inlineLoc : decl->getBeginLoc())
139 << decl->getSourceRange();
141 return true;
144 bool handleNonExternalLinkage(FunctionDecl const * decl) {
145 if (decl->getLinkageInternal() >= compat::Linkage::Module) {
146 return false;
148 if (!compiler.getSourceManager().isInMainFile(decl->getLocation())) {
149 // There *may* be benefit to "static inline" in include files (esp. in C code, where an
150 // inline function with external linkage still requires an external definition), so
151 // just ignore those for now:
152 return true;
154 if (isInMacroExpansion(decl, "G_DEFINE_TYPE")
155 || isInMacroExpansion(decl, "G_DEFINE_TYPE_WITH_CODE")
156 || isInMacroExpansion(decl, "G_DEFINE_TYPE_WITH_PRIVATE"))
158 return true;
160 SourceLocation inlineLoc;
161 if (!removeInline(decl, &inlineLoc)) {
162 report(
163 DiagnosticsEngine::Warning,
164 "function has no external linkage but is explicitly declared 'inline'",
165 inlineLoc.isValid() ? inlineLoc : decl->getBeginLoc())
166 << decl->getSourceRange();
168 return true;
172 loplugin::Plugin::Registration<RedundantInline> redundantinline("redundantinline");
174 } // namespace
176 #endif // LO_CLANG_SHARED_PLUGINS
178 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */