tdf#130857 qt weld: Support "Insert Breaks" dialog
[LibreOffice.git] / compilerplugins / clang / fakebool.cxx
blob274b9ef3d7290ee813df2388103b4212a69d062a
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 <map>
14 #include <string>
16 #include "clang/AST/Attr.h"
17 #include "clang/Basic/Builtins.h"
19 #include "config_clang.h"
21 #include "check.hxx"
22 #include "compat.hxx"
23 #include "functionaddress.hxx"
24 #include "plugin.hxx"
26 namespace {
28 enum FakeBoolKind {
29 FBK_No,
30 FBK_BOOL, FBK_First = FBK_BOOL,
31 FBK_Boolean, FBK_FT_Bool, FBK_FcBool, FBK_GLboolean, FBK_NPBool, FBK_TW_BOOL, FBK_UBool,
32 FBK_boolean, FBK_dbus_bool_t, FBK_gboolean, FBK_hb_boot_t, FBK_jboolean, FBK_my_bool,
33 FBK_sal_Bool,
34 FBK_End };
35 // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
37 StringRef getName(FakeBoolKind k) {
38 static constexpr llvm::StringLiteral names[] = {
39 "BOOL", "Boolean", "FT_Bool", "FcBool", "GLboolean", "NPBool", "TW_BOOL", "UBool",
40 "boolean", "dbus_bool_t", "gboolean", "hb_boot_t", "jboolean", "my_bool", "sal_Bool"};
41 assert(k >= FBK_First && k < FBK_End);
42 return names[k - FBK_First];
45 FakeBoolKind isFakeBool(QualType type) {
46 TypedefType const * t = type->getAs<TypedefType>();
47 if (t != nullptr) {
48 auto const name = t->getDecl()->getName();
49 for (int i = FBK_First; i != FBK_End; ++i) {
50 auto const k = FakeBoolKind(i);
51 if (name == getName(k)) {
52 return k;
56 return FBK_No;
59 FakeBoolKind isFakeBoolArray(QualType type) {
60 auto t = type->getAsArrayTypeUnsafe();
61 if (t == nullptr) {
62 return FBK_No;
64 auto const k = isFakeBool(t->getElementType());
65 if (k != FBK_No) {
66 return k;
68 return isFakeBoolArray(t->getElementType());
71 // It appears that, given a function declaration, there is no way to determine
72 // the language linkage of the function's type, only of the function's name
73 // (via FunctionDecl::isExternC); however, in a case like
75 // extern "C" { static void f(); }
77 // the function's name does not have C language linkage while the function's
78 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
79 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
80 // "Language linkage of function type":
81 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
82 assert(decl != nullptr);
83 if (decl->isExternC()) {
84 return true;
86 if (decl->isInExternCContext()) {
87 return true;
89 return false;
92 enum class OverrideKind { NO, YES, MAYBE };
94 OverrideKind getOverrideKind(FunctionDecl const * decl) {
95 CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
96 if (m == nullptr) {
97 return OverrideKind::NO;
99 if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
100 return OverrideKind::YES;
102 if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
103 return OverrideKind::NO;
105 return OverrideKind::MAYBE;
108 enum class BoolOverloadKind { No, Yes, CheckNext };
110 BoolOverloadKind isBoolOverloadOf(
111 FunctionDecl const * f, FunctionDecl const * decl, bool mustBeDeleted)
113 if (!mustBeDeleted || f->isDeleted()) {
114 unsigned n = decl->getNumParams();
115 if (f->getNumParams() == n) {
116 bool hasFB = false;
117 for (unsigned i = 0; i != n; ++i) {
118 QualType t1 { decl->getParamDecl(i)->getType() };
119 bool isFB = isFakeBool(t1) != FBK_No;
120 bool isFBRef = !isFB && t1->isReferenceType()
121 && isFakeBool(t1.getNonReferenceType()) != FBK_No;
122 QualType t2 { f->getParamDecl(i)->getType() };
123 if (!(isFB
124 ? t2->isBooleanType()
125 : isFBRef
126 ? (t2->isReferenceType()
127 && t2.getNonReferenceType()->isBooleanType())
128 : t2.getCanonicalType() == t1.getCanonicalType()))
130 return BoolOverloadKind::CheckNext;
132 hasFB |= isFB || isFBRef;
134 return hasFB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
135 // cheaply protect against the case where decl would have no
136 // fake bool parameters at all and would match itself
139 return BoolOverloadKind::CheckNext;
142 //TODO: current implementation is not at all general, just tests what we
143 // encounter in practice:
144 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
145 auto ctx = decl->getDeclContext();
146 if (!ctx->isLookupContext()) {
147 return false;
149 auto res = ctx->lookup(decl->getDeclName());
150 for (auto d = res.begin(); d != res.end(); ++d) {
151 if (auto f = dyn_cast<FunctionDecl>(*d)) {
152 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
153 case BoolOverloadKind::No:
154 return false;
155 case BoolOverloadKind::Yes:
156 return true;
157 case BoolOverloadKind::CheckNext:
158 break;
160 } else if (auto ftd = dyn_cast<FunctionTemplateDecl>(*d)) {
161 for (auto f: ftd->specializations()) {
162 if (f->getTemplateSpecializationKind()
163 == TSK_ExplicitSpecialization)
165 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
166 case BoolOverloadKind::No:
167 return false;
168 case BoolOverloadKind::Yes:
169 return true;
170 case BoolOverloadKind::CheckNext:
171 break;
177 return false;
180 class FakeBool:
181 public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<FakeBool>>
183 public:
184 explicit FakeBool(loplugin::InstantiationData const & data):
185 FunctionAddress(data) {}
187 virtual void run() override;
189 bool VisitUnaryOperator(UnaryOperator * op);
191 bool VisitCallExpr(CallExpr * expr);
193 bool VisitCStyleCastExpr(CStyleCastExpr * expr);
195 bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
197 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
199 bool VisitImplicitCastExpr(ImplicitCastExpr * expr);
201 bool VisitReturnStmt(ReturnStmt const * stmt);
203 bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
204 bool VisitParmVarDecl(ParmVarDecl const * decl);
206 bool WalkUpFromVarDecl(VarDecl const * decl);
207 bool VisitVarDecl(VarDecl const * decl);
209 bool WalkUpFromFieldDecl(FieldDecl const * decl);
210 bool VisitFieldDecl(FieldDecl const * decl);
212 bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
213 bool VisitFunctionDecl(FunctionDecl const * decl);
215 bool VisitValueDecl(ValueDecl const * decl);
217 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
219 private:
220 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
222 bool isSharedCAndCppCode(SourceLocation location) const;
224 bool rewrite(SourceLocation location, FakeBoolKind kind);
226 std::map<VarDecl const *, FakeBoolKind> varDecls_;
227 std::map<FieldDecl const *, FakeBoolKind> fieldDecls_;
228 std::map<ParmVarDecl const *, FakeBoolKind> parmVarDecls_;
229 std::map<FunctionDecl const *, FakeBoolKind> functionDecls_;
230 unsigned int externCContexts_ = 0;
233 void FakeBool::run() {
234 if (compiler.getLangOpts().CPlusPlus) {
235 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
236 for (auto const & dcl: varDecls_) {
237 auto const decl = dcl.first; auto const fbk = dcl.second;
238 SourceLocation loc { decl->getBeginLoc() };
239 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
240 if (tsi != nullptr) {
241 SourceLocation l {
242 compiler.getSourceManager().getExpansionLoc(
243 tsi->getTypeLoc().getBeginLoc()) };
244 SourceLocation end {
245 compiler.getSourceManager().getExpansionLoc(
246 tsi->getTypeLoc().getEndLoc()) };
247 assert(l.isFileID() && end.isFileID());
248 if (l == end
249 || compiler.getSourceManager().isBeforeInTranslationUnit(
250 l, end))
252 for (;;) {
253 unsigned n = Lexer::MeasureTokenLength(
254 l, compiler.getSourceManager(),
255 compiler.getLangOpts());
256 std::string s {
257 compiler.getSourceManager().getCharacterData(l),
258 n };
259 if (s == getName(fbk)) {
260 loc = l;
261 break;
263 if (l == end) {
264 break;
266 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
270 if (!(suppressWarningAt(loc) || rewrite(loc, fbk))) {
271 report(
272 DiagnosticsEngine::Warning,
273 "VarDecl, use \"bool\" instead of %0", loc)
274 << decl->getType().getLocalUnqualifiedType()
275 << decl->getSourceRange();
278 for (auto const & dcl: fieldDecls_) {
279 auto const decl = dcl.first; auto const fbk = dcl.second;
280 SourceLocation loc { decl->getBeginLoc() };
281 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
282 if (tsi != nullptr) {
283 SourceLocation l {
284 compiler.getSourceManager().getExpansionLoc(
285 tsi->getTypeLoc().getBeginLoc()) };
286 SourceLocation end {
287 compiler.getSourceManager().getExpansionLoc(
288 tsi->getTypeLoc().getEndLoc()) };
289 assert(l.isFileID() && end.isFileID());
290 if (l == end
291 || compiler.getSourceManager().isBeforeInTranslationUnit(
292 l, end))
294 for (;;) {
295 unsigned n = Lexer::MeasureTokenLength(
296 l, compiler.getSourceManager(),
297 compiler.getLangOpts());
298 std::string s {
299 compiler.getSourceManager().getCharacterData(l),
300 n };
301 if (s == getName(fbk)) {
302 loc = l;
303 break;
305 if (l == end) {
306 break;
308 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
312 if (!(suppressWarningAt(loc) || rewrite(loc, fbk))) {
313 report(
314 DiagnosticsEngine::Warning,
315 "FieldDecl, use \"bool\" instead of %0", loc)
316 << decl->getType().getLocalUnqualifiedType() << decl->getSourceRange();
319 auto const ignoredFns = getFunctionsWithAddressTaken();
320 for (auto const & dcl: parmVarDecls_) {
321 auto const decl = dcl.first; auto const fbk = dcl.second;
322 FunctionDecl const * f = cast<FunctionDecl>(decl->getDeclContext())->getCanonicalDecl();
323 if (ignoredFns.find(f) != ignoredFns.end()) {
324 continue;
326 SourceLocation loc { decl->getBeginLoc() };
327 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
328 if (tsi != nullptr) {
329 SourceLocation l {
330 compiler.getSourceManager().getExpansionLoc(
331 tsi->getTypeLoc().getBeginLoc()) };
332 SourceLocation end {
333 compiler.getSourceManager().getExpansionLoc(
334 tsi->getTypeLoc().getEndLoc()) };
335 assert(l.isFileID() && end.isFileID());
336 if (l == end
337 || (compiler.getSourceManager()
338 .isBeforeInTranslationUnit(l, end)))
340 for (;;) {
341 unsigned n = Lexer::MeasureTokenLength(
342 l, compiler.getSourceManager(),
343 compiler.getLangOpts());
344 std::string s {
345 compiler.getSourceManager().getCharacterData(l),
346 n };
347 if (s == getName(fbk)) {
348 loc = l;
349 break;
351 if (l == end) {
352 break;
354 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
358 // Only rewrite declarations in include files if a
359 // definition is also seen, to avoid compilation of a
360 // definition (in a main file only processed later) to fail
361 // with a "mismatch" error before the rewriter had a chance
362 // to act upon the definition (but use the heuristic of
363 // assuming pure virtual functions do not have definitions);
364 // also, do not automatically rewrite functions that could
365 // implicitly override depend base functions (and thus stop
366 // doing so after the rewrite; note that this is less
367 // dangerous for return types than for parameter types,
368 // where the function would still implicitly override and
369 // cause a compilation error due to the incompatible return
370 // type):
371 OverrideKind k = getOverrideKind(f);
372 if (!((compiler.getSourceManager().isInMainFile(
373 compiler.getSourceManager().getSpellingLoc(
374 dyn_cast<FunctionDecl>(
375 decl->getDeclContext())
376 ->getNameInfo().getLoc()))
377 || f->isDefined() || compat::isPureVirtual(f))
378 && k != OverrideKind::MAYBE && rewrite(loc, fbk)))
380 report(
381 DiagnosticsEngine::Warning,
382 ("ParmVarDecl, use \"bool\" instead of"
383 " %0%1"),
384 loc)
385 << decl->getType().getNonReferenceType().getLocalUnqualifiedType()
386 << (k == OverrideKind::MAYBE
387 ? (" (unless this member function overrides a"
388 " dependent base member function, even"
389 " though it is not marked 'override')")
390 : "")
391 << decl->getSourceRange();
394 for (auto const & dcl: functionDecls_) {
395 auto const decl = dcl.first; auto const fbk = dcl.second;
396 FunctionDecl const * f = decl->getCanonicalDecl();
397 if (ignoredFns.find(f) != ignoredFns.end()) {
398 continue;
400 SourceLocation loc { decl->getBeginLoc() };
401 SourceLocation l { compiler.getSourceManager().getExpansionLoc(
402 loc) };
403 SourceLocation end { compiler.getSourceManager().getExpansionLoc(
404 decl->getNameInfo().getLoc()) };
405 assert(l.isFileID() && end.isFileID());
406 if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
407 while (l != end) {
408 unsigned n = Lexer::MeasureTokenLength(
409 l, compiler.getSourceManager(), compiler.getLangOpts());
410 std::string s {
411 compiler.getSourceManager().getCharacterData(l), n };
412 if (s == getName(fbk)) {
413 loc = l;
414 break;
416 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
419 // Only rewrite declarations in include files if a definition is
420 // also seen, to avoid compilation of a definition (in a main file
421 // only processed later) to fail with a "mismatch" error before the
422 // rewriter had a chance to act upon the definition (but use the
423 // heuristic of assuming pure virtual functions do not have
424 // definitions):
425 if (!((compiler.getSourceManager().isInMainFile(
426 compiler.getSourceManager().getSpellingLoc(
427 decl->getNameInfo().getLoc()))
428 || f->isDefined() || compat::isPureVirtual(f))
429 && rewrite(loc, fbk)))
431 report(
432 DiagnosticsEngine::Warning,
433 "use \"bool\" instead of %0 as return type%1",
434 loc)
435 << decl->getReturnType().getNonReferenceType().getLocalUnqualifiedType()
436 << (getOverrideKind(f) == OverrideKind::MAYBE
437 ? (" (unless this member function overrides a dependent"
438 " base member function, even though it is not marked"
439 " 'override')")
440 : "")
441 << decl->getSourceRange();
447 bool FakeBool::VisitUnaryOperator(UnaryOperator * op) {
448 if (op->getOpcode() != UO_AddrOf) {
449 return FunctionAddress::VisitUnaryOperator(op);
451 FunctionAddress::VisitUnaryOperator(op);
452 Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
453 if (isFakeBool(e1->getType()) != FBK_No) {
454 if (DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1)) {
455 if (auto const d = dyn_cast<VarDecl>(e2->getDecl())) {
456 varDecls_.erase(d);
457 } else if (auto const d = dyn_cast<FieldDecl>(e2->getDecl())) {
458 fieldDecls_.erase(d);
460 } else if (auto const e3 = dyn_cast<MemberExpr>(e1)) {
461 if (auto const d = dyn_cast<FieldDecl>(e3->getMemberDecl())) {
462 fieldDecls_.erase(d);
466 return true;
469 bool FakeBool::VisitCallExpr(CallExpr * expr) {
470 Decl const * d = expr->getCalleeDecl();
471 FunctionProtoType const * ft = nullptr;
472 if (d != nullptr) {
473 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
474 if (fd != nullptr) {
475 if (!hasBoolOverload(fd, false)) {
476 clang::PointerType const * pt = fd->getType()
477 ->getAs<clang::PointerType>();
478 QualType t2(
479 pt == nullptr ? fd->getType() : pt->getPointeeType());
480 ft = t2->getAs<FunctionProtoType>();
481 assert(
482 ft != nullptr || !compiler.getLangOpts().CPlusPlus
483 || (fd->getBuiltinID() != Builtin::NotBuiltin
484 && isa<FunctionNoProtoType>(t2)));
485 // __builtin_*s have no proto type?
487 } else {
488 VarDecl const * vd = dyn_cast<VarDecl>(d);
489 if (vd != nullptr) {
490 clang::PointerType const * pt = vd->getType()
491 ->getAs<clang::PointerType>();
492 ft = (pt == nullptr ? vd->getType() : pt->getPointeeType())
493 ->getAs<FunctionProtoType>();
497 if (ft != nullptr) {
498 for (unsigned i = 0; i != ft->getNumParams(); ++i) {
499 QualType t(ft->getParamType(i));
500 bool b = false;
501 if (t->isLValueReferenceType()) {
502 t = t.getNonReferenceType();
503 b = !t.isConstQualified() && isFakeBool(t) != FBK_No;
504 } else if (t->isPointerType()) {
505 for (;;) {
506 auto t2 = t->getAs<clang::PointerType>();
507 if (t2 == nullptr) {
508 break;
510 t = t2->getPointeeType();
512 b = isFakeBool(t) != FBK_No;
514 if (b && i < expr->getNumArgs()) {
515 auto const e1 = expr->getArg(i)->IgnoreParenImpCasts();
516 if (DeclRefExpr * ref = dyn_cast<DeclRefExpr>(e1)) {
517 VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
518 if (d != nullptr) {
519 varDecls_.erase(d);
521 } else if (auto const e2 = dyn_cast<MemberExpr>(e1)) {
522 if (auto const d = dyn_cast<FieldDecl>(e2->getMemberDecl())) {
523 fieldDecls_.erase(d);
529 return true;
532 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
533 if (ignoreLocation(expr)) {
534 return true;
536 auto const k = isFakeBool(expr->getType());
537 if (k != FBK_No) {
538 SourceLocation loc { expr->getBeginLoc() };
539 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
540 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
542 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
543 StringRef name { Lexer::getImmediateMacroName(
544 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
545 if (k == FBK_sal_Bool && (name == "sal_False" || name == "sal_True")) {
546 auto callLoc = compiler.getSourceManager()
547 .getImmediateMacroCallerLoc(loc);
548 if (!isSharedCAndCppCode(callLoc)) {
549 SourceLocation argLoc;
550 if (compiler.getSourceManager().isMacroArgExpansion(
551 expr->getBeginLoc(), &argLoc)
552 //TODO: check it's the complete (first) arg to the macro
553 && (Lexer::getImmediateMacroName(
554 argLoc, compiler.getSourceManager(),
555 compiler.getLangOpts())
556 == "CPPUNIT_ASSERT_EQUAL"))
558 // Ignore sal_False/True that are directly used as
559 // arguments to CPPUNIT_ASSERT_EQUAL:
560 return true;
562 if (suppressWarningAt(callLoc)) {
563 return true;
565 bool b = k == FBK_sal_Bool && name == "sal_True";
566 if (rewriter != nullptr) {
567 auto callSpellLoc = compiler.getSourceManager()
568 .getSpellingLoc(callLoc);
569 unsigned n = Lexer::MeasureTokenLength(
570 callSpellLoc, compiler.getSourceManager(),
571 compiler.getLangOpts());
572 if (StringRef(
573 compiler.getSourceManager().getCharacterData(
574 callSpellLoc),
576 == name)
578 return replaceText(
579 callSpellLoc, n, b ? "true" : "false");
582 report(
583 DiagnosticsEngine::Warning,
584 "use '%select{false|true}0' instead of '%1'", callLoc)
585 << b << name << expr->getSourceRange();
587 return true;
589 if (isSharedCAndCppCode(loc)) {
590 return true;
593 report(
594 DiagnosticsEngine::Warning,
595 "CStyleCastExpr, suspicious cast from %0 to %1",
596 expr->getBeginLoc())
597 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
598 << expr->getType() << expr->getSourceRange();
600 return true;
603 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
604 if (ignoreLocation(expr)) {
605 return true;
607 if (isFakeBool(expr->getType()) == FBK_No) {
608 return true;
610 if (suppressWarningAt(expr->getBeginLoc())) {
611 return true;
613 report(
614 DiagnosticsEngine::Warning,
615 "CXXStaticCastExpr, suspicious cast from %0 to %1",
616 expr->getBeginLoc())
617 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
618 << expr->getType() << expr->getSourceRange();
619 return true;
622 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
623 if (ignoreLocation(expr)) {
624 return true;
626 if (isFakeBool(expr->getType()) != FBK_No) {
627 report(
628 DiagnosticsEngine::Warning,
629 "CXXFunctionalCastExpr, suspicious cast from %0 to %1",
630 expr->getBeginLoc())
631 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
632 << expr->getType() << expr->getSourceRange();
634 return true;
637 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
638 FunctionAddress::VisitImplicitCastExpr(expr);
639 if (ignoreLocation(expr)) {
640 return true;
642 if (isFakeBool(expr->getType()) == FBK_No) {
643 return true;
645 auto l = expr->getBeginLoc();
646 while (compiler.getSourceManager().isMacroArgExpansion(l)) {
647 l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
649 if (compiler.getSourceManager().isMacroBodyExpansion(l) && isSharedCAndCppCode(l)) {
650 return true;
652 auto e1 = expr->getSubExprAsWritten();
653 auto t = e1->getType();
654 if (!t->isFundamentalType() || loplugin::TypeCheck(t).AnyBoolean()) {
655 return true;
657 auto e2 = dyn_cast<ConditionalOperator>(e1);
658 if (e2 != nullptr) {
659 auto ic1 = dyn_cast<ImplicitCastExpr>(
660 e2->getTrueExpr()->IgnoreParens());
661 auto ic2 = dyn_cast<ImplicitCastExpr>(
662 e2->getFalseExpr()->IgnoreParens());
663 if (ic1 != nullptr && ic2 != nullptr
664 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
665 && (loplugin::TypeCheck(ic1->getSubExprAsWritten()->getType())
666 .AnyBoolean())
667 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
668 && (loplugin::TypeCheck(ic2->getSubExprAsWritten()->getType())
669 .AnyBoolean()))
671 return true;
674 report(
675 DiagnosticsEngine::Warning, "conversion from %0 to %1",
676 expr->getBeginLoc())
677 << t << expr->getType() << expr->getSourceRange();
678 return true;
681 bool FakeBool::VisitReturnStmt(ReturnStmt const * stmt) {
682 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
683 // which has
685 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
687 // and
689 // return aCharClass[eCharClass];
691 if (ignoreLocation(stmt)) {
692 return true;
694 auto e = stmt->getRetValue();
695 if (e == nullptr) {
696 return true;
698 auto t = e->getType();
699 if (!t->isPointerType()) {
700 return true;
702 for (;;) {
703 auto t2 = t->getAs<clang::PointerType>();
704 if (t2 == nullptr) {
705 break;
707 t = t2->getPointeeType();
709 if (isFakeBool(t) != FBK_sal_Bool) {
710 return true;
712 auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
713 if (e2 == nullptr) {
714 return true;
716 auto e3 = dyn_cast<DeclRefExpr>(e2->getBase()->IgnoreParenImpCasts());
717 if (e3 == nullptr) {
718 return true;
720 auto d = dyn_cast<VarDecl>(e3->getDecl());
721 if (d == nullptr) {
722 return true;
724 varDecls_.erase(d);
725 return true;
728 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
729 return VisitParmVarDecl(decl);
732 bool FakeBool::VisitParmVarDecl(ParmVarDecl const * decl) {
733 if (ignoreLocation(decl)) {
734 return true;
736 auto const fbk = isFakeBool(decl->getType().getNonReferenceType());
737 if (fbk != FBK_No) {
738 FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
739 if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
740 f = f->getCanonicalDecl();
741 if (handler.isAllRelevantCodeDefined(f)
742 && !(hasCLanguageLinkageType(f)
743 || (fbk == FBK_sal_Bool && isInUnoIncludeFile(f)
744 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
745 || decl->getType()->isReferenceType()
746 || hasBoolOverload(f, false)))
747 || f->isDeleted() || hasBoolOverload(f, true)))
749 OverrideKind k = getOverrideKind(f);
750 if (k != OverrideKind::YES) {
751 parmVarDecls_.insert({decl, fbk});
756 return true;
759 bool FakeBool::WalkUpFromVarDecl(VarDecl const * decl) {
760 return VisitVarDecl(decl);
763 bool FakeBool::VisitVarDecl(VarDecl const * decl) {
764 if (ignoreLocation(decl)) {
765 return true;
767 if (decl->isExternC()) {
768 return true;
770 auto k = isFakeBool(decl->getType());
771 if (k == FBK_No) {
772 k = isFakeBoolArray(decl->getType());
774 if (k == FBK_No) {
775 return true;
777 auto l = decl->getBeginLoc();
778 while (compiler.getSourceManager().isMacroArgExpansion(l)) {
779 l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
781 if (compiler.getSourceManager().isMacroBodyExpansion(l)
782 && isSharedCAndCppCode(l))
784 return true;
786 varDecls_.insert({decl, k});
787 return true;
790 bool FakeBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
791 return VisitFieldDecl(decl);
794 bool FakeBool::VisitFieldDecl(FieldDecl const * decl) {
795 if (ignoreLocation(decl)) {
796 return true;
798 auto k = isFakeBool(decl->getType());
799 if (k == FBK_No) {
800 k = isFakeBoolArray(decl->getType());
802 if (k == FBK_No) {
803 return true;
805 if (!handler.isAllRelevantCodeDefined(decl)) {
806 return true;
808 TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
809 if (td == nullptr) {
810 //TODO: ObjCInterface
811 return true;
813 if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
814 || isInUnoIncludeFile(
815 compiler.getSourceManager().getSpellingLoc(
816 decl->getLocation()))))
818 fieldDecls_.insert({decl, k});
820 return true;
823 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
824 return VisitFunctionDecl(decl);
827 bool FakeBool::VisitFunctionDecl(FunctionDecl const * decl) {
828 if (ignoreLocation(decl)) {
829 return true;
831 auto const fbk = isFakeBool(decl->getReturnType().getNonReferenceType());
832 if (fbk != FBK_No
833 && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl))
834 && handler.isAllRelevantCodeDefined(decl))
836 FunctionDecl const * f = decl->getCanonicalDecl();
837 OverrideKind k = getOverrideKind(f);
838 if (k != OverrideKind::YES
839 && !(hasCLanguageLinkageType(f)
840 || (isInUnoIncludeFile(f)
841 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()))))
843 functionDecls_.insert({decl, fbk});
846 return true;
849 bool FakeBool::VisitValueDecl(ValueDecl const * decl) {
850 if (ignoreLocation(decl)) {
851 return true;
853 auto const k = isFakeBool(decl->getType());
854 if (k != FBK_No && !rewrite(decl->getBeginLoc(), k)) {
855 report(
856 DiagnosticsEngine::Warning,
857 "ValueDecl, use \"bool\" instead of %0",
858 decl->getBeginLoc())
859 << decl->getType() << decl->getSourceRange();
861 return true;
864 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
865 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
866 ++externCContexts_;
867 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
868 assert(externCContexts_ != 0);
869 --externCContexts_;
870 return ret;
873 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
874 return !compiler.getSourceManager().isInMainFile(spellingLocation)
875 && compat::ends_with(
876 StringRef(compiler.getSourceManager().getPresumedLoc(spellingLocation).getFilename()),
877 ".h");
880 bool FakeBool::isSharedCAndCppCode(SourceLocation location) const {
881 // Assume that code is intended to be shared between C and C++ if it comes
882 // from an include file ending in .h, and is either in an extern "C" context
883 // or the body of a macro definition:
884 return
885 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
886 && (externCContexts_ != 0
887 || compiler.getSourceManager().isMacroBodyExpansion(location));
890 bool FakeBool::rewrite(SourceLocation location, FakeBoolKind kind) {
891 if (rewriter != nullptr) {
892 //TODO: "::sal_Bool" -> "bool", not "::bool"
893 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
894 location) };
895 unsigned n = Lexer::MeasureTokenLength(
896 loc, compiler.getSourceManager(), compiler.getLangOpts());
897 if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
898 == getName(kind))
900 return replaceText(loc, n, "bool");
903 return false;
906 loplugin::Plugin::Registration<FakeBool> X("fakebool", true);
910 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */