bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / salbool.cxx
blobad71435470f05376b8bfcf8ac24c47a62c053650
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>
14 #include <string>
16 #include "clang/AST/Attr.h"
18 #include "check.hxx"
19 #include "compat.hxx"
20 #include "plugin.hxx"
22 namespace {
24 bool isSalBool(QualType type) {
25 TypedefType const * t = type->getAs<TypedefType>();
26 return t != nullptr && t->getDecl()->getNameAsString() == "sal_Bool";
29 bool isSalBoolArray(QualType type) {
30 auto t = type->getAsArrayTypeUnsafe();
31 return t != nullptr
32 && (isSalBool(t->getElementType())
33 || isSalBoolArray(t->getElementType()));
36 // It appears that, given a function declaration, there is no way to determine
37 // the language linkage of the function's type, only of the function's name
38 // (via FunctionDecl::isExternC); however, in a case like
40 // extern "C" { static void f(); }
42 // the function's name does not have C language linkage while the function's
43 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
44 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
45 // "Language linkage of function type":
46 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
47 assert(decl != nullptr);
48 if (decl->isExternC()) {
49 return true;
51 if (decl->isInExternCContext()) {
52 return true;
54 return false;
57 enum class OverrideKind { NO, YES, MAYBE };
59 OverrideKind getOverrideKind(FunctionDecl const * decl) {
60 CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
61 if (m == nullptr) {
62 return OverrideKind::NO;
64 if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
65 return OverrideKind::YES;
67 if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
68 return OverrideKind::NO;
70 return OverrideKind::MAYBE;
73 enum class BoolOverloadKind { No, Yes, CheckNext };
75 BoolOverloadKind isBoolOverloadOf(
76 FunctionDecl const * f, FunctionDecl const * decl, bool mustBeDeleted)
78 if (!mustBeDeleted || f->isDeleted()) {
79 unsigned n = decl->getNumParams();
80 if (f->getNumParams() == n) {
81 bool hasSB = false;
82 for (unsigned i = 0; i != n; ++i) {
83 QualType t1 { decl->getParamDecl(i)->getType() };
84 bool isSB = isSalBool(t1);
85 bool isSBRef = !isSB && t1->isReferenceType()
86 && isSalBool(t1.getNonReferenceType());
87 QualType t2 { f->getParamDecl(i)->getType() };
88 if (!(isSB
89 ? t2->isBooleanType()
90 : isSBRef
91 ? (t2->isReferenceType()
92 && t2.getNonReferenceType()->isBooleanType())
93 : t2.getCanonicalType() == t1.getCanonicalType()))
95 return BoolOverloadKind::CheckNext;
97 hasSB |= isSB || isSBRef;
99 return hasSB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
100 // cheaply protect against the case where decl would have no
101 // sal_Bool parameters at all and would match itself
104 return BoolOverloadKind::CheckNext;
107 //TODO: current implementation is not at all general, just tests what we
108 // encounter in practice:
109 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
110 auto ctx = decl->getDeclContext();
111 if (!ctx->isLookupContext()) {
112 return false;
114 auto res = ctx->lookup(decl->getDeclName());
115 for (auto d = res.begin(); d != res.end(); ++d) {
116 if (auto f = dyn_cast<FunctionDecl>(*d)) {
117 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
118 case BoolOverloadKind::No:
119 return false;
120 case BoolOverloadKind::Yes:
121 return true;
122 case BoolOverloadKind::CheckNext:
123 break;
125 } else if (auto ftd = dyn_cast<FunctionTemplateDecl>(*d)) {
126 for (auto f: ftd->specializations()) {
127 if (f->getTemplateSpecializationKind()
128 == TSK_ExplicitSpecialization)
130 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
131 case BoolOverloadKind::No:
132 return false;
133 case BoolOverloadKind::Yes:
134 return true;
135 case BoolOverloadKind::CheckNext:
136 break;
142 return false;
145 class SalBool:
146 public loplugin::FilteringRewritePlugin<SalBool>
148 public:
149 explicit SalBool(loplugin::InstantiationData const & data):
150 FilteringRewritePlugin(data) {}
152 virtual void run() override;
154 bool VisitUnaryAddrOf(UnaryOperator const * op);
156 bool VisitCallExpr(CallExpr * expr);
158 bool VisitCStyleCastExpr(CStyleCastExpr * expr);
160 bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
162 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
164 bool VisitImplicitCastExpr(ImplicitCastExpr * expr);
166 bool VisitReturnStmt(ReturnStmt const * stmt);
168 bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
169 bool VisitParmVarDecl(ParmVarDecl const * decl);
171 bool WalkUpFromVarDecl(VarDecl const * decl);
172 bool VisitVarDecl(VarDecl const * decl);
174 bool WalkUpFromFieldDecl(FieldDecl const * decl);
175 bool VisitFieldDecl(FieldDecl const * decl);
177 bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
178 bool VisitFunctionDecl(FunctionDecl const * decl);
180 bool VisitValueDecl(ValueDecl const * decl);
182 bool TraverseStaticAssertDecl(StaticAssertDecl * decl);
184 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
186 private:
187 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
189 bool isSharedCAndCppCode(SourceLocation location) const;
191 bool isInSpecialMainFile(SourceLocation spellingLocation) const;
193 bool rewrite(SourceLocation location);
195 std::set<VarDecl const *> varDecls_;
196 unsigned int externCContexts_ = 0;
199 void SalBool::run() {
200 if (compiler.getLangOpts().CPlusPlus) {
201 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
202 for (auto decl: varDecls_) {
203 SourceLocation loc { compat::getBeginLoc(decl) };
204 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
205 if (tsi != nullptr) {
206 SourceLocation l {
207 compiler.getSourceManager().getExpansionLoc(
208 tsi->getTypeLoc().getBeginLoc()) };
209 SourceLocation end {
210 compiler.getSourceManager().getExpansionLoc(
211 tsi->getTypeLoc().getEndLoc()) };
212 assert(l.isFileID() && end.isFileID());
213 if (l == end
214 || compiler.getSourceManager().isBeforeInTranslationUnit(
215 l, end))
217 for (;;) {
218 unsigned n = Lexer::MeasureTokenLength(
219 l, compiler.getSourceManager(),
220 compiler.getLangOpts());
221 std::string s {
222 compiler.getSourceManager().getCharacterData(l),
223 n };
224 if (s == "sal_Bool") {
225 loc = l;
226 break;
228 if (l == end) {
229 break;
231 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
235 if (!rewrite(loc)) {
236 report(
237 DiagnosticsEngine::Warning,
238 "VarDecl, use \"bool\" instead of \"sal_Bool\"", loc)
239 << decl->getSourceRange();
245 bool SalBool::VisitUnaryAddrOf(UnaryOperator const * op) {
246 Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
247 if (isSalBool(e1->getType())) {
248 DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1);
249 if (e2 != nullptr) {
250 VarDecl const * d = dyn_cast<VarDecl>(e2->getDecl());
251 if (d != nullptr) {
252 varDecls_.erase(d);
256 return true;
259 bool SalBool::VisitCallExpr(CallExpr * expr) {
260 Decl const * d = expr->getCalleeDecl();
261 FunctionProtoType const * ft = nullptr;
262 if (d != nullptr) {
263 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
264 if (fd != nullptr) {
265 if (!hasBoolOverload(fd, false)) {
266 clang::PointerType const * pt = fd->getType()
267 ->getAs<clang::PointerType>();
268 QualType t2(
269 pt == nullptr ? fd->getType() : pt->getPointeeType());
270 ft = t2->getAs<FunctionProtoType>();
271 assert(
272 ft != nullptr || !compiler.getLangOpts().CPlusPlus
273 || (fd->getBuiltinID() != Builtin::NotBuiltin
274 && isa<FunctionNoProtoType>(t2)));
275 // __builtin_*s have no proto type?
277 } else {
278 VarDecl const * vd = dyn_cast<VarDecl>(d);
279 if (vd != nullptr) {
280 clang::PointerType const * pt = vd->getType()
281 ->getAs<clang::PointerType>();
282 ft = (pt == nullptr ? vd->getType() : pt->getPointeeType())
283 ->getAs<FunctionProtoType>();
287 if (ft != nullptr) {
288 for (unsigned i = 0; i != ft->getNumParams(); ++i) {
289 QualType t(ft->getParamType(i));
290 bool b = false;
291 if (t->isLValueReferenceType()) {
292 t = t.getNonReferenceType();
293 b = !t.isConstQualified() && isSalBool(t);
294 } else if (t->isPointerType()) {
295 for (;;) {
296 auto t2 = t->getAs<clang::PointerType>();
297 if (t2 == nullptr) {
298 break;
300 t = t2->getPointeeType();
302 b = isSalBool(t);
304 if (b && i < expr->getNumArgs()) {
305 DeclRefExpr * ref = dyn_cast<DeclRefExpr>(
306 expr->getArg(i)->IgnoreParenImpCasts());
307 if (ref != nullptr) {
308 VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
309 if (d != nullptr) {
310 varDecls_.erase(d);
316 return true;
319 bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
320 if (ignoreLocation(expr)) {
321 return true;
323 if (isSalBool(expr->getType())) {
324 SourceLocation loc { compat::getBeginLoc(expr) };
325 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
326 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
328 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
329 StringRef name { Lexer::getImmediateMacroName(
330 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
331 if (name == "sal_False" || name == "sal_True") {
332 auto callLoc = compiler.getSourceManager()
333 .getImmediateMacroCallerLoc(loc);
334 if (!isSharedCAndCppCode(callLoc)) {
335 SourceLocation argLoc;
336 if (compiler.getSourceManager().isMacroArgExpansion(
337 compat::getBeginLoc(expr), &argLoc)
338 //TODO: check it's the complete (first) arg to the macro
339 && (Lexer::getImmediateMacroName(
340 argLoc, compiler.getSourceManager(),
341 compiler.getLangOpts())
342 == "CPPUNIT_ASSERT_EQUAL"))
344 // Ignore sal_False/True that are directly used as
345 // arguments to CPPUNIT_ASSERT_EQUAL:
346 return true;
348 bool b = name == "sal_True";
349 if (rewriter != nullptr) {
350 auto callSpellLoc = compiler.getSourceManager()
351 .getSpellingLoc(callLoc);
352 unsigned n = Lexer::MeasureTokenLength(
353 callSpellLoc, compiler.getSourceManager(),
354 compiler.getLangOpts());
355 if (StringRef(
356 compiler.getSourceManager().getCharacterData(
357 callSpellLoc),
359 == name)
361 return replaceText(
362 callSpellLoc, n, b ? "true" : "false");
365 report(
366 DiagnosticsEngine::Warning,
367 "use '%select{false|true}0' instead of '%1'", callLoc)
368 << b << name << expr->getSourceRange();
370 return true;
373 report(
374 DiagnosticsEngine::Warning,
375 "CStyleCastExpr, suspicious cast from %0 to %1",
376 compat::getBeginLoc(expr))
377 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
378 << expr->getType() << expr->getSourceRange();
380 return true;
383 bool SalBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
384 if (ignoreLocation(expr)) {
385 return true;
387 if (isSalBool(expr->getType())
388 && !isInSpecialMainFile(
389 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr))))
391 report(
392 DiagnosticsEngine::Warning,
393 "CXXStaticCastExpr, suspicious cast from %0 to %1",
394 compat::getBeginLoc(expr))
395 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
396 << expr->getType() << expr->getSourceRange();
398 return true;
401 bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
402 if (ignoreLocation(expr)) {
403 return true;
405 if (isSalBool(expr->getType())) {
406 report(
407 DiagnosticsEngine::Warning,
408 "CXXFunctionalCastExpr, suspicious cast from %0 to %1",
409 compat::getBeginLoc(expr))
410 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
411 << expr->getType() << expr->getSourceRange();
413 return true;
416 bool SalBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
417 if (ignoreLocation(expr)) {
418 return true;
420 if (!isSalBool(expr->getType())) {
421 return true;
423 auto l = compat::getBeginLoc(expr);
424 while (compiler.getSourceManager().isMacroArgExpansion(l)) {
425 l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
427 if (compiler.getSourceManager().isMacroBodyExpansion(l)) {
428 auto n = Lexer::getImmediateMacroName(
429 l, compiler.getSourceManager(), compiler.getLangOpts());
430 if (n == "sal_False" || n == "sal_True") {
431 return true;
434 auto e1 = expr->getSubExprAsWritten();
435 auto t = e1->getType();
436 if (!t->isFundamentalType() || loplugin::TypeCheck(t).AnyBoolean()) {
437 return true;
439 auto e2 = dyn_cast<ConditionalOperator>(e1);
440 if (e2 != nullptr) {
441 auto ic1 = dyn_cast<ImplicitCastExpr>(
442 e2->getTrueExpr()->IgnoreParens());
443 auto ic2 = dyn_cast<ImplicitCastExpr>(
444 e2->getFalseExpr()->IgnoreParens());
445 if (ic1 != nullptr && ic2 != nullptr
446 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
447 && (loplugin::TypeCheck(ic1->getSubExprAsWritten()->getType())
448 .AnyBoolean())
449 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
450 && (loplugin::TypeCheck(ic2->getSubExprAsWritten()->getType())
451 .AnyBoolean()))
453 return true;
456 report(
457 DiagnosticsEngine::Warning, "conversion from %0 to sal_Bool",
458 compat::getBeginLoc(expr))
459 << t << expr->getSourceRange();
460 return true;
463 bool SalBool::VisitReturnStmt(ReturnStmt const * stmt) {
464 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
465 // which has
467 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
469 // and
471 // return aCharClass[eCharClass];
473 if (ignoreLocation(stmt)) {
474 return true;
476 auto e = stmt->getRetValue();
477 if (e == nullptr) {
478 return true;
480 auto t = e->getType();
481 if (!t->isPointerType()) {
482 return true;
484 for (;;) {
485 auto t2 = t->getAs<clang::PointerType>();
486 if (t2 == nullptr) {
487 break;
489 t = t2->getPointeeType();
491 if (!isSalBool(t)) {
492 return true;
494 auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
495 if (e2 == nullptr) {
496 return true;
498 auto e3 = dyn_cast<DeclRefExpr>(e2->getBase()->IgnoreParenImpCasts());
499 if (e3 == nullptr) {
500 return true;
502 auto d = dyn_cast<VarDecl>(e3->getDecl());
503 if (d == nullptr) {
504 return true;
506 varDecls_.erase(d);
507 return true;
510 bool SalBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
511 return VisitParmVarDecl(decl);
514 bool SalBool::VisitParmVarDecl(ParmVarDecl const * decl) {
515 if (ignoreLocation(decl)) {
516 return true;
518 if (isSalBool(decl->getType().getNonReferenceType())) {
519 FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
520 if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
521 f = f->getCanonicalDecl();
522 if (!(hasCLanguageLinkageType(f)
523 || (isInUnoIncludeFile(f)
524 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
525 || decl->getType()->isReferenceType()
526 || hasBoolOverload(f, false)))
527 || f->isDeleted() || hasBoolOverload(f, true)))
529 OverrideKind k = getOverrideKind(f);
530 if (k != OverrideKind::YES) {
531 SourceLocation loc { compat::getBeginLoc(decl) };
532 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
533 if (tsi != nullptr) {
534 SourceLocation l {
535 compiler.getSourceManager().getExpansionLoc(
536 tsi->getTypeLoc().getBeginLoc()) };
537 SourceLocation end {
538 compiler.getSourceManager().getExpansionLoc(
539 tsi->getTypeLoc().getEndLoc()) };
540 assert(l.isFileID() && end.isFileID());
541 if (l == end
542 || (compiler.getSourceManager()
543 .isBeforeInTranslationUnit(l, end)))
545 for (;;) {
546 unsigned n = Lexer::MeasureTokenLength(
547 l, compiler.getSourceManager(),
548 compiler.getLangOpts());
549 std::string s {
550 compiler.getSourceManager().getCharacterData(l),
551 n };
552 if (s == "sal_Bool") {
553 loc = l;
554 break;
556 if (l == end) {
557 break;
559 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
563 // Only rewrite declarations in include files if a
564 // definition is also seen, to avoid compilation of a
565 // definition (in a main file only processed later) to fail
566 // with a "mismatch" error before the rewriter had a chance
567 // to act upon the definition (but use the heuristic of
568 // assuming pure virtual functions do not have definitions);
569 // also, do not automatically rewrite functions that could
570 // implicitly override depend base functions (and thus stop
571 // doing so after the rewrite; note that this is less
572 // dangerous for return types than for parameter types,
573 // where the function would still implicitly override and
574 // cause a compilation error due to the incompatible return
575 // type):
576 if (!((compiler.getSourceManager().isInMainFile(
577 compiler.getSourceManager().getSpellingLoc(
578 dyn_cast<FunctionDecl>(
579 decl->getDeclContext())
580 ->getNameInfo().getLoc()))
581 || f->isDefined() || f->isPure())
582 && k != OverrideKind::MAYBE && rewrite(loc)))
584 report(
585 DiagnosticsEngine::Warning,
586 ("ParmVarDecl, use \"bool\" instead of"
587 " \"sal_Bool\"%0"),
588 loc)
589 << (k == OverrideKind::MAYBE
590 ? (" (unless this member function overrides a"
591 " dependent base member function, even"
592 " though it is not marked 'override')")
593 : "")
594 << decl->getSourceRange();
600 return true;
603 bool SalBool::WalkUpFromVarDecl(VarDecl const * decl) {
604 return VisitVarDecl(decl);
607 bool SalBool::VisitVarDecl(VarDecl const * decl) {
608 if (ignoreLocation(decl)) {
609 return true;
611 if (!decl->isExternC()
612 && (isSalBool(decl->getType()) || isSalBoolArray(decl->getType()))
613 && !isInSpecialMainFile(
614 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
616 varDecls_.insert(decl);
618 return true;
621 bool SalBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
622 return VisitFieldDecl(decl);
625 bool SalBool::VisitFieldDecl(FieldDecl const * decl) {
626 if (ignoreLocation(decl)) {
627 return true;
629 if ((isSalBool(decl->getType()) || isSalBoolArray(decl->getType()))
630 && !isInSpecialMainFile(
631 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
633 TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
634 assert(td != nullptr);
635 if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
636 || isInUnoIncludeFile(
637 compiler.getSourceManager().getSpellingLoc(
638 decl->getLocation()))))
640 SourceLocation loc { compat::getBeginLoc(decl) };
641 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
642 if (tsi != nullptr) {
643 SourceLocation l {
644 compiler.getSourceManager().getExpansionLoc(
645 tsi->getTypeLoc().getBeginLoc()) };
646 SourceLocation end {
647 compiler.getSourceManager().getExpansionLoc(
648 tsi->getTypeLoc().getEndLoc()) };
649 assert(l.isFileID() && end.isFileID());
650 if (l == end
651 || compiler.getSourceManager().isBeforeInTranslationUnit(
652 l, end))
654 for (;;) {
655 unsigned n = Lexer::MeasureTokenLength(
656 l, compiler.getSourceManager(),
657 compiler.getLangOpts());
658 std::string s {
659 compiler.getSourceManager().getCharacterData(l),
660 n };
661 if (s == "sal_Bool") {
662 loc = l;
663 break;
665 if (l == end) {
666 break;
668 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
672 if (!rewrite(loc)) {
673 report(
674 DiagnosticsEngine::Warning,
675 "FieldDecl, use \"bool\" instead of \"sal_Bool\"", loc)
676 << decl->getSourceRange();
680 return true;
683 bool SalBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
684 return VisitFunctionDecl(decl);
687 bool SalBool::VisitFunctionDecl(FunctionDecl const * decl) {
688 if (ignoreLocation(decl)) {
689 return true;
691 if (isSalBool(decl->getReturnType().getNonReferenceType())
692 && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl)))
694 FunctionDecl const * f = decl->getCanonicalDecl();
695 OverrideKind k = getOverrideKind(f);
696 if (k != OverrideKind::YES
697 && !(hasCLanguageLinkageType(f)
698 || (isInUnoIncludeFile(f)
699 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()))))
701 SourceLocation loc { compat::getBeginLoc(decl) };
702 SourceLocation l { compiler.getSourceManager().getExpansionLoc(
703 loc) };
704 SourceLocation end { compiler.getSourceManager().getExpansionLoc(
705 decl->getNameInfo().getLoc()) };
706 assert(l.isFileID() && end.isFileID());
707 if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
708 while (l != end) {
709 unsigned n = Lexer::MeasureTokenLength(
710 l, compiler.getSourceManager(), compiler.getLangOpts());
711 std::string s {
712 compiler.getSourceManager().getCharacterData(l), n };
713 if (s == "sal_Bool") {
714 loc = l;
715 break;
717 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
720 // Only rewrite declarations in include files if a definition is
721 // also seen, to avoid compilation of a definition (in a main file
722 // only processed later) to fail with a "mismatch" error before the
723 // rewriter had a chance to act upon the definition (but use the
724 // heuristic of assuming pure virtual functions do not have
725 // definitions):
726 if (!((compiler.getSourceManager().isInMainFile(
727 compiler.getSourceManager().getSpellingLoc(
728 decl->getNameInfo().getLoc()))
729 || f->isDefined() || f->isPure())
730 && rewrite(loc)))
732 report(
733 DiagnosticsEngine::Warning,
734 "use \"bool\" instead of \"sal_Bool\" as return type%0",
735 loc)
736 << (k == OverrideKind::MAYBE
737 ? (" (unless this member function overrides a dependent"
738 " base member function, even though it is not marked"
739 " 'override')")
740 : "")
741 << decl->getSourceRange();
745 return true;
748 bool SalBool::VisitValueDecl(ValueDecl const * decl) {
749 if (ignoreLocation(decl)) {
750 return true;
752 if (isSalBool(decl->getType()) && !rewrite(compat::getBeginLoc(decl))) {
753 report(
754 DiagnosticsEngine::Warning,
755 "ValueDecl, use \"bool\" instead of \"sal_Bool\"",
756 compat::getBeginLoc(decl))
757 << decl->getSourceRange();
759 return true;
762 bool SalBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
763 // Ignore special code like
765 // static_cast<sal_Bool>(true) == sal_True
767 // inside static_assert in cppu/source/uno/check.cxx:
768 return
769 loplugin::isSamePathname(
770 getFileNameOfSpellingLoc(decl->getLocation()),
771 SRCDIR "/cppu/source/uno/check.cxx")
772 || RecursiveASTVisitor::TraverseStaticAssertDecl(decl);
775 bool SalBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
776 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
777 ++externCContexts_;
778 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
779 assert(externCContexts_ != 0);
780 --externCContexts_;
781 return ret;
784 bool SalBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
785 return !compiler.getSourceManager().isInMainFile(spellingLocation)
786 && (StringRef(
787 compiler.getSourceManager().getPresumedLoc(spellingLocation)
788 .getFilename())
789 .endswith(".h"));
792 bool SalBool::isSharedCAndCppCode(SourceLocation location) const {
793 // Assume that code is intended to be shared between C and C++ if it comes
794 // from an include file ending in .h, and is either in an extern "C" context
795 // or the body of a macro definition:
796 return
797 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
798 && (externCContexts_ != 0
799 || compiler.getSourceManager().isMacroBodyExpansion(location));
802 bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
803 if (!compiler.getSourceManager().isInMainFile(spellingLocation)) {
804 return false;
806 auto f = getFileNameOfSpellingLoc(spellingLocation);
807 return loplugin::isSamePathname(f, SRCDIR "/cppu/qa/test_any.cxx")
808 || loplugin::isSamePathname(f, SRCDIR "/cppu/source/uno/check.cxx");
809 // TODO: the offset checks
812 bool SalBool::rewrite(SourceLocation location) {
813 if (rewriter != nullptr) {
814 //TODO: "::sal_Bool" -> "bool", not "::bool"
815 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
816 location) };
817 unsigned n = Lexer::MeasureTokenLength(
818 loc, compiler.getSourceManager(), compiler.getLangOpts());
819 if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
820 == "sal_Bool")
822 return replaceText(loc, n, "bool");
825 return false;
828 loplugin::Plugin::Registration<SalBool> X("salbool", true);
832 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */