diff options
Diffstat (limited to 'compilerplugins/clang/store/stdexception.cxx')
-rw-r--r-- | compilerplugins/clang/store/stdexception.cxx | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/compilerplugins/clang/store/stdexception.cxx b/compilerplugins/clang/store/stdexception.cxx new file mode 100644 index 0000000000..47a7d57911 --- /dev/null +++ b/compilerplugins/clang/store/stdexception.cxx @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 <cassert> +#include <limits> +#include <string> + +#include "plugin.hxx" + +namespace { + +bool isStdException(QualType type) { + //TODO: + std::string name { type.getAsString() }; + return name == "std::exception" || name == "::std::exception"; +} + +class StdException: + public loplugin::FilteringRewritePlugin<StdException> +{ +public: + explicit StdException(InstantiationData const & data): FilteringRewritePlugin(data) + {} + + virtual void run() override + { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } + + bool VisitCXXMethodDecl(CXXMethodDecl const * decl); +}; + +bool StdException::VisitCXXMethodDecl(CXXMethodDecl const * decl) { + if (ignoreLocation(decl) + || decl->begin_overridden_methods() == decl->end_overridden_methods()) + { + return true; + } + CXXMethodDecl const * over = nullptr; + for (auto i = decl->begin_overridden_methods(); + i != decl->end_overridden_methods(); ++i) + { + FunctionProtoType const * t + = (*i)->getType()->getAs<FunctionProtoType>(); + switch (t->getExceptionSpecType()) { + case EST_None: + continue; + case EST_DynamicNone: + case EST_BasicNoexcept: + return true; + case EST_Dynamic: + { + unsigned n = t->getNumExceptions(); + for (unsigned j = 0; j != n; ++j) { + if (isStdException(t->getExceptionType(j))) { + over = *i; + goto found; + } + } + return true; + } + case EST_ComputedNoexcept: + switch (t->getNoexceptSpec(compiler.getASTContext())) { + case FunctionProtoType::NR_NoNoexcept: + case FunctionProtoType::NR_BadNoexcept: + assert(false); + // fall through + case FunctionProtoType::NR_Dependent: + break; + case FunctionProtoType::NR_Throw: + continue; + case FunctionProtoType::NR_Nothrow: + return true; + } + case EST_MSAny: + case EST_Unevaluated: + case EST_Uninstantiated: + continue; //TODO??? + } + } + return true; +found: + FunctionProtoType const * t = decl->getType()->getAs<FunctionProtoType>(); + if (!t->hasDynamicExceptionSpec()) { + report( + DiagnosticsEngine::Warning, + "override does not have dynamic exception specification", + decl->getLocStart()) + << decl->getSourceRange(); + report( + DiagnosticsEngine::Note, + ("overridden declaration with dynamic exception specification" + " including std::exception is here"), + over->getLocStart()); + return true; + } + unsigned n = t->getNumExceptions(); + for (unsigned i = 0; i != n; ++i) { + if (isStdException(t->getExceptionType(i))) { + return true; + } + } + SourceRange r { decl->getSourceRange() }; + SourceLocation l { + compiler.getSourceManager().getExpansionLoc(r.getBegin()) }; + SourceLocation end { + compiler.getSourceManager().getExpansionLoc(r.getEnd()) }; + assert( + l == end + || compiler.getSourceManager().isBeforeInTranslationUnit(l, end)); + bool seenThrow = false; + unsigned parens = 0; + SourceLocation openParen; + SourceLocation loc; + for (;;) { + unsigned n = Lexer::MeasureTokenLength( + l, compiler.getSourceManager(), compiler.getLangOpts()); + std::string s { compiler.getSourceManager().getCharacterData(l), n }; + if (s == "{" || s == ";") { + break; + } + if (!seenThrow) { + if (s == "throw") { + seenThrow = true; + } + } else if (s == "(") { + assert(parens < std::numeric_limits<unsigned>::max()); + ++parens; + if (parens == 1) { + openParen = l; + } + loc = l; + } else if (s == ")") { + assert(parens != 0); + --parens; + if (parens == 0) { + assert(loc.isValid()); + // Only rewrite declarations in include files if a definition is + // also seen, to avoid compilation of a definition (in a main + // file only processed later) to fail with a "mismatch" error + // before the rewriter had a chance to act upon the definition + // (but use the heuristic of assuming pure virtual functions do + // not have definitions): + if (rewriter != nullptr + && (compiler.getSourceManager().isInMainFile( + compiler.getSourceManager().getSpellingLoc(loc)) + || decl->isDefined() || decl->isPure()) + && insertTextAfterToken( + loc, + (loc == openParen + ? "std::exception" : ", std::exception"))) + { + return true; + } + break; + } + loc = l; + } else if (!s.empty() && s.compare(0, 2, "/*") != 0 + && s.compare(0, 2, "//") != 0) + { + loc = l; + } + if (l == end) { + break; + } + l = l.getLocWithOffset(std::max<unsigned>(n, 1)); + } + report( + DiagnosticsEngine::Warning, + "override dropped std::exception from dynamic exception specification", + openParen.isValid() ? openParen : decl->getLocStart()) + << decl->getSourceRange(); + report( + DiagnosticsEngine::Note, "overridden declaration is here", + over->getLocStart()); + return true; +} + +loplugin::Plugin::Registration<StdException> X("stdexception", true); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |