Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / dynexcspec.cxx
blob651c997e60cf78d20567361a3b8af707151eb4cc
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 */
10 #include <algorithm>
11 #include <functional>
13 #include "clang/AST/Comment.h"
15 #include "plugin.hxx"
17 // Remove dynamic exception specifications. See the mail thread starting at
18 // <https://lists.freedesktop.org/archives/libreoffice/2017-January/076665.html>
19 // "Dynamic Exception Specifications" for details.
21 namespace {
23 bool isOverriding(FunctionDecl const * decl) {
24 if (decl->hasAttr<OverrideAttr>()) {
25 return true;
27 auto m = dyn_cast<CXXMethodDecl>(decl);
28 return m != nullptr
29 && m->begin_overridden_methods() != m->end_overridden_methods();
32 bool isDtorOrDealloc(FunctionDecl const * decl) {
33 if (isa<CXXDestructorDecl>(decl)) {
34 return true;
36 switch (decl->getOverloadedOperator()) {
37 case OO_Delete:
38 case OO_Array_Delete:
39 return true;
40 default:
41 return false;
45 class DynExcSpec:
46 public RecursiveASTVisitor<DynExcSpec>, public loplugin::RewritePlugin
48 public:
49 explicit DynExcSpec(loplugin::InstantiationData const & data):
50 RewritePlugin(data) {}
52 void run() override {
53 if (compiler.getLangOpts().CPlusPlus) {
54 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
58 bool VisitFunctionDecl(FunctionDecl const * decl) {
59 if (ignoreLocation(decl)) {
60 return true;
62 auto proto = decl->getType()->getAs<FunctionProtoType>();
63 if (proto == nullptr || proto->getExceptionSpecType() != EST_Dynamic) {
64 return true;
66 if (decl->isCanonicalDecl() && !isOverriding(decl)
67 && !anyRedeclHasThrowsDocumentation(decl))
69 report(
70 DiagnosticsEngine::Warning,
71 ("function declaration has dynamic exception specification but"
72 " no corresponding documentation comment"),
73 decl->getLocation())
74 << decl->getSourceRange();
76 if (rewriter != nullptr) {
77 if (!(decl->isDefined() || decl->isPure())) {
78 return true;
80 if (auto m = dyn_cast<CXXMethodDecl>(decl)) {
81 for (auto i = m->begin_overridden_methods();
82 i != m->end_overridden_methods(); ++i)
84 auto proto2 = (*i)->getType()->getAs<FunctionProtoType>();
85 assert(proto2 != nullptr);
86 if (proto2->getExceptionSpecType() == EST_Dynamic) {
87 return true;
92 bool dtorOrDealloc = isDtorOrDealloc(decl);
93 SourceRange source;
94 #if CLANG_VERSION >= 40000
95 source = decl->getExceptionSpecSourceRange();
96 #endif
97 if (rewriter != nullptr && source.isValid()) {
98 if (dtorOrDealloc) {
99 if (replaceText(source, "noexcept(false)")) {
100 return true;
102 } else {
103 auto beg = source.getBegin();
104 if (beg.isFileID()) {
105 for (;;) {
106 auto prev = Lexer::GetBeginningOfToken(
107 beg.getLocWithOffset(-1),
108 compiler.getSourceManager(),
109 compiler.getLangOpts());
110 auto n = Lexer::MeasureTokenLength(
111 prev, compiler.getSourceManager(),
112 compiler.getLangOpts());
113 auto s = StringRef(
114 compiler.getSourceManager().getCharacterData(prev),
116 while (s.startswith("\\\n")) {
117 s = s.drop_front(2);
118 while (!s.empty()
119 && (s.front() == ' ' || s.front() == '\t'
120 || s.front() == '\n' || s.front() == '\v'
121 || s.front() == '\f'))
123 s = s.drop_front(1);
126 if (!s.empty() && s != "\\") {
127 if (s.startswith("//")) {
128 beg = source.getBegin();
130 break;
132 beg = prev;
135 if (removeText(SourceRange(beg, source.getEnd()))) {
136 return true;
140 report(
141 DiagnosticsEngine::Warning,
142 (dtorOrDealloc
143 ? "replace dynamic exception specification with 'noexcept(false)'"
144 : "remove dynamic exception specification"),
145 source.isValid() ? source.getBegin() : decl->getLocation())
146 << (source.isValid() ? source : decl->getSourceRange());
147 return true;
150 private:
151 bool hasThrowsDocumentation(FunctionDecl const * decl) {
152 if (auto cmt = compiler.getASTContext().getCommentForDecl(
153 decl, &compiler.getPreprocessor()))
155 for (auto i = cmt->child_begin(); i != cmt->child_end(); ++i) {
156 if (auto bcc = dyn_cast<comments::BlockCommandComment>(*i)) {
157 if (compiler.getASTContext().getCommentCommandTraits()
158 .getCommandInfo(bcc->getCommandID())->IsThrowsCommand)
160 return true;
165 return false;
168 bool anyRedeclHasThrowsDocumentation(FunctionDecl const * decl) {
169 return std::any_of(
170 decl->redecls_begin(), decl->redecls_end(),
171 [this](FunctionDecl * d) { return hasThrowsDocumentation(d); });
172 // std::bind(
173 // &DynExcSpec::hasThrowsDocumentation, this,
174 // std::placeholders::_1));
178 loplugin::Plugin::Registration<DynExcSpec> X("dynexcspec", true);
182 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */