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