/* -*- 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 #include #include #include #include #include "clang/AST/Attr.h" #include "compat.hxx" #include "plugin.hxx" namespace { class OverrideVirtual: public loplugin::FilteringRewritePlugin { 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 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() || 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 "\" is followed directly by a real token without // intervening whitespace, tokens "\virtual" and "\{" are // reported: if (compat::starts_with(s, "\\\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::max()); ++parens; } else if (s == ")") { assert(parens != 0); --parens; } if (s.empty()) { if (!seenSpace) { addSpace = false; } seenSpace = true; } else if (compat::starts_with(s, "/*") || compat::starts_with(s, "//") || 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(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"); } // namespace #endif // LO_CLANG_SHARED_PLUGINS /* vim:set shiftwidth=4 softtabstop=4 expandtab: */