bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / dynexcspec.cxx
blobaf733ec4f10fa55fab9342a5c2cf9c8295b7d464
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 "plugin.hxx"
18 // Remove dynamic exception specifications. See the mail thread starting at
19 // <https://lists.freedesktop.org/archives/libreoffice/2017-January/076665.html>
20 // "Dynamic Exception Specifications" for details.
22 namespace {
24 bool isOverriding(FunctionDecl const * decl) {
25 if (decl->hasAttr<OverrideAttr>()) {
26 return true;
28 auto m = dyn_cast<CXXMethodDecl>(decl);
29 return m != nullptr
30 && m->begin_overridden_methods() != m->end_overridden_methods();
33 bool isDtorOrDealloc(FunctionDecl const * decl) {
34 if (isa<CXXDestructorDecl>(decl)) {
35 return true;
37 switch (decl->getOverloadedOperator()) {
38 case OO_Delete:
39 case OO_Array_Delete:
40 return true;
41 default:
42 return false;
46 class DynExcSpec:
47 public loplugin::FilteringRewritePlugin<DynExcSpec>
49 public:
50 explicit DynExcSpec(loplugin::InstantiationData const & data):
51 FilteringRewritePlugin(data) {}
53 bool preRun() override {
54 return compiler.getLangOpts().CPlusPlus;
57 void run() override {
58 if (preRun()) {
59 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
63 bool VisitFunctionDecl(FunctionDecl const * decl) {
64 if (ignoreLocation(decl)) {
65 return true;
67 auto proto = decl->getType()->getAs<FunctionProtoType>();
68 if (proto == nullptr || proto->getExceptionSpecType() != EST_Dynamic) {
69 return true;
71 if (decl->isCanonicalDecl() && !isOverriding(decl)
72 && !anyRedeclHasThrowsDocumentation(decl))
74 report(
75 DiagnosticsEngine::Warning,
76 ("function declaration has dynamic exception specification but"
77 " no corresponding documentation comment"),
78 decl->getLocation())
79 << decl->getSourceRange();
81 if (rewriter != nullptr) {
82 if (!(decl->isDefined() || decl->isPure())) {
83 return true;
85 if (auto m = dyn_cast<CXXMethodDecl>(decl)) {
86 for (auto i = m->begin_overridden_methods();
87 i != m->end_overridden_methods(); ++i)
89 auto proto2 = (*i)->getType()->getAs<FunctionProtoType>();
90 assert(proto2 != nullptr);
91 if (proto2->getExceptionSpecType() == EST_Dynamic) {
92 return true;
97 bool dtorOrDealloc = isDtorOrDealloc(decl);
98 auto const source = decl->getExceptionSpecSourceRange();
99 if (rewriter != nullptr && source.isValid()) {
100 if (dtorOrDealloc) {
101 if (replaceText(source, "noexcept(false)")) {
102 return true;
104 } else {
105 auto beg = source.getBegin();
106 if (beg.isFileID()) {
107 for (;;) {
108 auto prev = Lexer::GetBeginningOfToken(
109 beg.getLocWithOffset(-1),
110 compiler.getSourceManager(),
111 compiler.getLangOpts());
112 auto n = Lexer::MeasureTokenLength(
113 prev, compiler.getSourceManager(),
114 compiler.getLangOpts());
115 auto s = StringRef(
116 compiler.getSourceManager().getCharacterData(prev),
118 while (s.startswith("\\\n")) {
119 s = s.drop_front(2);
120 while (!s.empty()
121 && (s.front() == ' ' || s.front() == '\t'
122 || s.front() == '\n' || s.front() == '\v'
123 || s.front() == '\f'))
125 s = s.drop_front(1);
128 if (!s.empty() && s != "\\") {
129 if (s.startswith("//")) {
130 beg = source.getBegin();
132 break;
134 beg = prev;
137 if (removeText(SourceRange(beg, source.getEnd()))) {
138 return true;
142 report(
143 DiagnosticsEngine::Warning,
144 (dtorOrDealloc
145 ? "replace dynamic exception specification with 'noexcept(false)'"
146 : "remove dynamic exception specification"),
147 source.isValid() ? source.getBegin() : decl->getLocation())
148 << (source.isValid() ? source : decl->getSourceRange());
149 return true;
152 private:
153 bool hasThrowsDocumentation(FunctionDecl const * decl) {
154 if (auto cmt = compiler.getASTContext().getCommentForDecl(
155 decl, &compiler.getPreprocessor()))
157 for (auto i = cmt->child_begin(); i != cmt->child_end(); ++i) {
158 if (auto bcc = dyn_cast<comments::BlockCommandComment>(*i)) {
159 if (compiler.getASTContext().getCommentCommandTraits()
160 .getCommandInfo(bcc->getCommandID())->IsThrowsCommand)
162 return true;
167 return false;
170 bool anyRedeclHasThrowsDocumentation(FunctionDecl const * decl) {
171 return std::any_of(
172 decl->redecls_begin(), decl->redecls_end(),
173 [this](FunctionDecl * d) { return hasThrowsDocumentation(d); });
174 // std::bind(
175 // &DynExcSpec::hasThrowsDocumentation, this,
176 // std::placeholders::_1));
180 loplugin::Plugin::Registration<DynExcSpec> dynexcspec("dynexcspec");
182 } // namespace
184 #endif // LO_CLANG_SHARED_PLUGINS
186 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */