1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
/* -*- 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 <set>
#include "clang/AST/Attr.h"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
class SalOverride:
public RecursiveASTVisitor<SalOverride>, public loplugin::RewritePlugin
{
public:
explicit SalOverride(InstantiationData const & data): RewritePlugin(data) {}
virtual void run() override
{ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
bool VisitCXXMethodDecl(CXXMethodDecl const * decl);
private:
std::set<SourceLocation> insertions_;
};
bool SalOverride::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) || !compat::isFirstDecl(*decl)
|| decl->begin_overridden_methods() == decl->end_overridden_methods()
|| decl->hasAttr<OverrideAttr>()
|| ignoreLocation(
compiler.getSourceManager().getSpellingLoc(
decl->getNameInfo().getLoc())))
{
return true;
}
// It appears that the C++ standard allows overriding destructors to be
// marked "override," but at least some MSVC versions complain about it, so
// at least make sure such destructors are explicitly marked "virtual":
if (isa<CXXDestructorDecl>(decl)) {
if (!decl->isVirtualAsWritten()
&& (rewriter == nullptr
|| !insertTextBefore(
decl->getSourceRange().getBegin(), "virtual ")))
{
report(
DiagnosticsEngine::Warning,
("overriding destructor declaration not explicitly marked"
" 'virtual'"),
decl->getLocation())
<< decl->getSourceRange();
}
return true;
}
#if LO_COMPILERPLUGINS_CLANG_COMPAT_HAVE_isAtEndOfImmediateMacroExpansion
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 the 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;
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(
l, compiler.getSourceManager(), compiler.getLangOpts());
}
l = l.getLocWithOffset(std::max<unsigned>(n, 1));
}
assert(loc.isValid());
if (!insertions_.insert(loc).second
|| insertTextAfterToken(
loc, addSpace ? " SAL_OVERRIDE " : " SAL_OVERRIDE"))
{
return true;
}
}
#endif
report(
DiagnosticsEngine::Warning,
("overriding virtual function declaration not marked 'override' (aka"
" 'SAL_OVERRIDE')"),
decl->getLocation())
<< 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<SalOverride> X("saloverride", true);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|