update credits
[LibreOffice.git] / compilerplugins / clang / dynexcspec.cxx
blob483230e8cf31dc42d6e4ec24bcceb35f76ee6e11
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 <algorithm>
12 #include <functional>
14 #include "clang/AST/Comment.h"
16 #include "compat.hxx"
17 #include "plugin.hxx"
19 // Remove dynamic exception specifications. See the mail thread starting at
20 // <https://lists.freedesktop.org/archives/libreoffice/2017-January/076665.html>
21 // "Dynamic Exception Specifications" for details.
23 namespace {
25 bool isOverriding(FunctionDecl const * decl) {
26 if (decl->hasAttr<OverrideAttr>()) {
27 return true;
29 auto m = dyn_cast<CXXMethodDecl>(decl);
30 return m != nullptr
31 && m->begin_overridden_methods() != m->end_overridden_methods();
34 bool isDtorOrDealloc(FunctionDecl const * decl) {
35 if (isa<CXXDestructorDecl>(decl)) {
36 return true;
38 switch (decl->getOverloadedOperator()) {
39 case OO_Delete:
40 case OO_Array_Delete:
41 return true;
42 default:
43 return false;
47 class DynExcSpec:
48 public loplugin::FilteringRewritePlugin<DynExcSpec>
50 public:
51 explicit DynExcSpec(loplugin::InstantiationData const & data):
52 FilteringRewritePlugin(data) {}
54 bool preRun() override {
55 return compiler.getLangOpts().CPlusPlus;
58 void run() override {
59 if (preRun()) {
60 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
64 bool VisitFunctionDecl(FunctionDecl const * decl) {
65 if (ignoreLocation(decl)) {
66 return true;
68 auto proto = decl->getType()->getAs<FunctionProtoType>();
69 if (proto == nullptr || proto->getExceptionSpecType() != EST_Dynamic) {
70 return true;
72 if (decl->isCanonicalDecl() && !isOverriding(decl)
73 && !anyRedeclHasThrowsDocumentation(decl))
75 report(
76 DiagnosticsEngine::Warning,
77 ("function declaration has dynamic exception specification but"
78 " no corresponding documentation comment"),
79 decl->getLocation())
80 << decl->getSourceRange();
82 if (rewriter != nullptr) {
83 if (!(decl->isDefined() || compat::isPureVirtual(decl))) {
84 return true;
86 if (auto m = dyn_cast<CXXMethodDecl>(decl)) {
87 for (auto i = m->begin_overridden_methods();
88 i != m->end_overridden_methods(); ++i)
90 auto proto2 = (*i)->getType()->getAs<FunctionProtoType>();
91 assert(proto2 != nullptr);
92 if (proto2->getExceptionSpecType() == EST_Dynamic) {
93 return true;
98 bool dtorOrDealloc = isDtorOrDealloc(decl);
99 auto const source = decl->getExceptionSpecSourceRange();
100 if (rewriter != nullptr && source.isValid()) {
101 if (dtorOrDealloc) {
102 if (replaceText(source, "noexcept(false)")) {
103 return true;
105 } else {
106 auto beg = source.getBegin();
107 if (beg.isFileID()) {
108 for (;;) {
109 auto prev = Lexer::GetBeginningOfToken(
110 beg.getLocWithOffset(-1),
111 compiler.getSourceManager(),
112 compiler.getLangOpts());
113 auto n = Lexer::MeasureTokenLength(
114 prev, compiler.getSourceManager(),
115 compiler.getLangOpts());
116 auto s = StringRef(
117 compiler.getSourceManager().getCharacterData(prev),
119 while (compat::starts_with(s, "\\\n")) {
120 s = s.drop_front(2);
121 while (!s.empty()
122 && (s.front() == ' ' || s.front() == '\t'
123 || s.front() == '\n' || s.front() == '\v'
124 || s.front() == '\f'))
126 s = s.drop_front(1);
129 if (!s.empty() && s != "\\") {
130 if (compat::starts_with(s, "//")) {
131 beg = source.getBegin();
133 break;
135 beg = prev;
138 if (removeText(SourceRange(beg, source.getEnd()))) {
139 return true;
143 report(
144 DiagnosticsEngine::Warning,
145 (dtorOrDealloc
146 ? "replace dynamic exception specification with 'noexcept(false)'"
147 : "remove dynamic exception specification"),
148 source.isValid() ? source.getBegin() : decl->getLocation())
149 << (source.isValid() ? source : decl->getSourceRange());
150 return true;
153 private:
154 bool hasThrowsDocumentation(FunctionDecl const * decl) {
155 if (auto cmt = compiler.getASTContext().getCommentForDecl(
156 decl, &compiler.getPreprocessor()))
158 for (auto i = cmt->child_begin(); i != cmt->child_end(); ++i) {
159 if (auto bcc = dyn_cast<comments::BlockCommandComment>(*i)) {
160 if (compiler.getASTContext().getCommentCommandTraits()
161 .getCommandInfo(bcc->getCommandID())->IsThrowsCommand)
163 return true;
168 return false;
171 bool anyRedeclHasThrowsDocumentation(FunctionDecl const * decl) {
172 return std::any_of(
173 decl->redecls_begin(), decl->redecls_end(),
174 [this](FunctionDecl * d) { return hasThrowsDocumentation(d); });
175 // std::bind(
176 // &DynExcSpec::hasThrowsDocumentation, this,
177 // std::placeholders::_1));
181 loplugin::Plugin::Registration<DynExcSpec> dynexcspec("dynexcspec");
183 } // namespace
185 #endif // LO_CLANG_SHARED_PLUGINS
187 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */