bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / salbool.cxx
blob933f80fc78391f935f1a4cace4c903a02cb2e752
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 <set>
13 #include <string>
15 #include "clang/AST/Attr.h"
17 #include "compat.hxx"
18 #include "plugin.hxx"
20 namespace {
22 bool isSalBool(QualType type) {
23 TypedefType const * t = type->getAs<TypedefType>();
24 return t != nullptr && t->getDecl()->getNameAsString() == "sal_Bool";
27 // Clang 3.2 FunctionDecl::isInlined doesn't work as advertised ("Determine
28 // whether this function should be inlined, because it is either marked 'inline'
29 // or 'constexpr' or is a member function of a class that was defined in the
30 // class body.") but mis-classifies salhelper::Timer's isTicking, isExpired, and
31 // expiresBefore members as defined in salhelper/source/timer.cxx as inlined:
32 bool isInlined(FunctionDecl const & decl) {
33 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
34 return decl.isInlined();
35 #else
36 (void)decl;
37 return false;
38 #endif
41 // It appears that, given a function declaration, there is no way to determine
42 // the language linkage of the function's type, only of the function's name
43 // (via FunctionDecl::isExternC); however, in a case like
45 // extern "C" { static void f(); }
47 // the function's name does not have C language linkage while the function's
48 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
49 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
50 // "Language linkage of function type":
51 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
52 assert(decl != nullptr);
53 if (decl->isExternC()) {
54 return true;
56 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
57 if (decl->isInExternCContext()) {
58 return true;
60 #else
61 if (decl->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
62 return true;
64 #endif
65 return false;
68 enum class OverrideKind { NO, YES, MAYBE };
70 OverrideKind getOverrideKind(FunctionDecl const * decl) {
71 CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
72 if (m == nullptr) {
73 return OverrideKind::NO;
75 if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
76 return OverrideKind::YES;
78 if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
79 return OverrideKind::NO;
81 return OverrideKind::MAYBE;
84 //TODO: current implementation is not at all general, just tests what we
85 // encounter in practice:
86 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
87 unsigned n = decl->getNumParams();
88 auto res = decl->getDeclContext()->lookup(decl->getDeclName());
89 for (auto d = compat::begin(res); d != compat::end(res); ++d) {
90 FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
91 if (f != nullptr && (!mustBeDeleted || f->isDeleted())) {
92 if (f->getNumParams() == n) {
93 bool hasSB = false;
94 for (unsigned i = 0; i != n; ++i) {
95 QualType t1 { decl->getParamDecl(i)->getType() };
96 bool isSB = isSalBool(t1);
97 bool isSBRef = !isSB && t1->isReferenceType()
98 && isSalBool(t1.getNonReferenceType());
99 QualType t2 { f->getParamDecl(i)->getType() };
100 if (!(isSB
101 ? t2->isBooleanType()
102 : isSBRef
103 ? (t2->isReferenceType()
104 && t2.getNonReferenceType()->isBooleanType())
105 : t2 == t1))
107 goto next;
109 hasSB |= isSB || isSBRef;
111 return hasSB;
112 // cheaply protect against the case where decl would have no
113 // sal_Bool parameters at all and would match itself
114 next:;
118 return false;
121 class SalBool:
122 public RecursiveASTVisitor<SalBool>, public loplugin::RewritePlugin
124 public:
125 explicit SalBool(InstantiationData const & data): RewritePlugin(data) {}
127 virtual void run() override;
129 bool VisitUnaryAddrOf(UnaryOperator const * op);
131 bool VisitCallExpr(CallExpr * expr);
133 bool VisitCStyleCastExpr(CStyleCastExpr * expr);
135 bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
137 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
139 bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
140 bool VisitParmVarDecl(ParmVarDecl const * decl);
142 bool WalkUpFromVarDecl(VarDecl const * decl);
143 bool VisitVarDecl(VarDecl const * decl);
145 bool WalkUpFromFieldDecl(FieldDecl const * decl);
146 bool VisitFieldDecl(FieldDecl const * decl);
148 bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
149 bool VisitFunctionDecl(FunctionDecl const * decl);
151 bool VisitValueDecl(ValueDecl const * decl);
153 private:
154 bool isInSpecialMainFile(SourceLocation spellingLocation) const;
156 bool rewrite(SourceLocation location);
158 std::set<VarDecl const *> varDecls_;
161 void SalBool::run() {
162 if (compiler.getLangOpts().CPlusPlus) {
163 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
164 for (auto decl: varDecls_) {
165 SourceLocation loc { decl->getLocStart() };
166 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
167 if (tsi != nullptr) {
168 SourceLocation l {
169 compiler.getSourceManager().getExpansionLoc(
170 tsi->getTypeLoc().getBeginLoc()) };
171 SourceLocation end {
172 compiler.getSourceManager().getExpansionLoc(
173 tsi->getTypeLoc().getEndLoc()) };
174 assert(l.isFileID() && end.isFileID());
175 if (l == end
176 || compiler.getSourceManager().isBeforeInTranslationUnit(
177 l, end))
179 for (;;) {
180 unsigned n = Lexer::MeasureTokenLength(
181 l, compiler.getSourceManager(),
182 compiler.getLangOpts());
183 std::string s {
184 compiler.getSourceManager().getCharacterData(l),
185 n };
186 if (s == "sal_Bool") {
187 loc = l;
188 break;
190 if (l == end) {
191 break;
193 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
197 if (!rewrite(loc)) {
198 report(
199 DiagnosticsEngine::Warning,
200 "VarDecl, use \"bool\" instead of \"sal_Bool\"", loc)
201 << decl->getSourceRange();
207 bool SalBool::VisitUnaryAddrOf(UnaryOperator const * op) {
208 Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
209 if (isSalBool(e1->getType())) {
210 DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1);
211 if (e2 != nullptr) {
212 VarDecl const * d = dyn_cast<VarDecl>(e2->getDecl());
213 if (d != nullptr) {
214 varDecls_.erase(d);
218 return true;
221 bool SalBool::VisitCallExpr(CallExpr * expr) {
222 Decl const * d = expr->getCalleeDecl();
223 FunctionProtoType const * ft = nullptr;
224 if (d != nullptr) {
225 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
226 if (fd != nullptr) {
227 PointerType const * pt = fd->getType()->getAs<PointerType>();
228 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
229 ft = t2->getAs<FunctionProtoType>();
230 assert(
231 ft != nullptr || !compiler.getLangOpts().CPlusPlus
232 || (fd->getBuiltinID() != Builtin::NotBuiltin
233 && isa<FunctionNoProtoType>(t2)));
234 // __builtin_*s have no proto type?
235 } else {
236 VarDecl const * vd = dyn_cast<VarDecl>(d);
237 if (vd != nullptr) {
238 PointerType const * pt = vd->getType()->getAs<PointerType>();
239 ft = (pt == nullptr ? vd->getType() : pt->getPointeeType())
240 ->getAs<FunctionProtoType>();
244 if (ft != nullptr) {
245 for (unsigned i = 0; i != compat::getNumParams(*ft); ++i) {
246 QualType t(compat::getParamType(*ft, i));
247 if (t->isLValueReferenceType()) {
248 t = t.getNonReferenceType();
249 if (!t.isConstQualified() && isSalBool(t)
250 && i < expr->getNumArgs())
252 DeclRefExpr * ref = dyn_cast<DeclRefExpr>(expr->getArg(i));
253 if (ref != nullptr) {
254 VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
255 if (d != nullptr) {
256 varDecls_.erase(d);
263 return true;
266 bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
267 if (ignoreLocation(expr)) {
268 return true;
270 if (isSalBool(expr->getType())) {
271 SourceLocation loc { expr->getLocStart() };
272 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
273 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
275 if (compat::isMacroBodyExpansion(compiler, loc)) {
276 StringRef name { Lexer::getImmediateMacroName(
277 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
278 if (name == "sal_False" || name == "sal_True") {
279 return true;
282 report(
283 DiagnosticsEngine::Warning,
284 "CStyleCastExpr, suspicious cast from %0 to %1",
285 expr->getLocStart())
286 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
287 << expr->getType() << expr->getSourceRange();
289 return true;
292 bool SalBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
293 if (ignoreLocation(expr)) {
294 return true;
296 if (isSalBool(expr->getType())) {
297 report(
298 DiagnosticsEngine::Warning,
299 "CStyleCastExpr, suspicious cast from %0 to %1",
300 expr->getLocStart())
301 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
302 << expr->getType() << expr->getSourceRange();
304 return true;
307 bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
308 if (ignoreLocation(expr)) {
309 return true;
311 if (isSalBool(expr->getType())) {
312 report(
313 DiagnosticsEngine::Warning,
314 "CStyleCastExpr, suspicious cast from %0 to %1",
315 expr->getLocStart())
316 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
317 << expr->getType() << expr->getSourceRange();
319 return true;
322 bool SalBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
323 return VisitParmVarDecl(decl);
326 bool SalBool::VisitParmVarDecl(ParmVarDecl const * decl) {
327 if (ignoreLocation(decl)) {
328 return true;
330 if (isSalBool(decl->getType().getNonReferenceType())) {
331 FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
332 if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
333 f = f->getCanonicalDecl();
334 if (!(hasCLanguageLinkageType(f)
335 || (isInUnoIncludeFile(
336 compiler.getSourceManager().getSpellingLoc(
337 f->getNameInfo().getLoc()))
338 && (!isInlined(*f) || f->hasAttr<DeprecatedAttr>()
339 || decl->getType()->isReferenceType()
340 || hasBoolOverload(f, false)))
341 || f->isDeleted() || hasBoolOverload(f, true)))
343 OverrideKind k = getOverrideKind(f);
344 if (k != OverrideKind::YES) {
345 SourceLocation loc { decl->getLocStart() };
346 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
347 if (tsi != nullptr) {
348 SourceLocation l {
349 compiler.getSourceManager().getExpansionLoc(
350 tsi->getTypeLoc().getBeginLoc()) };
351 SourceLocation end {
352 compiler.getSourceManager().getExpansionLoc(
353 tsi->getTypeLoc().getEndLoc()) };
354 assert(l.isFileID() && end.isFileID());
355 if (l == end
356 || (compiler.getSourceManager()
357 .isBeforeInTranslationUnit(l, end)))
359 for (;;) {
360 unsigned n = Lexer::MeasureTokenLength(
361 l, compiler.getSourceManager(),
362 compiler.getLangOpts());
363 std::string s {
364 compiler.getSourceManager().getCharacterData(l),
365 n };
366 if (s == "sal_Bool") {
367 loc = l;
368 break;
370 if (l == end) {
371 break;
373 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
377 // Only rewrite declarations in include files if a
378 // definition is also seen, to avoid compilation of a
379 // definition (in a main file only processed later) to fail
380 // with a "mismatch" error before the rewriter had a chance
381 // to act upon the definition (but use the heuristic of
382 // assuming pure virtual functions do not have definitions);
383 // also, do not automatically rewrite functions that could
384 // implicitly override depend base functions (and thus stop
385 // doing so after the rewrite; note that this is less
386 // dangerous for return types than for parameter types,
387 // where the function would still implicitly override and
388 // cause a compilation error due to the incompatible return
389 // type):
390 if (!((compat::isInMainFile(
391 compiler.getSourceManager(),
392 compiler.getSourceManager().getSpellingLoc(
393 dyn_cast<FunctionDecl>(
394 decl->getDeclContext())
395 ->getNameInfo().getLoc()))
396 || f->isDefined() || f->isPure())
397 && k != OverrideKind::MAYBE && rewrite(loc)))
399 report(
400 DiagnosticsEngine::Warning,
401 ("ParmVarDecl, use \"bool\" instead of"
402 " \"sal_Bool\"%0"),
403 loc)
404 << (k == OverrideKind::MAYBE
405 ? (" (unless this member function overrides a"
406 " dependent base member function, even"
407 " though it is not marked 'override')")
408 : "")
409 << decl->getSourceRange();
415 return true;
418 bool SalBool::WalkUpFromVarDecl(VarDecl const * decl) {
419 return VisitVarDecl(decl);
422 bool SalBool::VisitVarDecl(VarDecl const * decl) {
423 if (ignoreLocation(decl)) {
424 return true;
426 if (!decl->isExternC() && isSalBool(decl->getType())
427 && !isInSpecialMainFile(
428 compiler.getSourceManager().getSpellingLoc(decl->getLocStart())))
430 varDecls_.insert(decl);
432 return true;
435 bool SalBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
436 return VisitFieldDecl(decl);
439 bool SalBool::VisitFieldDecl(FieldDecl const * decl) {
440 if (ignoreLocation(decl)) {
441 return true;
443 if (isSalBool(decl->getType())) {
444 TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
445 assert(td != nullptr);
446 if (!(((td->isStruct() || td->isUnion())
447 && compat::isExternCContext(*td))
448 || isInUnoIncludeFile(
449 compiler.getSourceManager().getSpellingLoc(
450 decl->getLocation()))))
452 SourceLocation loc { decl->getLocStart() };
453 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
454 if (tsi != nullptr) {
455 SourceLocation l {
456 compiler.getSourceManager().getExpansionLoc(
457 tsi->getTypeLoc().getBeginLoc()) };
458 SourceLocation end {
459 compiler.getSourceManager().getExpansionLoc(
460 tsi->getTypeLoc().getEndLoc()) };
461 assert(l.isFileID() && end.isFileID());
462 if (l == end
463 || compiler.getSourceManager().isBeforeInTranslationUnit(
464 l, end))
466 for (;;) {
467 unsigned n = Lexer::MeasureTokenLength(
468 l, compiler.getSourceManager(),
469 compiler.getLangOpts());
470 std::string s {
471 compiler.getSourceManager().getCharacterData(l),
472 n };
473 if (s == "sal_Bool") {
474 loc = l;
475 break;
477 if (l == end) {
478 break;
480 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
484 if (!rewrite(loc)) {
485 report(
486 DiagnosticsEngine::Warning,
487 "FieldDecl, use \"bool\" instead of \"sal_Bool\"", loc)
488 << decl->getSourceRange();
492 return true;
495 bool SalBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
496 return VisitFunctionDecl(decl);
499 bool SalBool::VisitFunctionDecl(FunctionDecl const * decl) {
500 if (ignoreLocation(decl)) {
501 return true;
503 if (isSalBool(compat::getReturnType(*decl).getNonReferenceType())) {
504 FunctionDecl const * f = decl->getCanonicalDecl();
505 OverrideKind k = getOverrideKind(f);
506 if (k != OverrideKind::YES
507 && !(hasCLanguageLinkageType(f)
508 || (isInUnoIncludeFile(
509 compiler.getSourceManager().getSpellingLoc(
510 f->getNameInfo().getLoc()))
511 && (!isInlined(*f) || f->hasAttr<DeprecatedAttr>()))))
513 SourceLocation loc { decl->getLocStart() };
514 SourceLocation l { compiler.getSourceManager().getExpansionLoc(
515 loc) };
516 SourceLocation end { compiler.getSourceManager().getExpansionLoc(
517 decl->getNameInfo().getLoc()) };
518 assert(l.isFileID() && end.isFileID());
519 if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
520 while (l != end) {
521 unsigned n = Lexer::MeasureTokenLength(
522 l, compiler.getSourceManager(), compiler.getLangOpts());
523 std::string s {
524 compiler.getSourceManager().getCharacterData(l), n };
525 if (s == "sal_Bool") {
526 loc = l;
527 break;
529 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
532 // Only rewrite declarations in include files if a definition is
533 // also seen, to avoid compilation of a definition (in a main file
534 // only processed later) to fail with a "mismatch" error before the
535 // rewriter had a chance to act upon the definition (but use the
536 // heuristic of assuming pure virtual functions do not have
537 // definitions):
538 if (!((compat::isInMainFile(
539 compiler.getSourceManager(),
540 compiler.getSourceManager().getSpellingLoc(
541 decl->getNameInfo().getLoc()))
542 || f->isDefined() || f->isPure())
543 && rewrite(loc)))
545 report(
546 DiagnosticsEngine::Warning,
547 "use \"bool\" instead of \"sal_Bool\" as return type%0",
548 loc)
549 << (k == OverrideKind::MAYBE
550 ? (" (unless this member function overrides a dependent"
551 " base member function, even though it is not marked"
552 " 'override')")
553 : "")
554 << decl->getSourceRange();
558 return true;
561 bool SalBool::VisitValueDecl(ValueDecl const * decl) {
562 if (ignoreLocation(decl)) {
563 return true;
565 if (isSalBool(decl->getType()) && !rewrite(decl->getLocStart())) {
566 report(
567 DiagnosticsEngine::Warning,
568 "ValueDecl, use \"bool\" instead of \"sal_Bool\"",
569 decl->getLocStart())
570 << decl->getSourceRange();
572 return true;
575 bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
576 return compat::isInMainFile(compiler.getSourceManager(), spellingLocation)
577 && (compiler.getSourceManager().getFilename(spellingLocation)
578 == SRCDIR "/cppu/qa/test_any.cxx");
581 bool SalBool::rewrite(SourceLocation location) {
582 if (rewriter != nullptr) {
583 //TODO: "::sal_Bool" -> "bool", not "::bool"
584 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
585 location) };
586 unsigned n = Lexer::MeasureTokenLength(
587 loc, compiler.getSourceManager(), compiler.getLangOpts());
588 if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
589 == "sal_Bool")
591 return replaceText(loc, n, "bool");
594 return false;
597 loplugin::Plugin::Registration<SalBool> X("salbool", true);
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */