summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/dynexcspec.cxx
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2017-01-19 18:01:57 +0100
committerStephan Bergmann <sbergman@redhat.com>2017-01-19 18:03:26 +0100
commit28ec4d1456ae17711749e4131e9f8e96eccb1b95 (patch)
tree7064ddc3136e999de5623d4cf25c0936b01a76c3 /compilerplugins/clang/dynexcspec.cxx
parent416252b1f6d60d36f8cb0b57c71a0edf33395a44 (diff)
New loplugin:dynexcspec: Add @throws documentation
See the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2017-January/076665.html> "Dynamic Exception Specifications" for details. * The check for missing @throws documentation is not too specific, it just checks whether a function with dynamic exception specification has /any/ @throws clause, not necessarily exactly matching the exception types. (Many of the details in the existing dynamic exception specifications are probably not very useful, anyway.) * When adding @throws clauses, I bluntly copied the exception specifications except for dropping any mentions of std::exception (except in the rare cases where that was the only exception typed mentioned). * In many places it might have looked more natural to use trailing Doxygen comments of the ///< @throws ... kind, but Clang's getCommentForDecl unfortunately doesn't detect trailing comments on function decls. * Also, Clang's getCommentForDecl doesn't look into macros, so some trivial silly macros were expanded along the way to add comments where necessary. Change-Id: I1831d72df2d9c801d4b8dd7d708d9cefea039589
Diffstat (limited to 'compilerplugins/clang/dynexcspec.cxx')
-rw-r--r--compilerplugins/clang/dynexcspec.cxx151
1 files changed, 151 insertions, 0 deletions
diff --git a/compilerplugins/clang/dynexcspec.cxx b/compilerplugins/clang/dynexcspec.cxx
new file mode 100644
index 000000000000..d92bf4f560dd
--- /dev/null
+++ b/compilerplugins/clang/dynexcspec.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <algorithm>
+#include <functional>
+
+#include "clang/AST/Comment.h"
+
+#include "plugin.hxx"
+
+// Remove dynamic exception specifications. See the mail thread starting at
+// <https://lists.freedesktop.org/archives/libreoffice/2017-January/076665.html>
+// "Dynamic Exception Specifications" for details.
+
+namespace {
+
+bool isOverriding(FunctionDecl const * decl) {
+ return decl->hasAttr<OverrideAttr>();
+}
+
+class DynExcSpec:
+ public RecursiveASTVisitor<DynExcSpec>, public loplugin::RewritePlugin
+{
+public:
+ explicit DynExcSpec(InstantiationData const & data): RewritePlugin(data) {}
+
+ void run() override {
+ // See the mail thread mentioned above for why !LIBO_INTERNAL_ONLY is
+ // excluded for now:
+ if (!compiler.getPreprocessor().getIdentifierInfo("LIBO_INTERNAL_ONLY")
+ ->hasMacroDefinition())
+ {
+ return;
+ }
+ if (compiler.getLangOpts().CPlusPlus) {
+ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+ }
+ }
+
+ bool VisitFunctionDecl(FunctionDecl const * decl) {
+ if (ignoreLocation(decl)) {
+ return true;
+ }
+ auto proto = dyn_cast<FunctionProtoType>(decl->getType());
+ if (proto == nullptr || proto->getExceptionSpecType() != EST_Dynamic) {
+ return true;
+ }
+ if (decl->isCanonicalDecl() && !isOverriding(decl)
+ && !anyRedeclHasThrowsDocumentation(decl))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ ("function declaration has dynamic exception specification but"
+ " no corresponding documentation comment"),
+ decl->getLocation())
+ << decl->getSourceRange();
+ return true;
+ }
+#if 0 // will be enabled later
+ bool dtor = isa<CXXDestructorDecl>(decl);
+ auto source = decl->getExceptionSpecSourceRange();
+ if (rewriter != nullptr && source.isValid()) {
+ if (dtor) {
+ if (replaceText(source, "noexcept(false)")) {
+ return true;
+ }
+ } else {
+ auto beg = source.getBegin();
+ if (beg.isFileID()) {
+ for (;;) {
+ auto prev = Lexer::GetBeginningOfToken(
+ beg.getLocWithOffset(-1),
+ compiler.getSourceManager(),
+ compiler.getLangOpts());
+ auto n = Lexer::MeasureTokenLength(
+ prev, compiler.getSourceManager(),
+ compiler.getLangOpts());
+ auto s = StringRef(
+ compiler.getSourceManager().getCharacterData(prev),
+ n);
+ while (s.startswith("\\\n")) {
+ s = s.drop_front(2);
+ while (!s.empty()
+ && (s.front() == ' ' || s.front() == '\t'
+ || s.front() == '\n' || s.front() == '\v'
+ || s.front() == '\f'))
+ {
+ s = s.drop_front(1);
+ }
+ }
+ if (!s.empty() && s != "\\") {
+ break;
+ }
+ beg = prev;
+ }
+ }
+ if (removeText(SourceRange(beg, source.getEnd()))) {
+ return true;
+ }
+ }
+ }
+ report(
+ DiagnosticsEngine::Warning,
+ (dtor
+ ? "replace dynamic exception specification with 'noexcept(false)'"
+ : "remove dynamic exception specification"),
+ source.isValid() ? source.getBegin() : decl->getLocation())
+ << (source.isValid() ? source : decl->getSourceRange());
+#endif
+ return true;
+ }
+
+private:
+ bool hasThrowsDocumentation(FunctionDecl const * decl) {
+ if (auto cmt = compiler.getASTContext().getCommentForDecl(
+ decl, &compiler.getPreprocessor()))
+ {
+ for (auto i = cmt->child_begin(); i != cmt->child_end(); ++i) {
+ if (auto bcc = dyn_cast<comments::BlockCommandComment>(*i)) {
+ if (compiler.getASTContext().getCommentCommandTraits()
+ .getCommandInfo(bcc->getCommandID())->IsThrowsCommand)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ bool anyRedeclHasThrowsDocumentation(FunctionDecl const * decl) {
+ return std::any_of(
+ decl->redecls_begin(), decl->redecls_end(),
+ [this](FunctionDecl * d) { return hasThrowsDocumentation(d); });
+ // std::bind(
+ // &DynExcSpec::hasThrowsDocumentation, this,
+ // std::placeholders::_1));
+ }
+};
+
+loplugin::Plugin::Registration<DynExcSpec> X("dynexcspec", true);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */