lok: Don't attempt to select the exact text after a failed search.
[LibreOffice.git] / compilerplugins / clang / saloverride.cxx
blobbb6766b64823669353a17065d88509d57da05dd5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <algorithm>
11 #include <cassert>
12 #include <limits>
13 #include <set>
15 #include "clang/AST/Attr.h"
17 #include "compat.hxx"
18 #include "plugin.hxx"
20 namespace {
22 class SalOverride:
23 public RecursiveASTVisitor<SalOverride>, public loplugin::RewritePlugin
25 public:
26 explicit SalOverride(InstantiationData const & data): RewritePlugin(data) {}
28 virtual void run() override;
30 bool VisitCXXMethodDecl(CXXMethodDecl const * decl);
32 private:
33 std::set<SourceLocation> insertions_;
36 void SalOverride::run() {
37 if (compiler.getLangOpts().CPlusPlus
38 && compiler.getPreprocessor().getIdentifierInfo(
39 "LIBO_INTERNAL_ONLY")->hasMacroDefinition())
41 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
44 bool SalOverride::VisitCXXMethodDecl(CXXMethodDecl const * decl) {
45 // As a heuristic, ignore declarations where the name is spelled out in an
46 // ignored location; that e.g. handles uses of the Q_OBJECT macro from
47 // external QtCore/qobjectdefs.h:
48 if (ignoreLocation(decl) || !compat::isFirstDecl(*decl)
49 || decl->begin_overridden_methods() == decl->end_overridden_methods()
50 || decl->hasAttr<OverrideAttr>()
51 || ignoreLocation(
52 compiler.getSourceManager().getSpellingLoc(
53 decl->getNameInfo().getLoc())))
55 return true;
57 // It appears that the C++ standard allows overriding destructors to be
58 // marked "override," but at least some MSVC versions complain about it, so
59 // at least make sure such destructors are explicitly marked "virtual":
60 if (isa<CXXDestructorDecl>(decl)) {
61 if (!decl->isVirtualAsWritten()
62 && (rewriter == nullptr
63 || !insertTextBefore(
64 decl->getSourceRange().getBegin(), "virtual ")))
66 report(
67 DiagnosticsEngine::Warning,
68 ("overriding destructor declaration not explicitly marked"
69 " 'virtual'"),
70 decl->getLocation())
71 << decl->getSourceRange();
73 return true;
75 #if LO_COMPILERPLUGINS_CLANG_COMPAT_HAVE_isAtEndOfImmediateMacroExpansion
76 if (rewriter != nullptr) {
77 // In void MACRO(...); getSourceRange().getEnd() would (erroneously?)
78 // point at "MACRO" rather than ")", so make the loop always terminate
79 // at the first ";" or "{" instead of getSourceRange().getEnd():
80 unsigned parens = 0;
81 bool seenSpace = false;
82 //TODO: Whether to add a space after the inserted "SAL_OVERRIDE" should
83 // depend on the following token at the spelling location where
84 // "SAL_OVERRIDE" is inserted, not on the following token in the fully-
85 // macro-expanded view:
86 bool addSpace;
87 SourceLocation loc;
88 for (SourceLocation l(decl->getSourceRange().getBegin());;) {
89 SourceLocation sl(compiler.getSourceManager().getSpellingLoc(l));
90 unsigned n = Lexer::MeasureTokenLength(
91 sl, compiler.getSourceManager(), compiler.getLangOpts());
92 StringRef s(compiler.getSourceManager().getCharacterData(sl), n);
93 //TODO: Looks like a Clang bug that in some cases like
94 // (filter/source/svg/svgexport.cxx)
96 // #define TEXT_FIELD_GET_CLASS_NAME_METHOD( class_name ) \
97 // virtual OUString getClassName() const \
98 // { \
99 // static const char className[] = #class_name; \
100 // return OUString( className ); \
101 // }
103 // TEXT_FIELD_GET_CLASS_NAME_METHOD( TextField )
105 // where "\<NL>" is followed directly by a real token without
106 // intervening whitespace, tokens "\<NL>virtual" and "\<NL>{" are
107 // reported:
108 if (s.startswith("\\\n")) {
109 s = s.drop_front(2);
111 if (parens == 0) {
112 if (s == "=" || s == "{") {
113 if (!seenSpace) {
114 addSpace = true;
116 break;
118 if (s == ";") {
119 break;
122 if (s == "(") {
123 assert(parens < std::numeric_limits<unsigned>::max());
124 ++parens;
125 } else if (s == ")") {
126 assert(parens != 0);
127 --parens;
129 if (s.empty()) {
130 if (!seenSpace) {
131 addSpace = false;
133 seenSpace = true;
134 } else if (s.startswith("/*") || s.startswith("//") || s == "\\") {
135 if (!seenSpace) {
136 addSpace = true;
138 seenSpace = true;
139 } else {
140 seenSpace = false;
141 addSpace = false;
142 loc = sl;
144 if (l.isMacroID()
145 && compiler.getSourceManager().isAtEndOfImmediateMacroExpansion(
146 l, &l))
148 n = Lexer::MeasureTokenLength(
149 l, compiler.getSourceManager(), compiler.getLangOpts());
151 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
153 assert(loc.isValid());
154 if (!insertions_.insert(loc).second
155 || insertTextAfterToken(
156 loc, addSpace ? " SAL_OVERRIDE " : " SAL_OVERRIDE"))
158 return true;
161 #endif
162 report(
163 DiagnosticsEngine::Warning,
164 ("overriding virtual function declaration not marked 'override' (aka"
165 " 'SAL_OVERRIDE')"),
166 decl->getLocation())
167 << decl->getSourceRange();
168 for (auto i = decl->begin_overridden_methods();
169 i != decl->end_overridden_methods(); ++i)
171 report(
172 DiagnosticsEngine::Note, "overridden declaration is here",
173 (*i)->getLocation())
174 << (*i)->getSourceRange();
176 return true;
179 loplugin::Plugin::Registration<SalOverride> X("saloverride", true);
183 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */