diff options
Diffstat (limited to 'compilerplugins/clang/overridevirtual.cxx')
-rw-r--r-- | compilerplugins/clang/overridevirtual.cxx | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/compilerplugins/clang/overridevirtual.cxx b/compilerplugins/clang/overridevirtual.cxx new file mode 100644 index 0000000000..8dd29ab0e4 --- /dev/null +++ b/compilerplugins/clang/overridevirtual.cxx @@ -0,0 +1,174 @@ +/* -*- 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/. + */ +#ifndef LO_CLANG_SHARED_PLUGINS + +#include <algorithm> +#include <cassert> +#include <limits> +#include <set> +#include <string> + +#include "clang/AST/Attr.h" + +#include "plugin.hxx" + +namespace { + +class OverrideVirtual: + public loplugin::FilteringRewritePlugin<OverrideVirtual> +{ +public: + explicit OverrideVirtual(loplugin::InstantiationData const & data): + FilteringRewritePlugin(data) {} + + virtual bool preRun() override; + virtual void run() override; + + bool VisitCXXMethodDecl(CXXMethodDecl const * decl); + +private: + std::set<SourceLocation> insertions_; +}; + +bool OverrideVirtual::preRun() { + return compiler.getLangOpts().CPlusPlus + && compiler.getPreprocessor().getIdentifierInfo( + "LIBO_INTERNAL_ONLY")->hasMacroDefinition(); +} + +void OverrideVirtual::run() { + if (preRun()) + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); +} +bool OverrideVirtual::VisitCXXMethodDecl(CXXMethodDecl const * decl) { + // As a heuristic, ignore declarations where the name is spelled out in an + // ignored location; that e.g. handles uses of the Q_OBJECT macro from + // external QtCore/qobjectdefs.h: + if (ignoreLocation(decl) || !decl->isFirstDecl() + || decl->begin_overridden_methods() == decl->end_overridden_methods() + || decl->hasAttr<OverrideAttr>() + || ignoreLocation( + compiler.getSourceManager().getSpellingLoc( + decl->getNameInfo().getLoc()))) + { + return true; + } + std::string over( + isInUnoIncludeFile(decl->getSourceRange().getBegin()) + ? "SAL_OVERRIDE" : "override"); + if (rewriter != nullptr) { + // In void MACRO(...); getSourceRange().getEnd() would (erroneously?) + // point at "MACRO" rather than ")", so make the loop always terminate + // at the first ";" or "{" instead of getSourceRange().getEnd(): + unsigned parens = 0; + bool seenSpace = false; + //TODO: Whether to add a space after an inserted "SAL_OVERRIDE" should + // depend on the following token at the spelling location where + // "SAL_OVERRIDE" is inserted, not on the following token in the fully- + // macro-expanded view: + bool addSpace = bool(); + SourceLocation loc; + for (SourceLocation l(decl->getSourceRange().getBegin());;) { + SourceLocation sl(compiler.getSourceManager().getSpellingLoc(l)); + unsigned n = Lexer::MeasureTokenLength( + sl, compiler.getSourceManager(), compiler.getLangOpts()); + StringRef s(compiler.getSourceManager().getCharacterData(sl), n); + //TODO: Looks like a Clang bug that in some cases like + // (filter/source/svg/svgexport.cxx) + // + // | #define TEXT_FIELD_GET_CLASS_NAME_METHOD( class_name ) \ | + // | virtual OUString getClassName() const \ | + // | { \ | + // | static const char className[] = #class_name; \ | + // | return OUString( className ); \ | + // | } | + // | | + // | TEXT_FIELD_GET_CLASS_NAME_METHOD( TextField ) | + // + // where "\<NL>" is followed directly by a real token without + // intervening whitespace, tokens "\<NL>virtual" and "\<NL>{" are + // reported: + if (s.startswith("\\\n")) { + s = s.drop_front(2); + } + if (parens == 0) { + if (s == "=" || s == "{") { + if (!seenSpace) { + addSpace = true; + } + break; + } + if (s == ";") { + break; + } + } + if (s == "(") { + assert(parens < std::numeric_limits<unsigned>::max()); + ++parens; + } else if (s == ")") { + assert(parens != 0); + --parens; + } + if (s.empty()) { + if (!seenSpace) { + addSpace = false; + } + seenSpace = true; + } else if (s.startswith("/*") || s.startswith("//") || s == "\\") { + if (!seenSpace) { + addSpace = true; + } + seenSpace = true; + } else { + seenSpace = false; + addSpace = false; + loc = sl; + } + if (l.isMacroID() + && compiler.getSourceManager().isAtEndOfImmediateMacroExpansion( + l, &l)) + { + n = Lexer::MeasureTokenLength( + compiler.getSourceManager().getSpellingLoc(l), + compiler.getSourceManager(), compiler.getLangOpts()); + } + l = l.getLocWithOffset(std::max<unsigned>(n, 1)); + } + assert(loc.isValid()); + if (!insertions_.insert(loc).second + || insertTextAfterToken( + loc, + std::string(" ") + over + std::string(addSpace ? " " : ""))) + { + return true; + } + } + report( + DiagnosticsEngine::Warning, + ("overriding virtual function declaration not marked '%0'"), + decl->getLocation()) + << over << decl->getSourceRange(); + for (auto i = decl->begin_overridden_methods(); + i != decl->end_overridden_methods(); ++i) + { + report( + DiagnosticsEngine::Note, "overridden declaration is here", + (*i)->getLocation()) + << (*i)->getSourceRange(); + } + return true; +} + +loplugin::Plugin::Registration<OverrideVirtual> overridevirtual("overridevirtual"); + +} // namespace + +#endif // LO_CLANG_SHARED_PLUGINS + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |