1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
16 #include "clang/AST/Attr.h"
17 #include "clang/Basic/Builtins.h"
21 #include "functionaddress.hxx"
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
,
33 // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
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
];
43 FakeBoolKind
isFakeBool(QualType type
) {
44 TypedefType
const * t
= type
->getAs
<TypedefType
>();
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
)) {
57 FakeBoolKind
isFakeBoolArray(QualType type
) {
58 auto t
= type
->getAsArrayTypeUnsafe();
62 auto const k
= isFakeBool(t
->getElementType());
66 return isFakeBoolArray(t
->getElementType());
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
73 // extern "C" { static void f(); }
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":
79 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
80 assert(decl
!= nullptr);
81 if (decl
->isExternC()) {
84 if (decl
->isInExternCContext()) {
90 enum class OverrideKind
{ NO
, YES
, MAYBE
};
92 OverrideKind
getOverrideKind(FunctionDecl
const * decl
) {
93 CXXMethodDecl
const * m
= dyn_cast
<CXXMethodDecl
>(decl
);
95 return OverrideKind::NO
;
97 if (m
->size_overridden_methods() != 0 || m
->hasAttr
<OverrideAttr
>()) {
98 return OverrideKind::YES
;
100 if (!dyn_cast
<CXXRecordDecl
>(m
->getDeclContext())->hasAnyDependentBases()) {
101 return OverrideKind::NO
;
103 return OverrideKind::MAYBE
;
106 enum class BoolOverloadKind
{ No
, Yes
, CheckNext
};
108 BoolOverloadKind
isBoolOverloadOf(
109 FunctionDecl
const * f
, FunctionDecl
const * decl
, bool mustBeDeleted
)
111 if (!mustBeDeleted
|| f
->isDeleted()) {
112 unsigned n
= decl
->getNumParams();
113 if (f
->getNumParams() == n
) {
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() };
122 ? t2
->isBooleanType()
124 ? (t2
->isReferenceType()
125 && t2
.getNonReferenceType()->isBooleanType())
126 : t2
.getCanonicalType() == t1
.getCanonicalType()))
128 return BoolOverloadKind::CheckNext
;
130 hasFB
|= isFB
|| isFBRef
;
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
137 return BoolOverloadKind::CheckNext
;
140 //TODO: current implementation is not at all general, just tests what we
141 // encounter in practice:
142 bool hasBoolOverload(FunctionDecl
const * decl
, bool mustBeDeleted
) {
143 auto ctx
= decl
->getDeclContext();
144 if (!ctx
->isLookupContext()) {
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
:
153 case BoolOverloadKind::Yes
:
155 case BoolOverloadKind::CheckNext
:
158 } else if (auto ftd
= dyn_cast
<FunctionTemplateDecl
>(*d
)) {
159 for (auto f
: ftd
->specializations()) {
160 if (f
->getTemplateSpecializationKind()
161 == TSK_ExplicitSpecialization
)
163 switch (isBoolOverloadOf(f
, decl
, mustBeDeleted
)) {
164 case BoolOverloadKind::No
:
166 case BoolOverloadKind::Yes
:
168 case BoolOverloadKind::CheckNext
:
179 public loplugin::FunctionAddress
<loplugin::FilteringRewritePlugin
<FakeBool
>>
182 explicit FakeBool(loplugin::InstantiationData
const & data
):
183 FunctionAddress(data
) {}
185 virtual void run() override
;
187 bool VisitUnaryOperator(UnaryOperator
* op
);
189 bool VisitCallExpr(CallExpr
* expr
);
191 bool VisitCStyleCastExpr(CStyleCastExpr
* expr
);
193 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
);
195 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
);
197 bool VisitImplicitCastExpr(ImplicitCastExpr
* expr
);
199 bool VisitReturnStmt(ReturnStmt
const * stmt
);
201 bool WalkUpFromParmVarDecl(ParmVarDecl
const * decl
);
202 bool VisitParmVarDecl(ParmVarDecl
const * decl
);
204 bool WalkUpFromVarDecl(VarDecl
const * decl
);
205 bool VisitVarDecl(VarDecl
const * decl
);
207 bool WalkUpFromFieldDecl(FieldDecl
const * decl
);
208 bool VisitFieldDecl(FieldDecl
const * decl
);
210 bool WalkUpFromFunctionDecl(FunctionDecl
const * decl
);
211 bool VisitFunctionDecl(FunctionDecl
const * decl
);
213 bool VisitValueDecl(ValueDecl
const * decl
);
215 bool TraverseStaticAssertDecl(StaticAssertDecl
* decl
);
217 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
);
220 bool isFromCIncludeFile(SourceLocation spellingLocation
) const;
222 bool isSharedCAndCppCode(SourceLocation location
) const;
224 bool isInSpecialMainFile(SourceLocation spellingLocation
) const;
226 bool rewrite(SourceLocation location
, FakeBoolKind kind
);
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;
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) {
244 compiler
.getSourceManager().getExpansionLoc(
245 tsi
->getTypeLoc().getBeginLoc()) };
247 compiler
.getSourceManager().getExpansionLoc(
248 tsi
->getTypeLoc().getEndLoc()) };
249 assert(l
.isFileID() && end
.isFileID());
251 || compiler
.getSourceManager().isBeforeInTranslationUnit(
255 unsigned n
= Lexer::MeasureTokenLength(
256 l
, compiler
.getSourceManager(),
257 compiler
.getLangOpts());
259 compiler
.getSourceManager().getCharacterData(l
),
261 if (s
== getName(fbk
)) {
268 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
272 if (!rewrite(loc
, fbk
)) {
274 DiagnosticsEngine::Warning
,
275 "VarDecl, use \"bool\" instead of %0", loc
)
276 << decl
->getType().getLocalUnqualifiedType()
277 << decl
->getSourceRange();
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) {
286 compiler
.getSourceManager().getExpansionLoc(
287 tsi
->getTypeLoc().getBeginLoc()) };
289 compiler
.getSourceManager().getExpansionLoc(
290 tsi
->getTypeLoc().getEndLoc()) };
291 assert(l
.isFileID() && end
.isFileID());
293 || compiler
.getSourceManager().isBeforeInTranslationUnit(
297 unsigned n
= Lexer::MeasureTokenLength(
298 l
, compiler
.getSourceManager(),
299 compiler
.getLangOpts());
301 compiler
.getSourceManager().getCharacterData(l
),
303 if (s
== getName(fbk
)) {
310 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
314 if (!rewrite(loc
, fbk
)) {
316 DiagnosticsEngine::Warning
,
317 "FieldDecl, use \"bool\" instead of %0", loc
)
318 << decl
->getType().getLocalUnqualifiedType() << decl
->getSourceRange();
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()) {
328 SourceLocation loc
{ compat::getBeginLoc(decl
) };
329 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
330 if (tsi
!= nullptr) {
332 compiler
.getSourceManager().getExpansionLoc(
333 tsi
->getTypeLoc().getBeginLoc()) };
335 compiler
.getSourceManager().getExpansionLoc(
336 tsi
->getTypeLoc().getEndLoc()) };
337 assert(l
.isFileID() && end
.isFileID());
339 || (compiler
.getSourceManager()
340 .isBeforeInTranslationUnit(l
, end
)))
343 unsigned n
= Lexer::MeasureTokenLength(
344 l
, compiler
.getSourceManager(),
345 compiler
.getLangOpts());
347 compiler
.getSourceManager().getCharacterData(l
),
349 if (s
== getName(fbk
)) {
356 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
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
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
)))
383 DiagnosticsEngine::Warning
,
384 ("ParmVarDecl, use \"bool\" instead of"
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')")
393 << decl
->getSourceRange();
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()) {
402 SourceLocation loc
{ compat::getBeginLoc(decl
) };
403 SourceLocation l
{ compiler
.getSourceManager().getExpansionLoc(
405 SourceLocation end
{ compiler
.getSourceManager().getExpansionLoc(
406 decl
->getNameInfo().getLoc()) };
407 assert(l
.isFileID() && end
.isFileID());
408 if (compiler
.getSourceManager().isBeforeInTranslationUnit(l
, end
)) {
410 unsigned n
= Lexer::MeasureTokenLength(
411 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
413 compiler
.getSourceManager().getCharacterData(l
), n
};
414 if (s
== getName(fbk
)) {
418 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
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
427 if (!((compiler
.getSourceManager().isInMainFile(
428 compiler
.getSourceManager().getSpellingLoc(
429 decl
->getNameInfo().getLoc()))
430 || f
->isDefined() || f
->isPure())
431 && rewrite(loc
, fbk
)))
434 DiagnosticsEngine::Warning
,
435 "use \"bool\" instead of %0 as return type%1",
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"
443 << decl
->getSourceRange();
449 bool FakeBool::VisitUnaryOperator(UnaryOperator
* op
) {
450 if (op
->getOpcode() != UO_AddrOf
) {
451 return FunctionAddress::VisitUnaryOperator(op
);
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());
461 } else if (auto const e3
= dyn_cast
<MemberExpr
>(e1
)) {
462 if (auto const d
= dyn_cast
<FieldDecl
>(e3
->getMemberDecl())) {
463 fieldDecls_
.erase(d
);
470 bool FakeBool::VisitCallExpr(CallExpr
* expr
) {
471 Decl
const * d
= expr
->getCalleeDecl();
472 FunctionProtoType
const * ft
= nullptr;
474 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
476 if (!hasBoolOverload(fd
, false)) {
477 clang::PointerType
const * pt
= fd
->getType()
478 ->getAs
<clang::PointerType
>();
480 pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
481 ft
= t2
->getAs
<FunctionProtoType
>();
483 ft
!= nullptr || !compiler
.getLangOpts().CPlusPlus
484 || (fd
->getBuiltinID() != Builtin::NotBuiltin
485 && isa
<FunctionNoProtoType
>(t2
)));
486 // __builtin_*s have no proto type?
489 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
491 clang::PointerType
const * pt
= vd
->getType()
492 ->getAs
<clang::PointerType
>();
493 ft
= (pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
494 ->getAs
<FunctionProtoType
>();
499 for (unsigned i
= 0; i
!= ft
->getNumParams(); ++i
) {
500 QualType
t(ft
->getParamType(i
));
502 if (t
->isLValueReferenceType()) {
503 t
= t
.getNonReferenceType();
504 b
= !t
.isConstQualified() && isFakeBool(t
) != FBK_No
;
505 } else if (t
->isPointerType()) {
507 auto t2
= t
->getAs
<clang::PointerType
>();
511 t
= t2
->getPointeeType();
513 b
= isFakeBool(t
) != FBK_No
;
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());
522 } else if (auto const e2
= dyn_cast
<MemberExpr
>(e1
)) {
523 if (auto const d
= dyn_cast
<FieldDecl
>(e2
->getMemberDecl())) {
524 fieldDecls_
.erase(d
);
533 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr
* expr
) {
534 if (ignoreLocation(expr
)) {
537 auto const k
= isFakeBool(expr
->getType());
539 SourceLocation loc
{ compat::getBeginLoc(expr
) };
540 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
541 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
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"))
559 // Ignore sal_False/True that are directly used as
560 // arguments to CPPUNIT_ASSERT_EQUAL:
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());
571 compiler
.getSourceManager().getCharacterData(
577 callSpellLoc
, n
, b
? "true" : "false");
581 DiagnosticsEngine::Warning
,
582 "use '%select{false|true}0' instead of '%1'", callLoc
)
583 << b
<< name
<< expr
->getSourceRange();
587 if (isSharedCAndCppCode(loc
)) {
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();
601 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
) {
602 if (ignoreLocation(expr
)) {
605 auto const k
= isFakeBool(expr
->getType());
609 if (k
== FBK_sal_Bool
610 && isInSpecialMainFile(
611 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
))))
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();
624 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
) {
625 if (ignoreLocation(expr
)) {
628 if (isFakeBool(expr
->getType()) != FBK_No
) {
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();
639 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr
* expr
) {
640 FunctionAddress::VisitImplicitCastExpr(expr
);
641 if (ignoreLocation(expr
)) {
644 if (isFakeBool(expr
->getType()) == FBK_No
) {
647 auto l
= compat::getBeginLoc(expr
);
648 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
649 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
651 if (compiler
.getSourceManager().isMacroBodyExpansion(l
) && isSharedCAndCppCode(l
)) {
654 auto e1
= expr
->getSubExprAsWritten();
655 auto t
= e1
->getType();
656 if (!t
->isFundamentalType() || loplugin::TypeCheck(t
).AnyBoolean()) {
659 auto e2
= dyn_cast
<ConditionalOperator
>(e1
);
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())
669 && ic2
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
670 && (loplugin::TypeCheck(ic2
->getSubExprAsWritten()->getType())
677 DiagnosticsEngine::Warning
, "conversion from %0 to %1",
678 compat::getBeginLoc(expr
))
679 << t
<< expr
->getType() << expr
->getSourceRange();
683 bool FakeBool::VisitReturnStmt(ReturnStmt
const * stmt
) {
684 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
687 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
691 // return aCharClass[eCharClass];
693 if (ignoreLocation(stmt
)) {
696 auto e
= stmt
->getRetValue();
700 auto t
= e
->getType();
701 if (!t
->isPointerType()) {
705 auto t2
= t
->getAs
<clang::PointerType
>();
709 t
= t2
->getPointeeType();
711 if (isFakeBool(t
) != FBK_sal_Bool
) {
714 auto e2
= dyn_cast
<ArraySubscriptExpr
>(e
->IgnoreParenImpCasts());
718 auto e3
= dyn_cast
<DeclRefExpr
>(e2
->getBase()->IgnoreParenImpCasts());
722 auto d
= dyn_cast
<VarDecl
>(e3
->getDecl());
730 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl
const * decl
) {
731 return VisitParmVarDecl(decl
);
734 bool FakeBool::VisitParmVarDecl(ParmVarDecl
const * decl
) {
735 if (ignoreLocation(decl
)) {
738 auto const fbk
= isFakeBool(decl
->getType().getNonReferenceType());
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)))
751 OverrideKind k
= getOverrideKind(f
);
752 if (k
!= OverrideKind::YES
) {
753 parmVarDecls_
.insert({decl
, fbk
});
761 bool FakeBool::WalkUpFromVarDecl(VarDecl
const * decl
) {
762 return VisitVarDecl(decl
);
765 bool FakeBool::VisitVarDecl(VarDecl
const * decl
) {
766 if (ignoreLocation(decl
)) {
769 if (decl
->isExternC()) {
772 auto k
= isFakeBool(decl
->getType());
774 k
= isFakeBoolArray(decl
->getType());
779 auto const loc
= compat::getBeginLoc(decl
);
780 if (k
== FBK_sal_Bool
781 && isInSpecialMainFile(
782 compiler
.getSourceManager().getSpellingLoc(loc
)))
787 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
788 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
790 if (compiler
.getSourceManager().isMacroBodyExpansion(l
)
791 && isSharedCAndCppCode(l
))
795 varDecls_
.insert({decl
, k
});
799 bool FakeBool::WalkUpFromFieldDecl(FieldDecl
const * decl
) {
800 return VisitFieldDecl(decl
);
803 bool FakeBool::VisitFieldDecl(FieldDecl
const * decl
) {
804 if (ignoreLocation(decl
)) {
807 auto k
= isFakeBool(decl
->getType());
809 k
= isFakeBoolArray(decl
->getType());
814 if (!handler
.isAllRelevantCodeDefined(decl
)) {
817 if (k
== FBK_sal_Bool
818 && isInSpecialMainFile(
819 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl
))))
823 TagDecl
const * td
= dyn_cast
<TagDecl
>(decl
->getDeclContext());
825 //TODO: ObjCInterface
828 if (!(((td
->isStruct() || td
->isUnion()) && td
->isExternCContext())
829 || isInUnoIncludeFile(
830 compiler
.getSourceManager().getSpellingLoc(
831 decl
->getLocation()))))
833 fieldDecls_
.insert({decl
, k
});
838 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl
const * decl
) {
839 return VisitFunctionDecl(decl
);
842 bool FakeBool::VisitFunctionDecl(FunctionDecl
const * decl
) {
843 if (ignoreLocation(decl
)) {
846 auto const fbk
= isFakeBool(decl
->getReturnType().getNonReferenceType());
848 && !(decl
->isDeletedAsWritten() && isa
<CXXConversionDecl
>(decl
))
849 && handler
.isAllRelevantCodeDefined(decl
))
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
>()))))
858 functionDecls_
.insert({decl
, fbk
});
864 bool FakeBool::VisitValueDecl(ValueDecl
const * decl
) {
865 if (ignoreLocation(decl
)) {
868 auto const k
= isFakeBool(decl
->getType());
869 if (k
!= FBK_No
&& !rewrite(compat::getBeginLoc(decl
), k
)) {
871 DiagnosticsEngine::Warning
,
872 "ValueDecl, use \"bool\" instead of %0",
873 compat::getBeginLoc(decl
))
874 << decl
->getType() << decl
->getSourceRange();
879 bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl
* decl
) {
880 // Ignore special code like
882 // static_cast<sal_Bool>(true) == sal_True
884 // inside static_assert in cppu/source/uno/check.cxx:
886 loplugin::isSamePathname(
887 getFilenameOfLocation(decl
->getLocation()),
888 SRCDIR
"/cppu/source/uno/check.cxx")
889 || RecursiveASTVisitor::TraverseStaticAssertDecl(decl
);
892 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
) {
893 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
895 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
896 assert(externCContexts_
!= 0);
901 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation
) const {
902 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
904 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
909 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:
914 isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
915 && (externCContexts_
!= 0
916 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
919 bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation
) const {
920 if (!compiler
.getSourceManager().isInMainFile(spellingLocation
)) {
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
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(
934 unsigned n
= Lexer::MeasureTokenLength(
935 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
936 if (std::string(compiler
.getSourceManager().getCharacterData(loc
), n
)
939 return replaceText(loc
, n
, "bool");
945 loplugin::Plugin::Registration
<FakeBool
> X("fakebool", true);
949 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */