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"
19 #include "config_clang.h"
23 #include "functionaddress.hxx"
30 FBK_BOOL
, FBK_First
= FBK_BOOL
,
31 FBK_Boolean
, FBK_FT_Bool
, FBK_FcBool
, FBK_GLboolean
, FBK_NPBool
, FBK_TW_BOOL
, FBK_UBool
,
32 FBK_boolean
, FBK_dbus_bool_t
, FBK_gboolean
, FBK_hb_boot_t
, FBK_jboolean
, FBK_my_bool
,
35 // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
37 StringRef
getName(FakeBoolKind k
) {
38 static constexpr llvm::StringLiteral names
[] = {
39 "BOOL", "Boolean", "FT_Bool", "FcBool", "GLboolean", "NPBool", "TW_BOOL", "UBool",
40 "boolean", "dbus_bool_t", "gboolean", "hb_boot_t", "jboolean", "my_bool", "sal_Bool"};
41 assert(k
>= FBK_First
&& k
< FBK_End
);
42 return names
[k
- FBK_First
];
45 FakeBoolKind
isFakeBool(QualType type
) {
46 TypedefType
const * t
= type
->getAs
<TypedefType
>();
48 auto const name
= t
->getDecl()->getName();
49 for (int i
= FBK_First
; i
!= FBK_End
; ++i
) {
50 auto const k
= FakeBoolKind(i
);
51 if (name
== getName(k
)) {
59 FakeBoolKind
isFakeBoolArray(QualType type
) {
60 auto t
= type
->getAsArrayTypeUnsafe();
64 auto const k
= isFakeBool(t
->getElementType());
68 return isFakeBoolArray(t
->getElementType());
71 // It appears that, given a function declaration, there is no way to determine
72 // the language linkage of the function's type, only of the function's name
73 // (via FunctionDecl::isExternC); however, in a case like
75 // extern "C" { static void f(); }
77 // the function's name does not have C language linkage while the function's
78 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
79 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
80 // "Language linkage of function type":
81 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
82 assert(decl
!= nullptr);
83 if (decl
->isExternC()) {
86 if (decl
->isInExternCContext()) {
92 enum class OverrideKind
{ NO
, YES
, MAYBE
};
94 OverrideKind
getOverrideKind(FunctionDecl
const * decl
) {
95 CXXMethodDecl
const * m
= dyn_cast
<CXXMethodDecl
>(decl
);
97 return OverrideKind::NO
;
99 if (m
->size_overridden_methods() != 0 || m
->hasAttr
<OverrideAttr
>()) {
100 return OverrideKind::YES
;
102 if (!dyn_cast
<CXXRecordDecl
>(m
->getDeclContext())->hasAnyDependentBases()) {
103 return OverrideKind::NO
;
105 return OverrideKind::MAYBE
;
108 enum class BoolOverloadKind
{ No
, Yes
, CheckNext
};
110 BoolOverloadKind
isBoolOverloadOf(
111 FunctionDecl
const * f
, FunctionDecl
const * decl
, bool mustBeDeleted
)
113 if (!mustBeDeleted
|| f
->isDeleted()) {
114 unsigned n
= decl
->getNumParams();
115 if (f
->getNumParams() == n
) {
117 for (unsigned i
= 0; i
!= n
; ++i
) {
118 QualType t1
{ decl
->getParamDecl(i
)->getType() };
119 bool isFB
= isFakeBool(t1
) != FBK_No
;
120 bool isFBRef
= !isFB
&& t1
->isReferenceType()
121 && isFakeBool(t1
.getNonReferenceType()) != FBK_No
;
122 QualType t2
{ f
->getParamDecl(i
)->getType() };
124 ? t2
->isBooleanType()
126 ? (t2
->isReferenceType()
127 && t2
.getNonReferenceType()->isBooleanType())
128 : t2
.getCanonicalType() == t1
.getCanonicalType()))
130 return BoolOverloadKind::CheckNext
;
132 hasFB
|= isFB
|| isFBRef
;
134 return hasFB
? BoolOverloadKind::Yes
: BoolOverloadKind::No
;
135 // cheaply protect against the case where decl would have no
136 // fake bool parameters at all and would match itself
139 return BoolOverloadKind::CheckNext
;
142 //TODO: current implementation is not at all general, just tests what we
143 // encounter in practice:
144 bool hasBoolOverload(FunctionDecl
const * decl
, bool mustBeDeleted
) {
145 auto ctx
= decl
->getDeclContext();
146 if (!ctx
->isLookupContext()) {
149 auto res
= ctx
->lookup(decl
->getDeclName());
150 for (auto d
= res
.begin(); d
!= res
.end(); ++d
) {
151 if (auto f
= dyn_cast
<FunctionDecl
>(*d
)) {
152 switch (isBoolOverloadOf(f
, decl
, mustBeDeleted
)) {
153 case BoolOverloadKind::No
:
155 case BoolOverloadKind::Yes
:
157 case BoolOverloadKind::CheckNext
:
160 } else if (auto ftd
= dyn_cast
<FunctionTemplateDecl
>(*d
)) {
161 for (auto f
: ftd
->specializations()) {
162 if (f
->getTemplateSpecializationKind()
163 == TSK_ExplicitSpecialization
)
165 switch (isBoolOverloadOf(f
, decl
, mustBeDeleted
)) {
166 case BoolOverloadKind::No
:
168 case BoolOverloadKind::Yes
:
170 case BoolOverloadKind::CheckNext
:
181 public loplugin::FunctionAddress
<loplugin::FilteringRewritePlugin
<FakeBool
>>
184 explicit FakeBool(loplugin::InstantiationData
const & data
):
185 FunctionAddress(data
) {}
187 virtual void run() override
;
189 bool VisitUnaryOperator(UnaryOperator
* op
);
191 bool VisitCallExpr(CallExpr
* expr
);
193 bool VisitCStyleCastExpr(CStyleCastExpr
* expr
);
195 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
);
197 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
);
199 bool VisitImplicitCastExpr(ImplicitCastExpr
* expr
);
201 bool VisitReturnStmt(ReturnStmt
const * stmt
);
203 bool WalkUpFromParmVarDecl(ParmVarDecl
const * decl
);
204 bool VisitParmVarDecl(ParmVarDecl
const * decl
);
206 bool WalkUpFromVarDecl(VarDecl
const * decl
);
207 bool VisitVarDecl(VarDecl
const * decl
);
209 bool WalkUpFromFieldDecl(FieldDecl
const * decl
);
210 bool VisitFieldDecl(FieldDecl
const * decl
);
212 bool WalkUpFromFunctionDecl(FunctionDecl
const * decl
);
213 bool VisitFunctionDecl(FunctionDecl
const * decl
);
215 bool VisitValueDecl(ValueDecl
const * decl
);
217 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
);
220 bool isFromCIncludeFile(SourceLocation spellingLocation
) const;
222 bool isSharedCAndCppCode(SourceLocation location
) const;
224 bool rewrite(SourceLocation location
, FakeBoolKind kind
);
226 std::map
<VarDecl
const *, FakeBoolKind
> varDecls_
;
227 std::map
<FieldDecl
const *, FakeBoolKind
> fieldDecls_
;
228 std::map
<ParmVarDecl
const *, FakeBoolKind
> parmVarDecls_
;
229 std::map
<FunctionDecl
const *, FakeBoolKind
> functionDecls_
;
230 unsigned int externCContexts_
= 0;
233 void FakeBool::run() {
234 if (compiler
.getLangOpts().CPlusPlus
) {
235 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
236 for (auto const & dcl
: varDecls_
) {
237 auto const decl
= dcl
.first
; auto const fbk
= dcl
.second
;
238 SourceLocation loc
{ decl
->getBeginLoc() };
239 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
240 if (tsi
!= nullptr) {
242 compiler
.getSourceManager().getExpansionLoc(
243 tsi
->getTypeLoc().getBeginLoc()) };
245 compiler
.getSourceManager().getExpansionLoc(
246 tsi
->getTypeLoc().getEndLoc()) };
247 assert(l
.isFileID() && end
.isFileID());
249 || compiler
.getSourceManager().isBeforeInTranslationUnit(
253 unsigned n
= Lexer::MeasureTokenLength(
254 l
, compiler
.getSourceManager(),
255 compiler
.getLangOpts());
257 compiler
.getSourceManager().getCharacterData(l
),
259 if (s
== getName(fbk
)) {
266 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
270 if (!(suppressWarningAt(loc
) || rewrite(loc
, fbk
))) {
272 DiagnosticsEngine::Warning
,
273 "VarDecl, use \"bool\" instead of %0", loc
)
274 << decl
->getType().getLocalUnqualifiedType()
275 << decl
->getSourceRange();
278 for (auto const & dcl
: fieldDecls_
) {
279 auto const decl
= dcl
.first
; auto const fbk
= dcl
.second
;
280 SourceLocation loc
{ decl
->getBeginLoc() };
281 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
282 if (tsi
!= nullptr) {
284 compiler
.getSourceManager().getExpansionLoc(
285 tsi
->getTypeLoc().getBeginLoc()) };
287 compiler
.getSourceManager().getExpansionLoc(
288 tsi
->getTypeLoc().getEndLoc()) };
289 assert(l
.isFileID() && end
.isFileID());
291 || compiler
.getSourceManager().isBeforeInTranslationUnit(
295 unsigned n
= Lexer::MeasureTokenLength(
296 l
, compiler
.getSourceManager(),
297 compiler
.getLangOpts());
299 compiler
.getSourceManager().getCharacterData(l
),
301 if (s
== getName(fbk
)) {
308 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
312 if (!(suppressWarningAt(loc
) || rewrite(loc
, fbk
))) {
314 DiagnosticsEngine::Warning
,
315 "FieldDecl, use \"bool\" instead of %0", loc
)
316 << decl
->getType().getLocalUnqualifiedType() << decl
->getSourceRange();
319 auto const ignoredFns
= getFunctionsWithAddressTaken();
320 for (auto const & dcl
: parmVarDecls_
) {
321 auto const decl
= dcl
.first
; auto const fbk
= dcl
.second
;
322 FunctionDecl
const * f
= cast
<FunctionDecl
>(decl
->getDeclContext())->getCanonicalDecl();
323 if (ignoredFns
.find(f
) != ignoredFns
.end()) {
326 SourceLocation loc
{ decl
->getBeginLoc() };
327 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
328 if (tsi
!= nullptr) {
330 compiler
.getSourceManager().getExpansionLoc(
331 tsi
->getTypeLoc().getBeginLoc()) };
333 compiler
.getSourceManager().getExpansionLoc(
334 tsi
->getTypeLoc().getEndLoc()) };
335 assert(l
.isFileID() && end
.isFileID());
337 || (compiler
.getSourceManager()
338 .isBeforeInTranslationUnit(l
, end
)))
341 unsigned n
= Lexer::MeasureTokenLength(
342 l
, compiler
.getSourceManager(),
343 compiler
.getLangOpts());
345 compiler
.getSourceManager().getCharacterData(l
),
347 if (s
== getName(fbk
)) {
354 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
358 // Only rewrite declarations in include files if a
359 // definition is also seen, to avoid compilation of a
360 // definition (in a main file only processed later) to fail
361 // with a "mismatch" error before the rewriter had a chance
362 // to act upon the definition (but use the heuristic of
363 // assuming pure virtual functions do not have definitions);
364 // also, do not automatically rewrite functions that could
365 // implicitly override depend base functions (and thus stop
366 // doing so after the rewrite; note that this is less
367 // dangerous for return types than for parameter types,
368 // where the function would still implicitly override and
369 // cause a compilation error due to the incompatible return
371 OverrideKind k
= getOverrideKind(f
);
372 if (!((compiler
.getSourceManager().isInMainFile(
373 compiler
.getSourceManager().getSpellingLoc(
374 dyn_cast
<FunctionDecl
>(
375 decl
->getDeclContext())
376 ->getNameInfo().getLoc()))
377 || f
->isDefined() || compat::isPureVirtual(f
))
378 && k
!= OverrideKind::MAYBE
&& rewrite(loc
, fbk
)))
381 DiagnosticsEngine::Warning
,
382 ("ParmVarDecl, use \"bool\" instead of"
385 << decl
->getType().getNonReferenceType().getLocalUnqualifiedType()
386 << (k
== OverrideKind::MAYBE
387 ? (" (unless this member function overrides a"
388 " dependent base member function, even"
389 " though it is not marked 'override')")
391 << decl
->getSourceRange();
394 for (auto const & dcl
: functionDecls_
) {
395 auto const decl
= dcl
.first
; auto const fbk
= dcl
.second
;
396 FunctionDecl
const * f
= decl
->getCanonicalDecl();
397 if (ignoredFns
.find(f
) != ignoredFns
.end()) {
400 SourceLocation loc
{ decl
->getBeginLoc() };
401 SourceLocation l
{ compiler
.getSourceManager().getExpansionLoc(
403 SourceLocation end
{ compiler
.getSourceManager().getExpansionLoc(
404 decl
->getNameInfo().getLoc()) };
405 assert(l
.isFileID() && end
.isFileID());
406 if (compiler
.getSourceManager().isBeforeInTranslationUnit(l
, end
)) {
408 unsigned n
= Lexer::MeasureTokenLength(
409 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
411 compiler
.getSourceManager().getCharacterData(l
), n
};
412 if (s
== getName(fbk
)) {
416 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
419 // Only rewrite declarations in include files if a definition is
420 // also seen, to avoid compilation of a definition (in a main file
421 // only processed later) to fail with a "mismatch" error before the
422 // rewriter had a chance to act upon the definition (but use the
423 // heuristic of assuming pure virtual functions do not have
425 if (!((compiler
.getSourceManager().isInMainFile(
426 compiler
.getSourceManager().getSpellingLoc(
427 decl
->getNameInfo().getLoc()))
428 || f
->isDefined() || compat::isPureVirtual(f
))
429 && rewrite(loc
, fbk
)))
432 DiagnosticsEngine::Warning
,
433 "use \"bool\" instead of %0 as return type%1",
435 << decl
->getReturnType().getNonReferenceType().getLocalUnqualifiedType()
436 << (getOverrideKind(f
) == OverrideKind::MAYBE
437 ? (" (unless this member function overrides a dependent"
438 " base member function, even though it is not marked"
441 << decl
->getSourceRange();
447 bool FakeBool::VisitUnaryOperator(UnaryOperator
* op
) {
448 if (op
->getOpcode() != UO_AddrOf
) {
449 return FunctionAddress::VisitUnaryOperator(op
);
451 FunctionAddress::VisitUnaryOperator(op
);
452 Expr
const * e1
= op
->getSubExpr()->IgnoreParenCasts();
453 if (isFakeBool(e1
->getType()) != FBK_No
) {
454 if (DeclRefExpr
const * e2
= dyn_cast
<DeclRefExpr
>(e1
)) {
455 if (auto const d
= dyn_cast
<VarDecl
>(e2
->getDecl())) {
457 } else if (auto const d
= dyn_cast
<FieldDecl
>(e2
->getDecl())) {
458 fieldDecls_
.erase(d
);
460 } else if (auto const e3
= dyn_cast
<MemberExpr
>(e1
)) {
461 if (auto const d
= dyn_cast
<FieldDecl
>(e3
->getMemberDecl())) {
462 fieldDecls_
.erase(d
);
469 bool FakeBool::VisitCallExpr(CallExpr
* expr
) {
470 Decl
const * d
= expr
->getCalleeDecl();
471 FunctionProtoType
const * ft
= nullptr;
473 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
475 if (!hasBoolOverload(fd
, false)) {
476 clang::PointerType
const * pt
= fd
->getType()
477 ->getAs
<clang::PointerType
>();
479 pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
480 ft
= t2
->getAs
<FunctionProtoType
>();
482 ft
!= nullptr || !compiler
.getLangOpts().CPlusPlus
483 || (fd
->getBuiltinID() != Builtin::NotBuiltin
484 && isa
<FunctionNoProtoType
>(t2
)));
485 // __builtin_*s have no proto type?
488 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
490 clang::PointerType
const * pt
= vd
->getType()
491 ->getAs
<clang::PointerType
>();
492 ft
= (pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
493 ->getAs
<FunctionProtoType
>();
498 for (unsigned i
= 0; i
!= ft
->getNumParams(); ++i
) {
499 QualType
t(ft
->getParamType(i
));
501 if (t
->isLValueReferenceType()) {
502 t
= t
.getNonReferenceType();
503 b
= !t
.isConstQualified() && isFakeBool(t
) != FBK_No
;
504 } else if (t
->isPointerType()) {
506 auto t2
= t
->getAs
<clang::PointerType
>();
510 t
= t2
->getPointeeType();
512 b
= isFakeBool(t
) != FBK_No
;
514 if (b
&& i
< expr
->getNumArgs()) {
515 auto const e1
= expr
->getArg(i
)->IgnoreParenImpCasts();
516 if (DeclRefExpr
* ref
= dyn_cast
<DeclRefExpr
>(e1
)) {
517 VarDecl
const * d
= dyn_cast
<VarDecl
>(ref
->getDecl());
521 } else if (auto const e2
= dyn_cast
<MemberExpr
>(e1
)) {
522 if (auto const d
= dyn_cast
<FieldDecl
>(e2
->getMemberDecl())) {
523 fieldDecls_
.erase(d
);
532 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr
* expr
) {
533 if (ignoreLocation(expr
)) {
536 auto const k
= isFakeBool(expr
->getType());
538 SourceLocation loc
{ expr
->getBeginLoc() };
539 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
540 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
542 if (compiler
.getSourceManager().isMacroBodyExpansion(loc
)) {
543 StringRef name
{ Lexer::getImmediateMacroName(
544 loc
, compiler
.getSourceManager(), compiler
.getLangOpts()) };
545 if (k
== FBK_sal_Bool
&& (name
== "sal_False" || name
== "sal_True")) {
546 auto callLoc
= compiler
.getSourceManager()
547 .getImmediateMacroCallerLoc(loc
);
548 if (!isSharedCAndCppCode(callLoc
)) {
549 SourceLocation argLoc
;
550 if (compiler
.getSourceManager().isMacroArgExpansion(
551 expr
->getBeginLoc(), &argLoc
)
552 //TODO: check it's the complete (first) arg to the macro
553 && (Lexer::getImmediateMacroName(
554 argLoc
, compiler
.getSourceManager(),
555 compiler
.getLangOpts())
556 == "CPPUNIT_ASSERT_EQUAL"))
558 // Ignore sal_False/True that are directly used as
559 // arguments to CPPUNIT_ASSERT_EQUAL:
562 if (suppressWarningAt(callLoc
)) {
565 bool b
= k
== FBK_sal_Bool
&& name
== "sal_True";
566 if (rewriter
!= nullptr) {
567 auto callSpellLoc
= compiler
.getSourceManager()
568 .getSpellingLoc(callLoc
);
569 unsigned n
= Lexer::MeasureTokenLength(
570 callSpellLoc
, compiler
.getSourceManager(),
571 compiler
.getLangOpts());
573 compiler
.getSourceManager().getCharacterData(
579 callSpellLoc
, n
, b
? "true" : "false");
583 DiagnosticsEngine::Warning
,
584 "use '%select{false|true}0' instead of '%1'", callLoc
)
585 << b
<< name
<< expr
->getSourceRange();
589 if (isSharedCAndCppCode(loc
)) {
594 DiagnosticsEngine::Warning
,
595 "CStyleCastExpr, suspicious cast from %0 to %1",
597 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
598 << expr
->getType() << expr
->getSourceRange();
603 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
) {
604 if (ignoreLocation(expr
)) {
607 if (isFakeBool(expr
->getType()) == FBK_No
) {
610 if (suppressWarningAt(expr
->getBeginLoc())) {
614 DiagnosticsEngine::Warning
,
615 "CXXStaticCastExpr, suspicious cast from %0 to %1",
617 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
618 << expr
->getType() << expr
->getSourceRange();
622 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
) {
623 if (ignoreLocation(expr
)) {
626 if (isFakeBool(expr
->getType()) != FBK_No
) {
628 DiagnosticsEngine::Warning
,
629 "CXXFunctionalCastExpr, suspicious cast from %0 to %1",
631 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
632 << expr
->getType() << expr
->getSourceRange();
637 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr
* expr
) {
638 FunctionAddress::VisitImplicitCastExpr(expr
);
639 if (ignoreLocation(expr
)) {
642 if (isFakeBool(expr
->getType()) == FBK_No
) {
645 auto l
= expr
->getBeginLoc();
646 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
647 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
649 if (compiler
.getSourceManager().isMacroBodyExpansion(l
) && isSharedCAndCppCode(l
)) {
652 auto e1
= expr
->getSubExprAsWritten();
653 auto t
= e1
->getType();
654 if (!t
->isFundamentalType() || loplugin::TypeCheck(t
).AnyBoolean()) {
657 auto e2
= dyn_cast
<ConditionalOperator
>(e1
);
659 auto ic1
= dyn_cast
<ImplicitCastExpr
>(
660 e2
->getTrueExpr()->IgnoreParens());
661 auto ic2
= dyn_cast
<ImplicitCastExpr
>(
662 e2
->getFalseExpr()->IgnoreParens());
663 if (ic1
!= nullptr && ic2
!= nullptr
664 && ic1
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
665 && (loplugin::TypeCheck(ic1
->getSubExprAsWritten()->getType())
667 && ic2
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
668 && (loplugin::TypeCheck(ic2
->getSubExprAsWritten()->getType())
675 DiagnosticsEngine::Warning
, "conversion from %0 to %1",
677 << t
<< expr
->getType() << expr
->getSourceRange();
681 bool FakeBool::VisitReturnStmt(ReturnStmt
const * stmt
) {
682 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
685 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
689 // return aCharClass[eCharClass];
691 if (ignoreLocation(stmt
)) {
694 auto e
= stmt
->getRetValue();
698 auto t
= e
->getType();
699 if (!t
->isPointerType()) {
703 auto t2
= t
->getAs
<clang::PointerType
>();
707 t
= t2
->getPointeeType();
709 if (isFakeBool(t
) != FBK_sal_Bool
) {
712 auto e2
= dyn_cast
<ArraySubscriptExpr
>(e
->IgnoreParenImpCasts());
716 auto e3
= dyn_cast
<DeclRefExpr
>(e2
->getBase()->IgnoreParenImpCasts());
720 auto d
= dyn_cast
<VarDecl
>(e3
->getDecl());
728 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl
const * decl
) {
729 return VisitParmVarDecl(decl
);
732 bool FakeBool::VisitParmVarDecl(ParmVarDecl
const * decl
) {
733 if (ignoreLocation(decl
)) {
736 auto const fbk
= isFakeBool(decl
->getType().getNonReferenceType());
738 FunctionDecl
const * f
= dyn_cast
<FunctionDecl
>(decl
->getDeclContext());
739 if (f
!= nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
740 f
= f
->getCanonicalDecl();
741 if (handler
.isAllRelevantCodeDefined(f
)
742 && !(hasCLanguageLinkageType(f
)
743 || (fbk
== FBK_sal_Bool
&& isInUnoIncludeFile(f
)
744 && (!f
->isInlined() || f
->hasAttr
<DeprecatedAttr
>()
745 || decl
->getType()->isReferenceType()
746 || hasBoolOverload(f
, false)))
747 || f
->isDeleted() || hasBoolOverload(f
, true)))
749 OverrideKind k
= getOverrideKind(f
);
750 if (k
!= OverrideKind::YES
) {
751 parmVarDecls_
.insert({decl
, fbk
});
759 bool FakeBool::WalkUpFromVarDecl(VarDecl
const * decl
) {
760 return VisitVarDecl(decl
);
763 bool FakeBool::VisitVarDecl(VarDecl
const * decl
) {
764 if (ignoreLocation(decl
)) {
767 if (decl
->isExternC()) {
770 auto k
= isFakeBool(decl
->getType());
772 k
= isFakeBoolArray(decl
->getType());
777 auto l
= decl
->getBeginLoc();
778 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
779 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
781 if (compiler
.getSourceManager().isMacroBodyExpansion(l
)
782 && isSharedCAndCppCode(l
))
786 varDecls_
.insert({decl
, k
});
790 bool FakeBool::WalkUpFromFieldDecl(FieldDecl
const * decl
) {
791 return VisitFieldDecl(decl
);
794 bool FakeBool::VisitFieldDecl(FieldDecl
const * decl
) {
795 if (ignoreLocation(decl
)) {
798 auto k
= isFakeBool(decl
->getType());
800 k
= isFakeBoolArray(decl
->getType());
805 if (!handler
.isAllRelevantCodeDefined(decl
)) {
808 TagDecl
const * td
= dyn_cast
<TagDecl
>(decl
->getDeclContext());
810 //TODO: ObjCInterface
813 if (!(((td
->isStruct() || td
->isUnion()) && td
->isExternCContext())
814 || isInUnoIncludeFile(
815 compiler
.getSourceManager().getSpellingLoc(
816 decl
->getLocation()))))
818 fieldDecls_
.insert({decl
, k
});
823 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl
const * decl
) {
824 return VisitFunctionDecl(decl
);
827 bool FakeBool::VisitFunctionDecl(FunctionDecl
const * decl
) {
828 if (ignoreLocation(decl
)) {
831 auto const fbk
= isFakeBool(decl
->getReturnType().getNonReferenceType());
833 && !(decl
->isDeletedAsWritten() && isa
<CXXConversionDecl
>(decl
))
834 && handler
.isAllRelevantCodeDefined(decl
))
836 FunctionDecl
const * f
= decl
->getCanonicalDecl();
837 OverrideKind k
= getOverrideKind(f
);
838 if (k
!= OverrideKind::YES
839 && !(hasCLanguageLinkageType(f
)
840 || (isInUnoIncludeFile(f
)
841 && (!f
->isInlined() || f
->hasAttr
<DeprecatedAttr
>()))))
843 functionDecls_
.insert({decl
, fbk
});
849 bool FakeBool::VisitValueDecl(ValueDecl
const * decl
) {
850 if (ignoreLocation(decl
)) {
853 auto const k
= isFakeBool(decl
->getType());
854 if (k
!= FBK_No
&& !rewrite(decl
->getBeginLoc(), k
)) {
856 DiagnosticsEngine::Warning
,
857 "ValueDecl, use \"bool\" instead of %0",
859 << decl
->getType() << decl
->getSourceRange();
864 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
) {
865 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
867 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
868 assert(externCContexts_
!= 0);
873 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation
) const {
874 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
875 && compat::ends_with(
876 StringRef(compiler
.getSourceManager().getPresumedLoc(spellingLocation
).getFilename()),
880 bool FakeBool::isSharedCAndCppCode(SourceLocation location
) const {
881 // Assume that code is intended to be shared between C and C++ if it comes
882 // from an include file ending in .h, and is either in an extern "C" context
883 // or the body of a macro definition:
885 isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
886 && (externCContexts_
!= 0
887 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
890 bool FakeBool::rewrite(SourceLocation location
, FakeBoolKind kind
) {
891 if (rewriter
!= nullptr) {
892 //TODO: "::sal_Bool" -> "bool", not "::bool"
893 SourceLocation loc
{ compiler
.getSourceManager().getExpansionLoc(
895 unsigned n
= Lexer::MeasureTokenLength(
896 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
897 if (std::string(compiler
.getSourceManager().getCharacterData(loc
), n
)
900 return replaceText(loc
, n
, "bool");
906 loplugin::Plugin::Registration
<FakeBool
> X("fakebool", true);
910 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */