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 
18 #include "check.hxx"
19 #include "compat.hxx"
20 #include "functionaddress.hxx"
21 #include "plugin.hxx"
22 
23 namespace {
24 
25 // BEGIN code copied from LLVM's clang/lib/Sema/Sema.cpp
26 
27 typedef llvm::DenseMap<const CXXRecordDecl*, bool> RecordCompleteMap;
28 
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.
MethodsAndNestedClassesComplete(const CXXRecordDecl * RD,RecordCompleteMap & MNCComplete)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;
63     }
64   }
65   MNCComplete[RD] = Complete;
66   return Complete;
67 }
68 
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.
IsRecordFullyDefined(const CXXRecordDecl * RD,RecordCompleteMap & RecordsComplete,RecordCompleteMap & MNCComplete)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;
101     }
102   }
103   RecordsComplete[RD] = Complete;
104   return Complete;
105 }
106 
107 RecordCompleteMap RecordsComplete;
108 RecordCompleteMap MNCComplete;
109 
110 // END code copied from LLVM's clang/lib/Sema/Sema.cpp
111 
112 // Is all code that could see `decl` defined in this TU?
isAllRelevantCodeDefined(NamedDecl const * decl)113 bool isAllRelevantCodeDefined(NamedDecl const * decl) {
114     switch (decl->getAccess()) {
115     case AS_protected:
116         if (!cast<CXXRecordDecl>(decl->getDeclContext())->hasAttr<FinalAttr>()) {
117             break;
118         }
119         LLVM_FALLTHROUGH;
120     case AS_private:
121         if (IsRecordFullyDefined(
122                 cast<CXXRecordDecl>(decl->getDeclContext()), RecordsComplete, MNCComplete))
123         {
124             return true;
125         }
126         break;
127     default:
128         break;
129     }
130     return !decl->isExternallyVisible();
131 }
132 
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)
141 
getName(FakeBoolKind k)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];
148 }
149 
isFakeBool(QualType type)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;
158             }
159         }
160     }
161     return FBK_No;
162 }
163 
isFakeBoolArray(QualType type)164 FakeBoolKind isFakeBoolArray(QualType type) {
165     auto t = type->getAsArrayTypeUnsafe();
166     if (t == nullptr) {
167         return FBK_No;
168     }
169     auto const k = isFakeBool(t->getElementType());
170     if (k != FBK_No) {
171         return k;
172     }
173     return isFakeBoolArray(t->getElementType());
174 }
175 
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
179 //
180 //   extern "C" { static void f(); }
181 //
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":
hasCLanguageLinkageType(FunctionDecl const * decl)186 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
187     assert(decl != nullptr);
188     if (decl->isExternC()) {
189         return true;
190     }
191     if (decl->isInExternCContext()) {
192         return true;
193     }
194     return false;
195 }
196 
197 enum class OverrideKind { NO, YES, MAYBE };
198 
getOverrideKind(FunctionDecl const * decl)199 OverrideKind getOverrideKind(FunctionDecl const * decl) {
200     CXXMethodDecl const * m = dyn_cast<CXXMethodDecl>(decl);
201     if (m == nullptr) {
202         return OverrideKind::NO;
203     }
204     if (m->size_overridden_methods() != 0 || m->hasAttr<OverrideAttr>()) {
205         return OverrideKind::YES;
206     }
207     if (!dyn_cast<CXXRecordDecl>(m->getDeclContext())->hasAnyDependentBases()) {
208         return OverrideKind::NO;
209     }
210     return OverrideKind::MAYBE;
211 }
212 
213 enum class BoolOverloadKind { No, Yes, CheckNext };
214 
isBoolOverloadOf(FunctionDecl const * f,FunctionDecl const * decl,bool mustBeDeleted)215 BoolOverloadKind isBoolOverloadOf(
216     FunctionDecl const * f, FunctionDecl const * decl, bool mustBeDeleted)
217 {
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()))
234                 {
235                     return BoolOverloadKind::CheckNext;
236                 }
237                 hasFB |= isFB || isFBRef;
238             }
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
242         }
243     }
244     return BoolOverloadKind::CheckNext;
245 }
246 
247 //TODO: current implementation is not at all general, just tests what we
248 // encounter in practice:
hasBoolOverload(FunctionDecl const * decl,bool mustBeDeleted)249 bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
250     auto ctx = decl->getDeclContext();
251     if (!ctx->isLookupContext()) {
252         return false;
253     }
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;
264             }
265         } else if (auto ftd = dyn_cast<FunctionTemplateDecl>(*d)) {
266             for (auto f: ftd->specializations()) {
267                 if (f->getTemplateSpecializationKind()
268                     == TSK_ExplicitSpecialization)
269                 {
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;
277                     }
278                 }
279             }
280         }
281     }
282     return false;
283 }
284 
285 class FakeBool:
286     public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<FakeBool>>
287 {
288 public:
FakeBool(loplugin::InstantiationData const & data)289     explicit FakeBool(loplugin::InstantiationData const & data):
290         FunctionAddress(data) {}
291 
292     virtual void run() override;
293 
294     bool VisitUnaryAddrOf(UnaryOperator const * op);
295 
296     bool VisitCallExpr(CallExpr * expr);
297 
298     bool VisitCStyleCastExpr(CStyleCastExpr * expr);
299 
300     bool VisitCXXStaticCastExpr(CXXStaticCastExpr * expr);
301 
302     bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
303 
304     bool VisitImplicitCastExpr(ImplicitCastExpr * expr);
305 
306     bool VisitReturnStmt(ReturnStmt const * stmt);
307 
308     bool WalkUpFromParmVarDecl(ParmVarDecl const * decl);
309     bool VisitParmVarDecl(ParmVarDecl const * decl);
310 
311     bool WalkUpFromVarDecl(VarDecl const * decl);
312     bool VisitVarDecl(VarDecl const * decl);
313 
314     bool WalkUpFromFieldDecl(FieldDecl const * decl);
315     bool VisitFieldDecl(FieldDecl const * decl);
316 
317     bool WalkUpFromFunctionDecl(FunctionDecl const * decl);
318     bool VisitFunctionDecl(FunctionDecl const * decl);
319 
320     bool VisitValueDecl(ValueDecl const * decl);
321 
322     bool TraverseStaticAssertDecl(StaticAssertDecl * decl);
323 
324     bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
325 
326 private:
327     bool isFromCIncludeFile(SourceLocation spellingLocation) const;
328 
329     bool isSharedCAndCppCode(SourceLocation location) const;
330 
331     bool isInSpecialMainFile(SourceLocation spellingLocation) const;
332 
333     bool rewrite(SourceLocation location, FakeBoolKind kind);
334 
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;
340 };
341 
run()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))
360                 {
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;
371                         }
372                         if (l == end) {
373                             break;
374                         }
375                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
376                     }
377                 }
378             }
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();
385             }
386         }
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))
402                 {
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;
413                         }
414                         if (l == end) {
415                             break;
416                         }
417                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
418                     }
419                 }
420             }
421             if (!rewrite(loc, fbk)) {
422                 report(
423                     DiagnosticsEngine::Warning,
424                     "FieldDecl, use \"bool\" instead of %0", loc)
425                     << decl->getType().getLocalUnqualifiedType() << decl->getSourceRange();
426             }
427         }
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;
434             }
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)))
448                 {
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;
459                         }
460                         if (l == end) {
461                             break;
462                         }
463                         l = l.getLocWithOffset(std::max<unsigned>(n, 1));
464                     }
465                 }
466             }
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)))
488             {
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();
501             }
502         }
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;
508             }
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;
524                     }
525                     l = l.getLocWithOffset(std::max<unsigned>(n, 1));
526                 }
527             }
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)))
539             {
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();
551             }
552         }
553     }
554 }
555 
VisitUnaryAddrOf(UnaryOperator const * op)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);
564             }
565         } else if (auto const e3 = dyn_cast<MemberExpr>(e1)) {
566             if (auto const d = dyn_cast<FieldDecl>(e3->getMemberDecl())) {
567                 fieldDecls_.erase(d);
568             }
569         }
570     }
571     return true;
572 }
573 
VisitCallExpr(CallExpr * expr)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?
591             }
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>();
599             }
600         }
601     }
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;
614                     }
615                     t = t2->getPointeeType();
616                 }
617                 b = isFakeBool(t) != FBK_No;
618             }
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);
625                     }
626                 } else if (auto const e2 = dyn_cast<MemberExpr>(e1)) {
627                     if (auto const d = dyn_cast<FieldDecl>(e2->getMemberDecl())) {
628                         fieldDecls_.erase(d);
629                     }
630                 }
631             }
632         }
633     }
634     return true;
635 }
636 
VisitCStyleCastExpr(CStyleCastExpr * expr)637 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
638     if (ignoreLocation(expr)) {
639         return true;
640     }
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);
646         }
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"))
662                     {
663                         // Ignore sal_False/True that are directly used as
664                         // arguments to CPPUNIT_ASSERT_EQUAL:
665                         return true;
666                     }
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),
677                                 n)
678                             == name)
679                         {
680                             return replaceText(
681                                 callSpellLoc, n, b ? "true" : "false");
682                         }
683                     }
684                     report(
685                         DiagnosticsEngine::Warning,
686                         "use '%select{false|true}0' instead of '%1'", callLoc)
687                         << b << name << expr->getSourceRange();
688                 }
689                 return true;
690             }
691         }
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();
698     }
699     return true;
700 }
701 
VisitCXXStaticCastExpr(CXXStaticCastExpr * expr)702 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
703     if (ignoreLocation(expr)) {
704         return true;
705     }
706     auto const k = isFakeBool(expr->getType());
707     if (k == FBK_No) {
708         return true;
709     }
710     if (k == FBK_sal_Bool
711         && isInSpecialMainFile(
712             compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr))))
713     {
714         return true;
715     }
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;
723 }
724 
VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr)725 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
726     if (ignoreLocation(expr)) {
727         return true;
728     }
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();
736     }
737     return true;
738 }
739 
VisitImplicitCastExpr(ImplicitCastExpr * expr)740 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
741     FunctionAddress::VisitImplicitCastExpr(expr);
742     if (ignoreLocation(expr)) {
743         return true;
744     }
745     auto const k = isFakeBool(expr->getType());
746     if (k == FBK_No) {
747         return true;
748     }
749     auto l = compat::getBeginLoc(expr);
750     while (compiler.getSourceManager().isMacroArgExpansion(l)) {
751         l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
752     }
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")))
760         {
761             return true;
762         }
763     }
764     auto e1 = expr->getSubExprAsWritten();
765     auto t = e1->getType();
766     if (!t->isFundamentalType() || loplugin::TypeCheck(t).AnyBoolean()) {
767         return true;
768     }
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()))
782         {
783             return true;
784         }
785     }
786     report(
787         DiagnosticsEngine::Warning, "conversion from %0 to %1",
788         compat::getBeginLoc(expr))
789         << t << expr->getType() << expr->getSourceRange();
790     return true;
791 }
792 
VisitReturnStmt(ReturnStmt const * stmt)793 bool FakeBool::VisitReturnStmt(ReturnStmt const * stmt) {
794     // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
795     // which has
796     //
797     //  static sal_Bool const aCharClass[][nCharClassSize] = ...;
798     //
799     // and
800     //
801     //  return aCharClass[eCharClass];
802     //
803     if (ignoreLocation(stmt)) {
804         return true;
805     }
806     auto e = stmt->getRetValue();
807     if (e == nullptr) {
808         return true;
809     }
810     auto t = e->getType();
811     if (!t->isPointerType()) {
812         return true;
813     }
814     for (;;) {
815         auto t2 = t->getAs<clang::PointerType>();
816         if (t2 == nullptr) {
817             break;
818         }
819         t = t2->getPointeeType();
820     }
821     if (isFakeBool(t) != FBK_sal_Bool) {
822         return true;
823     }
824     auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
825     if (e2 == nullptr) {
826         return true;
827     }
828     auto e3 = dyn_cast<DeclRefExpr>(e2->getBase()->IgnoreParenImpCasts());
829     if (e3 == nullptr) {
830         return true;
831     }
832     auto d = dyn_cast<VarDecl>(e3->getDecl());
833     if (d == nullptr) {
834         return true;
835     }
836     varDecls_.erase(d);
837     return true;
838 }
839 
WalkUpFromParmVarDecl(ParmVarDecl const * decl)840 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
841     return VisitParmVarDecl(decl);
842 }
843 
VisitParmVarDecl(ParmVarDecl const * decl)844 bool FakeBool::VisitParmVarDecl(ParmVarDecl const * decl) {
845     if (ignoreLocation(decl)) {
846         return true;
847     }
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)))
860             {
861                 OverrideKind k = getOverrideKind(f);
862                 if (k != OverrideKind::YES) {
863                     parmVarDecls_.insert({decl, fbk});
864                 }
865             }
866         }
867     }
868     return true;
869 }
870 
WalkUpFromVarDecl(VarDecl const * decl)871 bool FakeBool::WalkUpFromVarDecl(VarDecl const * decl) {
872     return VisitVarDecl(decl);
873 }
874 
VisitVarDecl(VarDecl const * decl)875 bool FakeBool::VisitVarDecl(VarDecl const * decl) {
876     if (ignoreLocation(decl)) {
877         return true;
878     }
879     if (decl->isExternC()) {
880         return true;
881     }
882     auto k = isFakeBool(decl->getType());
883     if (k == FBK_No) {
884         k = isFakeBoolArray(decl->getType());
885     }
886     if (k == FBK_No) {
887         return true;
888     }
889     auto const loc = compat::getBeginLoc(decl);
890     if (k == FBK_sal_Bool
891         && isInSpecialMainFile(
892             compiler.getSourceManager().getSpellingLoc(loc)))
893     {
894         return true;
895     }
896     auto l = loc;
897     while (compiler.getSourceManager().isMacroArgExpansion(l)) {
898         l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
899     }
900     if (compiler.getSourceManager().isMacroBodyExpansion(l)
901         && isSharedCAndCppCode(compiler.getSourceManager().getImmediateMacroCallerLoc(l)))
902     {
903         return true;
904     }
905     varDecls_.insert({decl, k});
906     return true;
907 }
908 
WalkUpFromFieldDecl(FieldDecl const * decl)909 bool FakeBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
910     return VisitFieldDecl(decl);
911 }
912 
VisitFieldDecl(FieldDecl const * decl)913 bool FakeBool::VisitFieldDecl(FieldDecl const * decl) {
914     if (ignoreLocation(decl)) {
915         return true;
916     }
917     auto k = isFakeBool(decl->getType());
918     if (k == FBK_No) {
919         k = isFakeBoolArray(decl->getType());
920     }
921     if (k == FBK_No) {
922         return true;
923     }
924     if (!isAllRelevantCodeDefined(decl)) {
925         return true;
926     }
927     if (k == FBK_sal_Bool
928         && isInSpecialMainFile(
929             compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
930     {
931         return true;
932     }
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()))))
939     {
940         fieldDecls_.insert({decl, k});
941     }
942     return true;
943 }
944 
WalkUpFromFunctionDecl(FunctionDecl const * decl)945 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
946     return VisitFunctionDecl(decl);
947 }
948 
VisitFunctionDecl(FunctionDecl const * decl)949 bool FakeBool::VisitFunctionDecl(FunctionDecl const * decl) {
950     if (ignoreLocation(decl)) {
951         return true;
952     }
953     auto const fbk = isFakeBool(decl->getReturnType().getNonReferenceType());
954     if (fbk != FBK_No
955         && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl))
956         && isAllRelevantCodeDefined(decl))
957     {
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>()))))
964         {
965             functionDecls_.insert({decl, fbk});
966         }
967     }
968     return true;
969 }
970 
VisitValueDecl(ValueDecl const * decl)971 bool FakeBool::VisitValueDecl(ValueDecl const * decl) {
972     if (ignoreLocation(decl)) {
973         return true;
974     }
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();
982     }
983     return true;
984 }
985 
TraverseStaticAssertDecl(StaticAssertDecl * decl)986 bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
987     // Ignore special code like
988     //
989     //   static_cast<sal_Bool>(true) == sal_True
990     //
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);
997 }
998 
TraverseLinkageSpecDecl(LinkageSpecDecl * 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;
1006 }
1007 
isFromCIncludeFile(SourceLocation spellingLocation) const1008 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
1009     return !compiler.getSourceManager().isInMainFile(spellingLocation)
1010         && (StringRef(
1011                 compiler.getSourceManager().getPresumedLoc(spellingLocation)
1012                 .getFilename())
1013             .endswith(".h"));
1014 }
1015 
isSharedCAndCppCode(SourceLocation location) const1016 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));
1024 }
1025 
isInSpecialMainFile(SourceLocation spellingLocation) const1026 bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
1027     if (!compiler.getSourceManager().isInMainFile(spellingLocation)) {
1028         return false;
1029     }
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
1034 }
1035 
rewrite(SourceLocation location,FakeBoolKind kind)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))
1045         {
1046             return replaceText(loc, n, "bool");
1047         }
1048     }
1049     return false;
1050 }
1051 
1052 loplugin::Plugin::Registration<FakeBool> X("fakebool", true);
1053 
1054 }
1055 
1056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1057