bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / fakebool.cxx
blob1dbb535ceef91babf2b155a7055d718cb4fd4305
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"
18 #include "check.hxx"
19 #include "compat.hxx"
20 #include "functionaddress.hxx"
21 #include "plugin.hxx"
23 namespace {
25 // BEGIN code copied from LLVM's clang/lib/Sema/Sema.cpp
27 typedef llvm::DenseMap<const CXXRecordDecl*, bool> RecordCompleteMap;
29 /// Returns true, if all methods and nested classes of the given
30 /// CXXRecordDecl are defined in this translation unit.
31 ///
32 /// Should only be called from ActOnEndOfTranslationUnit so that all
33 /// definitions are actually read.
34 static bool MethodsAndNestedClassesComplete(const CXXRecordDecl *RD,
35 RecordCompleteMap &MNCComplete) {
36 RecordCompleteMap::iterator Cache = MNCComplete.find(RD);
37 if (Cache != MNCComplete.end())
38 return Cache->second;
39 if (!RD->isCompleteDefinition())
40 return false;
41 bool Complete = true;
42 for (DeclContext::decl_iterator I = RD->decls_begin(),
43 E = RD->decls_end();
44 I != E && Complete; ++I) {
45 if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I))
46 Complete = M->isDefined() || M->isDefaulted() ||
47 (M->isPure() && !isa<CXXDestructorDecl>(M));
48 else if (const FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(*I))
49 // If the template function is marked as late template parsed at this
50 // point, it has not been instantiated and therefore we have not
51 // performed semantic analysis on it yet, so we cannot know if the type
52 // can be considered complete.
53 Complete = !F->getTemplatedDecl()->isLateTemplateParsed() &&
54 F->getTemplatedDecl()->isDefined();
55 else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) {
56 if (R->isInjectedClassName())
57 continue;
58 if (R->hasDefinition())
59 Complete = MethodsAndNestedClassesComplete(R->getDefinition(),
60 MNCComplete);
61 else
62 Complete = false;
65 MNCComplete[RD] = Complete;
66 return Complete;
69 /// Returns true, if the given CXXRecordDecl is fully defined in this
70 /// translation unit, i.e. all methods are defined or pure virtual and all
71 /// friends, friend functions and nested classes are fully defined in this
72 /// translation unit.
73 ///
74 /// Should only be called from ActOnEndOfTranslationUnit so that all
75 /// definitions are actually read.
76 static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
77 RecordCompleteMap &RecordsComplete,
78 RecordCompleteMap &MNCComplete) {
79 RecordCompleteMap::iterator Cache = RecordsComplete.find(RD);
80 if (Cache != RecordsComplete.end())
81 return Cache->second;
82 bool Complete = MethodsAndNestedClassesComplete(RD, MNCComplete);
83 for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
84 E = RD->friend_end();
85 I != E && Complete; ++I) {
86 // Check if friend classes and methods are complete.
87 if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
88 // Friend classes are available as the TypeSourceInfo of the FriendDecl.
89 if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
90 Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
91 else
92 Complete = false;
93 } else {
94 // Friend functions are available through the NamedDecl of FriendDecl.
95 if (const FunctionDecl *FD =
96 dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
97 Complete = FD->isDefined();
98 else
99 // This is a template friend, give up.
100 Complete = false;
103 RecordsComplete[RD] = Complete;
104 return Complete;
107 RecordCompleteMap RecordsComplete;
108 RecordCompleteMap MNCComplete;
110 // END code copied from LLVM's clang/lib/Sema/Sema.cpp
112 // Is all code that could see `decl` defined in this TU?
113 bool isAllRelevantCodeDefined(NamedDecl const * decl) {
114 switch (decl->getAccess()) {
115 case AS_protected:
116 if (!cast<CXXRecordDecl>(decl->getDeclContext())->hasAttr<FinalAttr>()) {
117 break;
119 LLVM_FALLTHROUGH;
120 case AS_private:
121 if (IsRecordFullyDefined(
122 cast<CXXRecordDecl>(decl->getDeclContext()), RecordsComplete, MNCComplete))
124 return true;
126 break;
127 default:
128 break;
130 return !decl->isExternallyVisible();
133 enum FakeBoolKind {
134 FBK_No,
135 FBK_BOOL, FBK_First = FBK_BOOL,
136 FBK_Boolean, FBK_FT_Bool, FBK_FcBool, FBK_GLboolean, FBK_NPBool, FBK_TW_BOOL, FBK_UBool,
137 FBK_boolean, FBK_dbus_bool_t, FBK_gboolean, FBK_hb_boot_t, FBK_jboolean, FBK_my_bool,
138 FBK_sal_Bool,
139 FBK_End };
140 // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
142 StringRef getName(FakeBoolKind k) {
143 static constexpr llvm::StringLiteral names[] = {
144 "BOOL", "Boolean", "FT_Bool", "FcBool", "GLboolean", "NPBool", "TW_BOOL", "UBool",
145 "boolean", "dbus_bool_t", "gboolean", "hb_boot_t", "jboolean", "my_bool", "sal_Bool"};
146 assert(k >= FBK_First && k < FBK_End);
147 return names[k - FBK_First];
150 FakeBoolKind isFakeBool(QualType type) {
151 TypedefType const * t = type->getAs<TypedefType>();
152 if (t != nullptr) {
153 auto const name = t->getDecl()->getName();
154 for (int i = FBK_First; i != FBK_End; ++i) {
155 auto const k = FakeBoolKind(i);
156 if (name == getName(k)) {
157 return k;
161 return FBK_No;
164 FakeBoolKind isFakeBoolArray(QualType type) {
165 auto t = type->getAsArrayTypeUnsafe();
166 if (t == nullptr) {
167 return FBK_No;
169 auto const k = isFakeBool(t->getElementType());
170 if (k != FBK_No) {
171 return k;
173 return isFakeBoolArray(t->getElementType());
176 // It appears that, given a function declaration, there is no way to determine
177 // the language linkage of the function's type, only of the function's name
178 // (via FunctionDecl::isExternC); however, in a case like
180 // extern "C" { static void f(); }
182 // the function's name does not have C language linkage while the function's
183 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
184 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
185 // "Language linkage of function type":
186 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
187 assert(decl != nullptr);
188 if (decl->isExternC()) {
189 return true;
191 if (decl->isInExternCContext()) {
192 return true;
194 return false;
197 enum class OverrideKind { NO, YES, MAYBE };
199 OverrideKind getOverrideKind(FunctionDecl const * decl) {
200 CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
201 if (m == nullptr) {
202 return OverrideKind::NO;
204 if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
205 return OverrideKind::YES;
207 if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
208 return OverrideKind::NO;
210 return OverrideKind::MAYBE;
213 enum class BoolOverloadKind { No, Yes, CheckNext };
215 BoolOverloadKind isBoolOverloadOf(
216 FunctionDecl const * f, FunctionDecl const * decl, bool mustBeDeleted)
218 if (!mustBeDeleted || f->isDeleted()) {
219 unsigned n = decl->getNumParams();
220 if (f->getNumParams() == n) {
221 bool hasFB = false;
222 for (unsigned i = 0; i != n; ++i) {
223 QualType t1 { decl->getParamDecl(i)->getType() };
224 bool isFB = isFakeBool(t1) != FBK_No;
225 bool isFBRef = !isFB && t1->isReferenceType()
226 && isFakeBool(t1.getNonReferenceType()) != FBK_No;
227 QualType t2 { f->getParamDecl(i)->getType() };
228 if (!(isFB
229 ? t2->isBooleanType()
230 : isFBRef
231 ? (t2->isReferenceType()
232 && t2.getNonReferenceType()->isBooleanType())
233 : t2.getCanonicalType() == t1.getCanonicalType()))
235 return BoolOverloadKind::CheckNext;
237 hasFB |= isFB || isFBRef;
239 return hasFB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
240 // cheaply protect against the case where decl would have no
241 // fake bool parameters at all and would match itself
244 return BoolOverloadKind::CheckNext;
247 //TODO: current implementation is not at all general, just tests what we
248 // encounter in practice:
249 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
250 auto ctx = decl->getDeclContext();
251 if (!ctx->isLookupContext()) {
252 return false;
254 auto res = ctx->lookup(decl->getDeclName());
255 for (auto d = res.begin(); d != res.end(); ++d) {
256 if (auto f = dyn_cast<FunctionDecl>(*d)) {
257 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
258 case BoolOverloadKind::No:
259 return false;
260 case BoolOverloadKind::Yes:
261 return true;
262 case BoolOverloadKind::CheckNext:
263 break;
265 } else if (auto ftd = dyn_cast<FunctionTemplateDecl>(*d)) {
266 for (auto f: ftd->specializations()) {
267 if (f->getTemplateSpecializationKind()
268 == TSK_ExplicitSpecialization)
270 switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
271 case BoolOverloadKind::No:
272 return false;
273 case BoolOverloadKind::Yes:
274 return true;
275 case BoolOverloadKind::CheckNext:
276 break;
282 return false;
285 class FakeBool:
286 public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<FakeBool>>
288 public:
289 explicit FakeBool(loplugin::InstantiationData const & data):
290 FunctionAddress(data) {}
292 virtual void run() override;
294 bool VisitUnaryAddrOf(UnaryOperator const * op);
296 bool VisitCallExpr(CallExpr * expr);
298 bool VisitCStyleCastExpr(CStyleCastExpr * expr);
300 bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
302 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
304 bool VisitImplicitCastExpr(ImplicitCastExpr * expr);
306 bool VisitReturnStmt(ReturnStmt const * stmt);
308 bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
309 bool VisitParmVarDecl(ParmVarDecl const * decl);
311 bool WalkUpFromVarDecl(VarDecl const * decl);
312 bool VisitVarDecl(VarDecl const * decl);
314 bool WalkUpFromFieldDecl(FieldDecl const * decl);
315 bool VisitFieldDecl(FieldDecl const * decl);
317 bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
318 bool VisitFunctionDecl(FunctionDecl const * decl);
320 bool VisitValueDecl(ValueDecl const * decl);
322 bool TraverseStaticAssertDecl(StaticAssertDecl * decl);
324 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
326 private:
327 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
329 bool isSharedCAndCppCode(SourceLocation location) const;
331 bool isInSpecialMainFile(SourceLocation spellingLocation) const;
333 bool rewrite(SourceLocation location, FakeBoolKind kind);
335 std::map<VarDecl const *, FakeBoolKind> varDecls_;
336 std::map<FieldDecl const *, FakeBoolKind> fieldDecls_;
337 std::map<ParmVarDecl const *, FakeBoolKind> parmVarDecls_;
338 std::map<FunctionDecl const *, FakeBoolKind> functionDecls_;
339 unsigned int externCContexts_ = 0;
342 void FakeBool::run() {
343 if (compiler.getLangOpts().CPlusPlus) {
344 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
345 for (auto const dcl: varDecls_) {
346 auto const decl = dcl.first; auto const fbk = dcl.second;
347 SourceLocation loc { compat::getBeginLoc(decl) };
348 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
349 if (tsi != nullptr) {
350 SourceLocation l {
351 compiler.getSourceManager().getExpansionLoc(
352 tsi->getTypeLoc().getBeginLoc()) };
353 SourceLocation end {
354 compiler.getSourceManager().getExpansionLoc(
355 tsi->getTypeLoc().getEndLoc()) };
356 assert(l.isFileID() && end.isFileID());
357 if (l == end
358 || compiler.getSourceManager().isBeforeInTranslationUnit(
359 l, end))
361 for (;;) {
362 unsigned n = Lexer::MeasureTokenLength(
363 l, compiler.getSourceManager(),
364 compiler.getLangOpts());
365 std::string s {
366 compiler.getSourceManager().getCharacterData(l),
367 n };
368 if (s == getName(fbk)) {
369 loc = l;
370 break;
372 if (l == end) {
373 break;
375 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
379 if (!rewrite(loc, fbk)) {
380 report(
381 DiagnosticsEngine::Warning,
382 "VarDecl, use \"bool\" instead of %0", loc)
383 << decl->getType().getLocalUnqualifiedType()
384 << decl->getSourceRange();
387 for (auto const dcl: fieldDecls_) {
388 auto const decl = dcl.first; auto const fbk = dcl.second;
389 SourceLocation loc { compat::getBeginLoc(decl) };
390 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
391 if (tsi != nullptr) {
392 SourceLocation l {
393 compiler.getSourceManager().getExpansionLoc(
394 tsi->getTypeLoc().getBeginLoc()) };
395 SourceLocation end {
396 compiler.getSourceManager().getExpansionLoc(
397 tsi->getTypeLoc().getEndLoc()) };
398 assert(l.isFileID() && end.isFileID());
399 if (l == end
400 || compiler.getSourceManager().isBeforeInTranslationUnit(
401 l, end))
403 for (;;) {
404 unsigned n = Lexer::MeasureTokenLength(
405 l, compiler.getSourceManager(),
406 compiler.getLangOpts());
407 std::string s {
408 compiler.getSourceManager().getCharacterData(l),
409 n };
410 if (s == getName(fbk)) {
411 loc = l;
412 break;
414 if (l == end) {
415 break;
417 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
421 if (!rewrite(loc, fbk)) {
422 report(
423 DiagnosticsEngine::Warning,
424 "FieldDecl, use \"bool\" instead of %0", loc)
425 << decl->getType().getLocalUnqualifiedType() << decl->getSourceRange();
428 auto const ignoredFns = getFunctionsWithAddressTaken();
429 for (auto const dcl: parmVarDecls_) {
430 auto const decl = dcl.first; auto const fbk = dcl.second;
431 FunctionDecl const * f = cast<FunctionDecl>(decl->getDeclContext())->getCanonicalDecl();
432 if (ignoredFns.find(f) != ignoredFns.end()) {
433 continue;
435 SourceLocation loc { compat::getBeginLoc(decl) };
436 TypeSourceInfo * tsi = decl->getTypeSourceInfo();
437 if (tsi != nullptr) {
438 SourceLocation l {
439 compiler.getSourceManager().getExpansionLoc(
440 tsi->getTypeLoc().getBeginLoc()) };
441 SourceLocation end {
442 compiler.getSourceManager().getExpansionLoc(
443 tsi->getTypeLoc().getEndLoc()) };
444 assert(l.isFileID() && end.isFileID());
445 if (l == end
446 || (compiler.getSourceManager()
447 .isBeforeInTranslationUnit(l, end)))
449 for (;;) {
450 unsigned n = Lexer::MeasureTokenLength(
451 l, compiler.getSourceManager(),
452 compiler.getLangOpts());
453 std::string s {
454 compiler.getSourceManager().getCharacterData(l),
455 n };
456 if (s == getName(fbk)) {
457 loc = l;
458 break;
460 if (l == end) {
461 break;
463 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
467 // Only rewrite declarations in include files if a
468 // definition is also seen, to avoid compilation of a
469 // definition (in a main file only processed later) to fail
470 // with a "mismatch" error before the rewriter had a chance
471 // to act upon the definition (but use the heuristic of
472 // assuming pure virtual functions do not have definitions);
473 // also, do not automatically rewrite functions that could
474 // implicitly override depend base functions (and thus stop
475 // doing so after the rewrite; note that this is less
476 // dangerous for return types than for parameter types,
477 // where the function would still implicitly override and
478 // cause a compilation error due to the incompatible return
479 // type):
480 OverrideKind k = getOverrideKind(f);
481 if (!((compiler.getSourceManager().isInMainFile(
482 compiler.getSourceManager().getSpellingLoc(
483 dyn_cast<FunctionDecl>(
484 decl->getDeclContext())
485 ->getNameInfo().getLoc()))
486 || f->isDefined() || f->isPure())
487 && k != OverrideKind::MAYBE && rewrite(loc, fbk)))
489 report(
490 DiagnosticsEngine::Warning,
491 ("ParmVarDecl, use \"bool\" instead of"
492 " %0%1"),
493 loc)
494 << decl->getType().getNonReferenceType().getLocalUnqualifiedType()
495 << (k == OverrideKind::MAYBE
496 ? (" (unless this member function overrides a"
497 " dependent base member function, even"
498 " though it is not marked 'override')")
499 : "")
500 << decl->getSourceRange();
503 for (auto const dcl: functionDecls_) {
504 auto const decl = dcl.first; auto const fbk = dcl.second;
505 FunctionDecl const * f = decl->getCanonicalDecl();
506 if (ignoredFns.find(f) != ignoredFns.end()) {
507 continue;
509 SourceLocation loc { compat::getBeginLoc(decl) };
510 SourceLocation l { compiler.getSourceManager().getExpansionLoc(
511 loc) };
512 SourceLocation end { compiler.getSourceManager().getExpansionLoc(
513 decl->getNameInfo().getLoc()) };
514 assert(l.isFileID() && end.isFileID());
515 if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
516 while (l != end) {
517 unsigned n = Lexer::MeasureTokenLength(
518 l, compiler.getSourceManager(), compiler.getLangOpts());
519 std::string s {
520 compiler.getSourceManager().getCharacterData(l), n };
521 if (s == getName(fbk)) {
522 loc = l;
523 break;
525 l = l.getLocWithOffset(std::max<unsigned>(n, 1));
528 // Only rewrite declarations in include files if a definition is
529 // also seen, to avoid compilation of a definition (in a main file
530 // only processed later) to fail with a "mismatch" error before the
531 // rewriter had a chance to act upon the definition (but use the
532 // heuristic of assuming pure virtual functions do not have
533 // definitions):
534 if (!((compiler.getSourceManager().isInMainFile(
535 compiler.getSourceManager().getSpellingLoc(
536 decl->getNameInfo().getLoc()))
537 || f->isDefined() || f->isPure())
538 && rewrite(loc, fbk)))
540 report(
541 DiagnosticsEngine::Warning,
542 "use \"bool\" instead of %0 as return type%1",
543 loc)
544 << decl->getReturnType().getNonReferenceType().getLocalUnqualifiedType()
545 << (getOverrideKind(f) == OverrideKind::MAYBE
546 ? (" (unless this member function overrides a dependent"
547 " base member function, even though it is not marked"
548 " 'override')")
549 : "")
550 << decl->getSourceRange();
556 bool FakeBool::VisitUnaryAddrOf(UnaryOperator const * op) {
557 FunctionAddress::VisitUnaryAddrOf(op);
558 Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
559 if (isFakeBool(e1->getType()) != FBK_No) {
560 if (DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1)) {
561 VarDecl const * d = dyn_cast<VarDecl>(e2->getDecl());
562 if (d != nullptr) {
563 varDecls_.erase(d);
565 } else if (auto const e3 = dyn_cast<MemberExpr>(e1)) {
566 if (auto const d = dyn_cast<FieldDecl>(e3->getMemberDecl())) {
567 fieldDecls_.erase(d);
571 return true;
574 bool FakeBool::VisitCallExpr(CallExpr * expr) {
575 Decl const * d = expr->getCalleeDecl();
576 FunctionProtoType const * ft = nullptr;
577 if (d != nullptr) {
578 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
579 if (fd != nullptr) {
580 if (!hasBoolOverload(fd, false)) {
581 clang::PointerType const * pt = fd->getType()
582 ->getAs<clang::PointerType>();
583 QualType t2(
584 pt == nullptr ? fd->getType() : pt->getPointeeType());
585 ft = t2->getAs<FunctionProtoType>();
586 assert(
587 ft != nullptr || !compiler.getLangOpts().CPlusPlus
588 || (fd->getBuiltinID() != Builtin::NotBuiltin
589 && isa<FunctionNoProtoType>(t2)));
590 // __builtin_*s have no proto type?
592 } else {
593 VarDecl const * vd = dyn_cast<VarDecl>(d);
594 if (vd != nullptr) {
595 clang::PointerType const * pt = vd->getType()
596 ->getAs<clang::PointerType>();
597 ft = (pt == nullptr ? vd->getType() : pt->getPointeeType())
598 ->getAs<FunctionProtoType>();
602 if (ft != nullptr) {
603 for (unsigned i = 0; i != ft->getNumParams(); ++i) {
604 QualType t(ft->getParamType(i));
605 bool b = false;
606 if (t->isLValueReferenceType()) {
607 t = t.getNonReferenceType();
608 b = !t.isConstQualified() && isFakeBool(t) != FBK_No;
609 } else if (t->isPointerType()) {
610 for (;;) {
611 auto t2 = t->getAs<clang::PointerType>();
612 if (t2 == nullptr) {
613 break;
615 t = t2->getPointeeType();
617 b = isFakeBool(t) != FBK_No;
619 if (b && i < expr->getNumArgs()) {
620 auto const e1 = expr->getArg(i)->IgnoreParenImpCasts();
621 if (DeclRefExpr * ref = dyn_cast<DeclRefExpr>(e1)) {
622 VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
623 if (d != nullptr) {
624 varDecls_.erase(d);
626 } else if (auto const e2 = dyn_cast<MemberExpr>(e1)) {
627 if (auto const d = dyn_cast<FieldDecl>(e2->getMemberDecl())) {
628 fieldDecls_.erase(d);
634 return true;
637 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
638 if (ignoreLocation(expr)) {
639 return true;
641 auto const k = isFakeBool(expr->getType());
642 if (k != FBK_No) {
643 SourceLocation loc { compat::getBeginLoc(expr) };
644 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
645 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
647 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
648 StringRef name { Lexer::getImmediateMacroName(
649 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
650 if (k == FBK_sal_Bool && (name == "sal_False" || name == "sal_True")) {
651 auto callLoc = compiler.getSourceManager()
652 .getImmediateMacroCallerLoc(loc);
653 if (!isSharedCAndCppCode(callLoc)) {
654 SourceLocation argLoc;
655 if (compiler.getSourceManager().isMacroArgExpansion(
656 compat::getBeginLoc(expr), &argLoc)
657 //TODO: check it's the complete (first) arg to the macro
658 && (Lexer::getImmediateMacroName(
659 argLoc, compiler.getSourceManager(),
660 compiler.getLangOpts())
661 == "CPPUNIT_ASSERT_EQUAL"))
663 // Ignore sal_False/True that are directly used as
664 // arguments to CPPUNIT_ASSERT_EQUAL:
665 return true;
667 bool b = k == FBK_sal_Bool && name == "sal_True";
668 if (rewriter != nullptr) {
669 auto callSpellLoc = compiler.getSourceManager()
670 .getSpellingLoc(callLoc);
671 unsigned n = Lexer::MeasureTokenLength(
672 callSpellLoc, compiler.getSourceManager(),
673 compiler.getLangOpts());
674 if (StringRef(
675 compiler.getSourceManager().getCharacterData(
676 callSpellLoc),
678 == name)
680 return replaceText(
681 callSpellLoc, n, b ? "true" : "false");
684 report(
685 DiagnosticsEngine::Warning,
686 "use '%select{false|true}0' instead of '%1'", callLoc)
687 << b << name << expr->getSourceRange();
689 return true;
692 report(
693 DiagnosticsEngine::Warning,
694 "CStyleCastExpr, suspicious cast from %0 to %1",
695 compat::getBeginLoc(expr))
696 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
697 << expr->getType() << expr->getSourceRange();
699 return true;
702 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
703 if (ignoreLocation(expr)) {
704 return true;
706 auto const k = isFakeBool(expr->getType());
707 if (k == FBK_No) {
708 return true;
710 if (k == FBK_sal_Bool
711 && isInSpecialMainFile(
712 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr))))
714 return true;
716 report(
717 DiagnosticsEngine::Warning,
718 "CXXStaticCastExpr, suspicious cast from %0 to %1",
719 compat::getBeginLoc(expr))
720 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
721 << expr->getType() << expr->getSourceRange();
722 return true;
725 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
726 if (ignoreLocation(expr)) {
727 return true;
729 if (isFakeBool(expr->getType()) != FBK_No) {
730 report(
731 DiagnosticsEngine::Warning,
732 "CXXFunctionalCastExpr, suspicious cast from %0 to %1",
733 compat::getBeginLoc(expr))
734 << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
735 << expr->getType() << expr->getSourceRange();
737 return true;
740 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
741 FunctionAddress::VisitImplicitCastExpr(expr);
742 if (ignoreLocation(expr)) {
743 return true;
745 auto const k = isFakeBool(expr->getType());
746 if (k == FBK_No) {
747 return true;
749 auto l = compat::getBeginLoc(expr);
750 while (compiler.getSourceManager().isMacroArgExpansion(l)) {
751 l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
753 if (compiler.getSourceManager().isMacroBodyExpansion(l)) {
754 auto n = Lexer::getImmediateMacroName(
755 l, compiler.getSourceManager(), compiler.getLangOpts());
756 if ((k == FBK_GLboolean && (n == "GL_FALSE" || n == "GL_TRUE"))
757 || (k == FBK_UBool && (n == "FALSE" || n == "TRUE"))
758 || (k == FBK_jboolean && (n == "JNI_FALSE" || n == "JNI_TRUE"))
759 || (k == FBK_sal_Bool && (n == "sal_False" || n == "sal_True")))
761 return true;
764 auto e1 = expr->getSubExprAsWritten();
765 auto t = e1->getType();
766 if (!t->isFundamentalType() || loplugin::TypeCheck(t).AnyBoolean()) {
767 return true;
769 auto e2 = dyn_cast<ConditionalOperator>(e1);
770 if (e2 != nullptr) {
771 auto ic1 = dyn_cast<ImplicitCastExpr>(
772 e2->getTrueExpr()->IgnoreParens());
773 auto ic2 = dyn_cast<ImplicitCastExpr>(
774 e2->getFalseExpr()->IgnoreParens());
775 if (ic1 != nullptr && ic2 != nullptr
776 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
777 && (loplugin::TypeCheck(ic1->getSubExprAsWritten()->getType())
778 .AnyBoolean())
779 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
780 && (loplugin::TypeCheck(ic2->getSubExprAsWritten()->getType())
781 .AnyBoolean()))
783 return true;
786 report(
787 DiagnosticsEngine::Warning, "conversion from %0 to %1",
788 compat::getBeginLoc(expr))
789 << t << expr->getType() << expr->getSourceRange();
790 return true;
793 bool FakeBool::VisitReturnStmt(ReturnStmt const * stmt) {
794 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
795 // which has
797 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
799 // and
801 // return aCharClass[eCharClass];
803 if (ignoreLocation(stmt)) {
804 return true;
806 auto e = stmt->getRetValue();
807 if (e == nullptr) {
808 return true;
810 auto t = e->getType();
811 if (!t->isPointerType()) {
812 return true;
814 for (;;) {
815 auto t2 = t->getAs<clang::PointerType>();
816 if (t2 == nullptr) {
817 break;
819 t = t2->getPointeeType();
821 if (isFakeBool(t) != FBK_sal_Bool) {
822 return true;
824 auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
825 if (e2 == nullptr) {
826 return true;
828 auto e3 = dyn_cast<DeclRefExpr>(e2->getBase()->IgnoreParenImpCasts());
829 if (e3 == nullptr) {
830 return true;
832 auto d = dyn_cast<VarDecl>(e3->getDecl());
833 if (d == nullptr) {
834 return true;
836 varDecls_.erase(d);
837 return true;
840 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
841 return VisitParmVarDecl(decl);
844 bool FakeBool::VisitParmVarDecl(ParmVarDecl const * decl) {
845 if (ignoreLocation(decl)) {
846 return true;
848 auto const fbk = isFakeBool(decl->getType().getNonReferenceType());
849 if (fbk != FBK_No) {
850 FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
851 if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
852 f = f->getCanonicalDecl();
853 if (isAllRelevantCodeDefined(f)
854 && !(hasCLanguageLinkageType(f)
855 || (fbk == FBK_sal_Bool && isInUnoIncludeFile(f)
856 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
857 || decl->getType()->isReferenceType()
858 || hasBoolOverload(f, false)))
859 || f->isDeleted() || hasBoolOverload(f, true)))
861 OverrideKind k = getOverrideKind(f);
862 if (k != OverrideKind::YES) {
863 parmVarDecls_.insert({decl, fbk});
868 return true;
871 bool FakeBool::WalkUpFromVarDecl(VarDecl const * decl) {
872 return VisitVarDecl(decl);
875 bool FakeBool::VisitVarDecl(VarDecl const * decl) {
876 if (ignoreLocation(decl)) {
877 return true;
879 if (decl->isExternC()) {
880 return true;
882 auto k = isFakeBool(decl->getType());
883 if (k == FBK_No) {
884 k = isFakeBoolArray(decl->getType());
886 if (k == FBK_No) {
887 return true;
889 auto const loc = compat::getBeginLoc(decl);
890 if (k == FBK_sal_Bool
891 && isInSpecialMainFile(
892 compiler.getSourceManager().getSpellingLoc(loc)))
894 return true;
896 auto l = loc;
897 while (compiler.getSourceManager().isMacroArgExpansion(l)) {
898 l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
900 if (compiler.getSourceManager().isMacroBodyExpansion(l)
901 && isSharedCAndCppCode(compiler.getSourceManager().getImmediateMacroCallerLoc(l)))
903 return true;
905 varDecls_.insert({decl, k});
906 return true;
909 bool FakeBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
910 return VisitFieldDecl(decl);
913 bool FakeBool::VisitFieldDecl(FieldDecl const * decl) {
914 if (ignoreLocation(decl)) {
915 return true;
917 auto k = isFakeBool(decl->getType());
918 if (k == FBK_No) {
919 k = isFakeBoolArray(decl->getType());
921 if (k == FBK_No) {
922 return true;
924 if (!isAllRelevantCodeDefined(decl)) {
925 return true;
927 if (k == FBK_sal_Bool
928 && isInSpecialMainFile(
929 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
931 return true;
933 TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
934 assert(td != nullptr);
935 if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
936 || isInUnoIncludeFile(
937 compiler.getSourceManager().getSpellingLoc(
938 decl->getLocation()))))
940 fieldDecls_.insert({decl, k});
942 return true;
945 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
946 return VisitFunctionDecl(decl);
949 bool FakeBool::VisitFunctionDecl(FunctionDecl const * decl) {
950 if (ignoreLocation(decl)) {
951 return true;
953 auto const fbk = isFakeBool(decl->getReturnType().getNonReferenceType());
954 if (fbk != FBK_No
955 && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl))
956 && isAllRelevantCodeDefined(decl))
958 FunctionDecl const * f = decl->getCanonicalDecl();
959 OverrideKind k = getOverrideKind(f);
960 if (k != OverrideKind::YES
961 && !(hasCLanguageLinkageType(f)
962 || (isInUnoIncludeFile(f)
963 && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()))))
965 functionDecls_.insert({decl, fbk});
968 return true;
971 bool FakeBool::VisitValueDecl(ValueDecl const * decl) {
972 if (ignoreLocation(decl)) {
973 return true;
975 auto const k = isFakeBool(decl->getType());
976 if (k != FBK_No && !rewrite(compat::getBeginLoc(decl), k)) {
977 report(
978 DiagnosticsEngine::Warning,
979 "ValueDecl, use \"bool\" instead of %0",
980 compat::getBeginLoc(decl))
981 << decl->getType() << decl->getSourceRange();
983 return true;
986 bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
987 // Ignore special code like
989 // static_cast<sal_Bool>(true) == sal_True
991 // inside static_assert in cppu/source/uno/check.cxx:
992 return
993 loplugin::isSamePathname(
994 getFilenameOfLocation(decl->getLocation()),
995 SRCDIR "/cppu/source/uno/check.cxx")
996 || RecursiveASTVisitor::TraverseStaticAssertDecl(decl);
999 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
1000 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
1001 ++externCContexts_;
1002 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
1003 assert(externCContexts_ != 0);
1004 --externCContexts_;
1005 return ret;
1008 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
1009 return !compiler.getSourceManager().isInMainFile(spellingLocation)
1010 && (StringRef(
1011 compiler.getSourceManager().getPresumedLoc(spellingLocation)
1012 .getFilename())
1013 .endswith(".h"));
1016 bool FakeBool::isSharedCAndCppCode(SourceLocation location) const {
1017 // Assume that code is intended to be shared between C and C++ if it comes
1018 // from an include file ending in .h, and is either in an extern "C" context
1019 // or the body of a macro definition:
1020 return
1021 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
1022 && (externCContexts_ != 0
1023 || compiler.getSourceManager().isMacroBodyExpansion(location));
1026 bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
1027 if (!compiler.getSourceManager().isInMainFile(spellingLocation)) {
1028 return false;
1030 auto f = getFilenameOfLocation(spellingLocation);
1031 return loplugin::isSamePathname(f, SRCDIR "/cppu/qa/test_any.cxx")
1032 || loplugin::isSamePathname(f, SRCDIR "/cppu/source/uno/check.cxx");
1033 // TODO: the offset checks
1036 bool FakeBool::rewrite(SourceLocation location, FakeBoolKind kind) {
1037 if (rewriter != nullptr) {
1038 //TODO: "::sal_Bool" -> "bool", not "::bool"
1039 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
1040 location) };
1041 unsigned n = Lexer::MeasureTokenLength(
1042 loc, compiler.getSourceManager(), compiler.getLangOpts());
1043 if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
1044 == getName(kind))
1046 return replaceText(loc, n, "bool");
1049 return false;
1052 loplugin::Plugin::Registration<FakeBool> X("fakebool", true);
1056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */