1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include <algorithm>
11 #include <cassert>
12 #include <limits>
13 #include <map>
14 #include <string>
15 
16 #include "clang/AST/Attr.h"
17 #include "clang/Basic/Builtins.h"
18 
19 #include "check.hxx"
20 #include "compat.hxx"
21 #include "functionaddress.hxx"
22 #include "plugin.hxx"
23 
24 namespace {
25 
26 enum FakeBoolKind {
27     FBK_No,
28     FBK_BOOL, FBK_First = FBK_BOOL,
29     FBK_Boolean, FBK_FT_Bool, FBK_FcBool, FBK_GLboolean, FBK_NPBool, FBK_TW_BOOL, FBK_UBool,
30     FBK_boolean, FBK_dbus_bool_t, FBK_gboolean, FBK_hb_boot_t, FBK_jboolean, FBK_my_bool,
31     FBK_sal_Bool,
32     FBK_End };
33     // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
34 
getName(FakeBoolKind k)35 StringRef getName(FakeBoolKind k) {
36     static constexpr llvm::StringLiteral names[] = {
37         "BOOL", "Boolean", "FT_Bool", "FcBool", "GLboolean", "NPBool", "TW_BOOL", "UBool",
38         "boolean", "dbus_bool_t", "gboolean", "hb_boot_t", "jboolean", "my_bool", "sal_Bool"};
39     assert(k >= FBK_First && k < FBK_End);
40     return names[k - FBK_First];
41 }
42 
isFakeBool(QualType type)43 FakeBoolKind isFakeBool(QualType type) {
44     TypedefType const * t = type->getAs<TypedefType>();
45     if (t != nullptr) {
46         auto const name = t->getDecl()->getName();
47         for (int i = FBK_First; i != FBK_End; ++i) {
48             auto const k = FakeBoolKind(i);
49             if (name == getName(k)) {
50                 return k;
51             }
52         }
53     }
54     return FBK_No;
55 }
56 
isFakeBoolArray(QualType type)57 FakeBoolKind isFakeBoolArray(QualType type) {
58     auto t = type->getAsArrayTypeUnsafe();
59     if (t == nullptr) {
60         return FBK_No;
61     }
62     auto const k = isFakeBool(t->getElementType());
63     if (k != FBK_No) {
64         return k;
65     }
66     return isFakeBoolArray(t->getElementType());
67 }
68 
69 // It appears that, given a function declaration, there is no way to determine
70 // the language linkage of the function's type, only of the function's name
71 // (via FunctionDecl::isExternC); however, in a case like
72 //
73 //   extern "C" { static void f(); }
74 //
75 // the function's name does not have C language linkage while the function's
76 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
77 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
78 // "Language linkage of function type":
hasCLanguageLinkageType(FunctionDecl const * decl)79 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
80     assert(decl != nullptr);
81     if (decl->isExternC()) {
82         return true;
83     }
84     if (decl->isInExternCContext()) {
85         return true;
86     }
87     return false;
88 }
89 
90 enum class OverrideKind { NO, YES, MAYBE };
91 
getOverrideKind(FunctionDecl const * decl)92 OverrideKind getOverrideKind(FunctionDecl const * decl) {
93     CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
94     if (m == nullptr) {
95         return OverrideKind::NO;
96     }
97     if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
98         return OverrideKind::YES;
99     }
100     if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
101         return OverrideKind::NO;
102     }
103     return OverrideKind::MAYBE;
104 }
105 
106 enum class BoolOverloadKind { No, Yes, CheckNext };
107 
isBoolOverloadOf(FunctionDecl const * f,FunctionDecl const * decl,bool mustBeDeleted)108 BoolOverloadKind isBoolOverloadOf(
109     FunctionDecl const * f, FunctionDecl const * decl, bool mustBeDeleted)
110 {
111     if (!mustBeDeleted || f->isDeleted()) {
112         unsigned n = decl->getNumParams();
113         if (f->getNumParams() == n) {
114             bool hasFB = false;
115             for (unsigned i = 0; i != n; ++i) {
116                 QualType t1 { decl->getParamDecl(i)->getType() };
117                 bool isFB = isFakeBool(t1) != FBK_No;
118                 bool isFBRef = !isFB && t1->isReferenceType()
119                     && isFakeBool(t1.getNonReferenceType()) != FBK_No;
120                 QualType t2 { f->getParamDecl(i)->getType() };
121                 if (!(isFB
122                       ? t2->isBooleanType()
123                       : isFBRef
124                       ? (t2->isReferenceType()
125                          && t2.getNonReferenceType()->isBooleanType())
126                       : t2.getCanonicalType() == t1.getCanonicalType()))
127                 {
128                     return BoolOverloadKind::CheckNext;
129                 }
130                 hasFB |= isFB || isFBRef;
131             }
132             return hasFB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
133                 // cheaply protect against the case where decl would have no
134                 // fake bool parameters at all and would match itself
135         }
136     }
137     return BoolOverloadKind::CheckNext;
138 }
139 
140 //TODO: current implementation is not at all general, just tests what we
141 // encounter in practice:
hasBoolOverload(FunctionDecl const * decl,bool mustBeDeleted)142 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
143     auto ctx = decl->getDeclContext();
144     if (!ctx->isLookupContext()) {
145         return false;
146     }
147     auto res = ctx->lookup(decl->getDeclName());
148     for (auto d = res.begin(); d != res.end(); ++d) {
149         if (auto f = dyn_cast<FunctionDecl>(*d)) {
150             switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
151             case BoolOverloadKind::No:
152                 return false;
153             case BoolOverloadKind::Yes:
154                 return true;
155             case BoolOverloadKind::CheckNext:
156                 break;
157             }
158         } else if (auto ftd = dyn_cast<FunctionTemplateDecl>(*d)) {
159             for (auto f: ftd->specializations()) {
160                 if (f->getTemplateSpecializationKind()
161                     == TSK_ExplicitSpecialization)
162                 {
163                     switch (isBoolOverloadOf(f, decl, mustBeDeleted)) {
164                     case BoolOverloadKind::No:
165                         return false;
166                     case BoolOverloadKind::Yes:
167                         return true;
168                     case BoolOverloadKind::CheckNext:
169                         break;
170                     }
171                 }
172             }
173         }
174     }
175     return false;
176 }
177 
178 class FakeBool:
179     public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<FakeBool>>
180 {
181 public:
FakeBool(loplugin::InstantiationData const & data)182     explicit FakeBool(loplugin::InstantiationData const & data):
183         FunctionAddress(data) {}
184 
185     virtual void run() override;
186 
187     bool VisitUnaryOperator(UnaryOperator * op);
188 
189     bool VisitCallExpr(CallExpr * expr);
190 
191     bool VisitCStyleCastExpr(CStyleCastExpr * expr);
192 
193     bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
194 
195     bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
196 
197     bool VisitImplicitCastExpr(ImplicitCastExpr * expr);
198 
199     bool VisitReturnStmt(ReturnStmt const * stmt);
200 
201     bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
202     bool VisitParmVarDecl(ParmVarDecl const * decl);
203 
204     bool WalkUpFromVarDecl(VarDecl const * decl);
205     bool VisitVarDecl(VarDecl const * decl);
206 
207     bool WalkUpFromFieldDecl(FieldDecl const * decl);
208     bool VisitFieldDecl(FieldDecl const * decl);
209 
210     bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
211     bool VisitFunctionDecl(FunctionDecl const * decl);
212 
213     bool VisitValueDecl(ValueDecl const * decl);
214 
215     bool TraverseStaticAssertDecl(StaticAssertDecl * decl);
216 
217     bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
218 
219 private:
220     bool isFromCIncludeFile(SourceLocation spellingLocation) const;
221 
222     bool isSharedCAndCppCode(SourceLocation location) const;
223 
224     bool isInSpecialMainFile(SourceLocation spellingLocation) const;
225 
226     bool rewrite(SourceLocation location, FakeBoolKind kind);
227 
228     std::map<VarDecl const *, FakeBoolKind> varDecls_;
229     std::map<FieldDecl const *, FakeBoolKind> fieldDecls_;
230     std::map<ParmVarDecl const *, FakeBoolKind> parmVarDecls_;
231     std::map<FunctionDecl const *, FakeBoolKind> functionDecls_;
232     unsigned int externCContexts_ = 0;
233 };
234 
run()235 void FakeBool::run() {
236     if (compiler.getLangOpts().CPlusPlus) {
237         TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
238         for (auto const & dcl: varDecls_) {
239             auto const decl = dcl.first; auto const fbk = dcl.second;
240             SourceLocation loc { compat::getBeginLoc(decl) };
241             TypeSourceInfo * tsi = decl->getTypeSourceInfo();
242             if (tsi != nullptr) {
243                 SourceLocation l {
244                     compiler.getSourceManager().getExpansionLoc(
245                         tsi->getTypeLoc().getBeginLoc()) };
246                 SourceLocation end {
247                     compiler.getSourceManager().getExpansionLoc(
248                         tsi->getTypeLoc().getEndLoc()) };
249                 assert(l.isFileID() && end.isFileID());
250                 if (l == end
251                     || compiler.getSourceManager().isBeforeInTranslationUnit(
252                         l, end))
253                 {
254                     for (;;) {
255                         unsigned n = Lexer::MeasureTokenLength(
256                             l, compiler.getSourceManager(),
257                             compiler.getLangOpts());
258                         std::string s {
259                             compiler.getSourceManager().getCharacterData(l),
260                             n };
261                         if (s == getName(fbk)) {
262                             loc = l;
263                             break;
264                         }
265                         if (l == end) {
266                             break;
267                         }
268                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
269                     }
270                 }
271             }
272             if (!rewrite(loc, fbk)) {
273                 report(
274                     DiagnosticsEngine::Warning,
275                     "VarDecl, use \"bool\" instead of %0", loc)
276                     << decl->getType().getLocalUnqualifiedType()
277                     << decl->getSourceRange();
278             }
279         }
280         for (auto const & dcl: fieldDecls_) {
281             auto const decl = dcl.first; auto const fbk = dcl.second;
282             SourceLocation loc { compat::getBeginLoc(decl) };
283             TypeSourceInfo * tsi = decl->getTypeSourceInfo();
284             if (tsi != nullptr) {
285                 SourceLocation l {
286                     compiler.getSourceManager().getExpansionLoc(
287                         tsi->getTypeLoc().getBeginLoc()) };
288                 SourceLocation end {
289                     compiler.getSourceManager().getExpansionLoc(
290                         tsi->getTypeLoc().getEndLoc()) };
291                 assert(l.isFileID() && end.isFileID());
292                 if (l == end
293                     || compiler.getSourceManager().isBeforeInTranslationUnit(
294                         l, end))
295                 {
296                     for (;;) {
297                         unsigned n = Lexer::MeasureTokenLength(
298                             l, compiler.getSourceManager(),
299                             compiler.getLangOpts());
300                         std::string s {
301                             compiler.getSourceManager().getCharacterData(l),
302                             n };
303                         if (s == getName(fbk)) {
304                             loc = l;
305                             break;
306                         }
307                         if (l == end) {
308                             break;
309                         }
310                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
311                     }
312                 }
313             }
314             if (!rewrite(loc, fbk)) {
315                 report(
316                     DiagnosticsEngine::Warning,
317                     "FieldDecl, use \"bool\" instead of %0", loc)
318                     << decl->getType().getLocalUnqualifiedType() << decl->getSourceRange();
319             }
320         }
321         auto const ignoredFns = getFunctionsWithAddressTaken();
322         for (auto const & dcl: parmVarDecls_) {
323             auto const decl = dcl.first; auto const fbk = dcl.second;
324             FunctionDecl const * f = cast<FunctionDecl>(decl->getDeclContext())->getCanonicalDecl();
325             if (ignoredFns.find(f) != ignoredFns.end()) {
326                 continue;
327             }
328             SourceLocation loc { compat::getBeginLoc(decl) };
329             TypeSourceInfo * tsi = decl->getTypeSourceInfo();
330             if (tsi != nullptr) {
331                 SourceLocation l {
332                     compiler.getSourceManager().getExpansionLoc(
333                         tsi->getTypeLoc().getBeginLoc()) };
334                 SourceLocation end {
335                     compiler.getSourceManager().getExpansionLoc(
336                         tsi->getTypeLoc().getEndLoc()) };
337                 assert(l.isFileID() && end.isFileID());
338                 if (l == end
339                     || (compiler.getSourceManager()
340                         .isBeforeInTranslationUnit(l, end)))
341                 {
342                     for (;;) {
343                         unsigned n = Lexer::MeasureTokenLength(
344                             l, compiler.getSourceManager(),
345                             compiler.getLangOpts());
346                         std::string s {
347                             compiler.getSourceManager().getCharacterData(l),
348                             n };
349                         if (s == getName(fbk)) {
350                             loc = l;
351                             break;
352                         }
353                         if (l == end) {
354                             break;
355                         }
356                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
357                     }
358                 }
359             }
360             // Only rewrite declarations in include files if a
361             // definition is also seen, to avoid compilation of a
362             // definition (in a main file only processed later) to fail
363             // with a "mismatch" error before the rewriter had a chance
364             // to act upon the definition (but use the heuristic of
365             // assuming pure virtual functions do not have definitions);
366             // also, do not automatically rewrite functions that could
367             // implicitly override depend base functions (and thus stop
368             // doing so after the rewrite; note that this is less
369             // dangerous for return types than for parameter types,
370             // where the function would still implicitly override and
371             // cause a compilation error due to the incompatible return
372             // type):
373             OverrideKind k = getOverrideKind(f);
374             if (!((compiler.getSourceManager().isInMainFile(
375                        compiler.getSourceManager().getSpellingLoc(
376                            dyn_cast<FunctionDecl>(
377                                decl->getDeclContext())
378                            ->getNameInfo().getLoc()))
379                    || f->isDefined() || f->isPure())
380                   && k != OverrideKind::MAYBE && rewrite(loc, fbk)))
381             {
382                 report(
383                     DiagnosticsEngine::Warning,
384                     ("ParmVarDecl, use \"bool\" instead of"
385                      " %0%1"),
386                     loc)
387                     << decl->getType().getNonReferenceType().getLocalUnqualifiedType()
388                     << (k == OverrideKind::MAYBE
389                         ? (" (unless this member function overrides a"
390                            " dependent base member function, even"
391                            " though it is not marked 'override')")
392                         : "")
393                     << decl->getSourceRange();
394             }
395         }
396         for (auto const & dcl: functionDecls_) {
397             auto const decl = dcl.first; auto const fbk = dcl.second;
398             FunctionDecl const * f = decl->getCanonicalDecl();
399             if (ignoredFns.find(f) != ignoredFns.end()) {
400                 continue;
401             }
402             SourceLocation loc { compat::getBeginLoc(decl) };
403             SourceLocation l { compiler.getSourceManager().getExpansionLoc(
404                 loc) };
405             SourceLocation end { compiler.getSourceManager().getExpansionLoc(
406                 decl->getNameInfo().getLoc()) };
407             assert(l.isFileID() && end.isFileID());
408             if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
409                 while (l != end) {
410                     unsigned n = Lexer::MeasureTokenLength(
411                         l, compiler.getSourceManager(), compiler.getLangOpts());
412                     std::string s {
413                         compiler.getSourceManager().getCharacterData(l), n };
414                     if (s == getName(fbk)) {
415                         loc = l;
416                         break;
417                     }
418                     l = l.getLocWithOffset(std::max<unsigned>(n, 1));
419                 }
420             }
421             // Only rewrite declarations in include files if a definition is
422             // also seen, to avoid compilation of a definition (in a main file
423             // only processed later) to fail with a "mismatch" error before the
424             // rewriter had a chance to act upon the definition (but use the
425             // heuristic of assuming pure virtual functions do not have
426             // definitions):
427             if (!((compiler.getSourceManager().isInMainFile(
428                        compiler.getSourceManager().getSpellingLoc(
429                            decl->getNameInfo().getLoc()))
430                    || f->isDefined() || f->isPure())
431                   && rewrite(loc, fbk)))
432             {
433                 report(
434                     DiagnosticsEngine::Warning,
435                     "use \"bool\" instead of %0 as return type%1",
436                     loc)
437                     << decl->getReturnType().getNonReferenceType().getLocalUnqualifiedType()
438                     << (getOverrideKind(f) == OverrideKind::MAYBE
439                         ? (" (unless this member function overrides a dependent"
440                            " base member function, even though it is not marked"
441                            " 'override')")
442                         : "")
443                     << decl->getSourceRange();
444             }
445         }
446     }
447 }
448 
VisitUnaryOperator(UnaryOperator * op)449 bool FakeBool::VisitUnaryOperator(UnaryOperator * op) {
450     if (op->getOpcode() != UO_AddrOf) {
451         return FunctionAddress::VisitUnaryOperator(op);
452     }
453     FunctionAddress::VisitUnaryOperator(op);
454     Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
455     if (isFakeBool(e1->getType()) != FBK_No) {
456         if (DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1)) {
457             VarDecl const * d = dyn_cast<VarDecl>(e2->getDecl());
458             if (d != nullptr) {
459                 varDecls_.erase(d);
460             }
461         } else if (auto const e3 = dyn_cast<MemberExpr>(e1)) {
462             if (auto const d = dyn_cast<FieldDecl>(e3->getMemberDecl())) {
463                 fieldDecls_.erase(d);
464             }
465         }
466     }
467     return true;
468 }
469 
VisitCallExpr(CallExpr * expr)470 bool FakeBool::VisitCallExpr(CallExpr * expr) {
471     Decl const * d = expr->getCalleeDecl();
472     FunctionProtoType const * ft = nullptr;
473     if (d != nullptr) {
474         FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
475         if (fd != nullptr) {
476             if (!hasBoolOverload(fd, false)) {
477                 clang::PointerType const * pt = fd->getType()
478                     ->getAs<clang::PointerType>();
479                 QualType t2(
480                     pt == nullptr ? fd->getType() : pt->getPointeeType());
481                 ft = t2->getAs<FunctionProtoType>();
482                 assert(
483                     ft != nullptr || !compiler.getLangOpts().CPlusPlus
484                     || (fd->getBuiltinID() != Builtin::NotBuiltin
485                         && isa<FunctionNoProtoType>(t2)));
486                     // __builtin_*s have no proto type?
487             }
488         } else {
489             VarDecl const * vd = dyn_cast<VarDecl>(d);
490             if (vd != nullptr) {
491                 clang::PointerType const * pt = vd->getType()
492                     ->getAs<clang::PointerType>();
493                 ft = (pt == nullptr ? vd->getType() : pt->getPointeeType())
494                     ->getAs<FunctionProtoType>();
495             }
496         }
497     }
498     if (ft != nullptr) {
499         for (unsigned i = 0; i != ft->getNumParams(); ++i) {
500             QualType t(ft->getParamType(i));
501             bool b = false;
502             if (t->isLValueReferenceType()) {
503                 t = t.getNonReferenceType();
504                 b = !t.isConstQualified() && isFakeBool(t) != FBK_No;
505             } else if (t->isPointerType()) {
506                 for (;;) {
507                     auto t2 = t->getAs<clang::PointerType>();
508                     if (t2 == nullptr) {
509                         break;
510                     }
511                     t = t2->getPointeeType();
512                 }
513                 b = isFakeBool(t) != FBK_No;
514             }
515             if (b && i < expr->getNumArgs()) {
516                 auto const e1 = expr->getArg(i)->IgnoreParenImpCasts();
517                 if (DeclRefExpr * ref = dyn_cast<DeclRefExpr>(e1)) {
518                     VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
519                     if (d != nullptr) {
520                         varDecls_.erase(d);
521                     }
522                 } else if (auto const e2 = dyn_cast<MemberExpr>(e1)) {
523                     if (auto const d = dyn_cast<FieldDecl>(e2->getMemberDecl())) {
524                         fieldDecls_.erase(d);
525                     }
526                 }
527             }
528         }
529     }
530     return true;
531 }
532 
VisitCStyleCastExpr(CStyleCastExpr * expr)533 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
534     if (ignoreLocation(expr)) {
535         return true;
536     }
537     auto const k = isFakeBool(expr->getType());
538     if (k != FBK_No) {
539         SourceLocation loc { compat::getBeginLoc(expr) };
540         while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
541             loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
542         }
543         if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
544             StringRef name { Lexer::getImmediateMacroName(
545                 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
546             if (k == FBK_sal_Bool && (name == "sal_False" || name == "sal_True")) {
547                 auto callLoc = compiler.getSourceManager()
548                     .getImmediateMacroCallerLoc(loc);
549                 if (!isSharedCAndCppCode(callLoc)) {
550                     SourceLocation argLoc;
551                     if (compiler.getSourceManager().isMacroArgExpansion(
552                             compat::getBeginLoc(expr), &argLoc)
553                         //TODO: check it's the complete (first) arg to the macro
554                         && (Lexer::getImmediateMacroName(
555                                 argLoc, compiler.getSourceManager(),
556                                 compiler.getLangOpts())
557                             == "CPPUNIT_ASSERT_EQUAL"))
558                     {
559                         // Ignore sal_False/True that are directly used as
560                         // arguments to CPPUNIT_ASSERT_EQUAL:
561                         return true;
562                     }
563                     bool b = k == FBK_sal_Bool && name == "sal_True";
564                     if (rewriter != nullptr) {
565                         auto callSpellLoc = compiler.getSourceManager()
566                             .getSpellingLoc(callLoc);
567                         unsigned n = Lexer::MeasureTokenLength(
568                             callSpellLoc, compiler.getSourceManager(),
569                             compiler.getLangOpts());
570                         if (StringRef(
571                                 compiler.getSourceManager().getCharacterData(
572                                     callSpellLoc),
573                                 n)
574                             == name)
575                         {
576                             return replaceText(
577                                 callSpellLoc, n, b ? "true" : "false");
578                         }
579                     }
580                     report(
581                         DiagnosticsEngine::Warning,
582                         "use '%select{false|true}0' instead of '%1'", callLoc)
583                         << b << name << expr->getSourceRange();
584                 }
585                 return true;
586             }
587             if (isSharedCAndCppCode(loc)) {
588                 return true;
589             }
590         }
591         report(
592             DiagnosticsEngine::Warning,
593             "CStyleCastExpr, suspicious cast from %0 to %1",
594             compat::getBeginLoc(expr))
595             << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
596             << expr->getType() << expr->getSourceRange();
597     }
598     return true;
599 }
600 
VisitCXXStaticCastExpr(CXXStaticCastExpr * expr)601 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
602     if (ignoreLocation(expr)) {
603         return true;
604     }
605     auto const k = isFakeBool(expr->getType());
606     if (k == FBK_No) {
607         return true;
608     }
609     if (k == FBK_sal_Bool
610         && isInSpecialMainFile(
611             compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr))))
612     {
613         return true;
614     }
615     report(
616         DiagnosticsEngine::Warning,
617         "CXXStaticCastExpr, suspicious cast from %0 to %1",
618         compat::getBeginLoc(expr))
619         << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
620         << expr->getType() << expr->getSourceRange();
621     return true;
622 }
623 
VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr)624 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
625     if (ignoreLocation(expr)) {
626         return true;
627     }
628     if (isFakeBool(expr->getType()) != FBK_No) {
629         report(
630             DiagnosticsEngine::Warning,
631             "CXXFunctionalCastExpr, suspicious cast from %0 to %1",
632             compat::getBeginLoc(expr))
633             << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
634             << expr->getType() << expr->getSourceRange();
635     }
636     return true;
637 }
638 
VisitImplicitCastExpr(ImplicitCastExpr * expr)639 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
640     FunctionAddress::VisitImplicitCastExpr(expr);
641     if (ignoreLocation(expr)) {
642         return true;
643     }
644     if (isFakeBool(expr->getType()) == FBK_No) {
645         return true;
646     }
647     auto l = compat::getBeginLoc(expr);
648     while (compiler.getSourceManager().isMacroArgExpansion(l)) {
649         l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
650     }
651     if (compiler.getSourceManager().isMacroBodyExpansion(l) && isSharedCAndCppCode(l)) {
652         return true;
653     }
654     auto e1 = expr->getSubExprAsWritten();
655     auto t = e1->getType();
656     if (!t->isFundamentalType() || loplugin::TypeCheck(t).AnyBoolean()) {
657         return true;
658     }
659     auto e2 = dyn_cast<ConditionalOperator>(e1);
660     if (e2 != nullptr) {
661         auto ic1 = dyn_cast<ImplicitCastExpr>(
662             e2->getTrueExpr()->IgnoreParens());
663         auto ic2 = dyn_cast<ImplicitCastExpr>(
664             e2->getFalseExpr()->IgnoreParens());
665         if (ic1 != nullptr && ic2 != nullptr
666             && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
667             && (loplugin::TypeCheck(ic1->getSubExprAsWritten()->getType())
668                 .AnyBoolean())
669             && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
670             && (loplugin::TypeCheck(ic2->getSubExprAsWritten()->getType())
671                 .AnyBoolean()))
672         {
673             return true;
674         }
675     }
676     report(
677         DiagnosticsEngine::Warning, "conversion from %0 to %1",
678         compat::getBeginLoc(expr))
679         << t << expr->getType() << expr->getSourceRange();
680     return true;
681 }
682 
VisitReturnStmt(ReturnStmt const * stmt)683 bool FakeBool::VisitReturnStmt(ReturnStmt const * stmt) {
684     // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
685     // which has
686     //
687     //  static sal_Bool const aCharClass[][nCharClassSize] = ...;
688     //
689     // and
690     //
691     //  return aCharClass[eCharClass];
692     //
693     if (ignoreLocation(stmt)) {
694         return true;
695     }
696     auto e = stmt->getRetValue();
697     if (e == nullptr) {
698         return true;
699     }
700     auto t = e->getType();
701     if (!t->isPointerType()) {
702         return true;
703     }
704     for (;;) {
705         auto t2 = t->getAs<clang::PointerType>();
706         if (t2 == nullptr) {
707             break;
708         }
709         t = t2->getPointeeType();
710     }
711     if (isFakeBool(t) != FBK_sal_Bool) {
712         return true;
713     }
714     auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
715     if (e2 == nullptr) {
716         return true;
717     }
718     auto e3 = dyn_cast<DeclRefExpr>(e2->getBase()->IgnoreParenImpCasts());
719     if (e3 == nullptr) {
720         return true;
721     }
722     auto d = dyn_cast<VarDecl>(e3->getDecl());
723     if (d == nullptr) {
724         return true;
725     }
726     varDecls_.erase(d);
727     return true;
728 }
729 
WalkUpFromParmVarDecl(ParmVarDecl const * decl)730 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
731     return VisitParmVarDecl(decl);
732 }
733 
VisitParmVarDecl(ParmVarDecl const * decl)734 bool FakeBool::VisitParmVarDecl(ParmVarDecl const * decl) {
735     if (ignoreLocation(decl)) {
736         return true;
737     }
738     auto const fbk = isFakeBool(decl->getType().getNonReferenceType());
739     if (fbk != FBK_No) {
740         FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
741         if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
742             f = f->getCanonicalDecl();
743             if (handler.isAllRelevantCodeDefined(f)
744                 && !(hasCLanguageLinkageType(f)
745                      || (fbk == FBK_sal_Bool && isInUnoIncludeFile(f)
746                          && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
747                              || decl->getType()->isReferenceType()
748                              || hasBoolOverload(f, false)))
749                      || f->isDeleted() || hasBoolOverload(f, true)))
750             {
751                 OverrideKind k = getOverrideKind(f);
752                 if (k != OverrideKind::YES) {
753                     parmVarDecls_.insert({decl, fbk});
754                 }
755             }
756         }
757     }
758     return true;
759 }
760 
WalkUpFromVarDecl(VarDecl const * decl)761 bool FakeBool::WalkUpFromVarDecl(VarDecl const * decl) {
762     return VisitVarDecl(decl);
763 }
764 
VisitVarDecl(VarDecl const * decl)765 bool FakeBool::VisitVarDecl(VarDecl const * decl) {
766     if (ignoreLocation(decl)) {
767         return true;
768     }
769     if (decl->isExternC()) {
770         return true;
771     }
772     auto k = isFakeBool(decl->getType());
773     if (k == FBK_No) {
774         k = isFakeBoolArray(decl->getType());
775     }
776     if (k == FBK_No) {
777         return true;
778     }
779     auto const loc = compat::getBeginLoc(decl);
780     if (k == FBK_sal_Bool
781         && isInSpecialMainFile(
782             compiler.getSourceManager().getSpellingLoc(loc)))
783     {
784         return true;
785     }
786     auto l = loc;
787     while (compiler.getSourceManager().isMacroArgExpansion(l)) {
788         l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
789     }
790     if (compiler.getSourceManager().isMacroBodyExpansion(l)
791         && isSharedCAndCppCode(l))
792     {
793         return true;
794     }
795     varDecls_.insert({decl, k});
796     return true;
797 }
798 
WalkUpFromFieldDecl(FieldDecl const * decl)799 bool FakeBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
800     return VisitFieldDecl(decl);
801 }
802 
VisitFieldDecl(FieldDecl const * decl)803 bool FakeBool::VisitFieldDecl(FieldDecl const * decl) {
804     if (ignoreLocation(decl)) {
805         return true;
806     }
807     auto k = isFakeBool(decl->getType());
808     if (k == FBK_No) {
809         k = isFakeBoolArray(decl->getType());
810     }
811     if (k == FBK_No) {
812         return true;
813     }
814     if (!handler.isAllRelevantCodeDefined(decl)) {
815         return true;
816     }
817     if (k == FBK_sal_Bool
818         && isInSpecialMainFile(
819             compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
820     {
821         return true;
822     }
823     TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
824     if (td == nullptr) {
825         //TODO: ObjCInterface
826         return true;
827     }
828     if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
829           || isInUnoIncludeFile(
830               compiler.getSourceManager().getSpellingLoc(
831                   decl->getLocation()))))
832     {
833         fieldDecls_.insert({decl, k});
834     }
835     return true;
836 }
837 
WalkUpFromFunctionDecl(FunctionDecl const * decl)838 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
839     return VisitFunctionDecl(decl);
840 }
841 
VisitFunctionDecl(FunctionDecl const * decl)842 bool FakeBool::VisitFunctionDecl(FunctionDecl const * decl) {
843     if (ignoreLocation(decl)) {
844         return true;
845     }
846     auto const fbk = isFakeBool(decl->getReturnType().getNonReferenceType());
847     if (fbk != FBK_No
848         && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl))
849         && handler.isAllRelevantCodeDefined(decl))
850     {
851         FunctionDecl const * f = decl->getCanonicalDecl();
852         OverrideKind k = getOverrideKind(f);
853         if (k != OverrideKind::YES
854             && !(hasCLanguageLinkageType(f)
855                  || (isInUnoIncludeFile(f)
856                      && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()))))
857         {
858             functionDecls_.insert({decl, fbk});
859         }
860     }
861     return true;
862 }
863 
VisitValueDecl(ValueDecl const * decl)864 bool FakeBool::VisitValueDecl(ValueDecl const * decl) {
865     if (ignoreLocation(decl)) {
866         return true;
867     }
868     auto const k = isFakeBool(decl->getType());
869     if (k != FBK_No && !rewrite(compat::getBeginLoc(decl), k)) {
870         report(
871             DiagnosticsEngine::Warning,
872             "ValueDecl, use \"bool\" instead of %0",
873             compat::getBeginLoc(decl))
874             << decl->getType() << decl->getSourceRange();
875     }
876     return true;
877 }
878 
TraverseStaticAssertDecl(StaticAssertDecl * decl)879 bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
880     // Ignore special code like
881     //
882     //   static_cast<sal_Bool>(true) == sal_True
883     //
884     // inside static_assert in cppu/source/uno/check.cxx:
885     return
886         loplugin::isSamePathname(
887             getFilenameOfLocation(decl->getLocation()),
888             SRCDIR "/cppu/source/uno/check.cxx")
889         || RecursiveASTVisitor::TraverseStaticAssertDecl(decl);
890 }
891 
TraverseLinkageSpecDecl(LinkageSpecDecl * decl)892 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
893     assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
894     ++externCContexts_;
895     bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
896     assert(externCContexts_ != 0);
897     --externCContexts_;
898     return ret;
899 }
900 
isFromCIncludeFile(SourceLocation spellingLocation) const901 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
902     return !compiler.getSourceManager().isInMainFile(spellingLocation)
903         && (StringRef(
904                 compiler.getSourceManager().getPresumedLoc(spellingLocation)
905                 .getFilename())
906             .endswith(".h"));
907 }
908 
isSharedCAndCppCode(SourceLocation location) const909 bool FakeBool::isSharedCAndCppCode(SourceLocation location) const {
910     // Assume that code is intended to be shared between C and C++ if it comes
911     // from an include file ending in .h, and is either in an extern "C" context
912     // or the body of a macro definition:
913     return
914         isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
915         && (externCContexts_ != 0
916             || compiler.getSourceManager().isMacroBodyExpansion(location));
917 }
918 
isInSpecialMainFile(SourceLocation spellingLocation) const919 bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
920     if (!compiler.getSourceManager().isInMainFile(spellingLocation)) {
921         return false;
922     }
923     auto f = getFilenameOfLocation(spellingLocation);
924     return loplugin::isSamePathname(f, SRCDIR "/cppu/qa/test_any.cxx")
925         || loplugin::isSamePathname(f, SRCDIR "/cppu/source/uno/check.cxx");
926         // TODO: the offset checks
927 }
928 
rewrite(SourceLocation location,FakeBoolKind kind)929 bool FakeBool::rewrite(SourceLocation location, FakeBoolKind kind) {
930     if (rewriter != nullptr) {
931         //TODO: "::sal_Bool" -> "bool", not "::bool"
932         SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
933                 location) };
934         unsigned n = Lexer::MeasureTokenLength(
935             loc, compiler.getSourceManager(), compiler.getLangOpts());
936         if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
937             == getName(kind))
938         {
939             return replaceText(loc, n, "bool");
940         }
941     }
942     return false;
943 }
944 
945 loplugin::Plugin::Registration<FakeBool> X("fakebool", true);
946 
947 }
948 
949 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
950