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"
20 #include "functionaddress.hxx"
25 // BEGIN code copied from LLVM's clang/lib/Sema/Sema.cpp
27 typedef llvm::DenseMap
<const CXXRecordDecl
*, bool> RecordCompleteMap
;
29 /// Returns true, if all methods and nested classes of the given
30 /// CXXRecordDecl are defined in this translation unit.
32 /// Should only be called from ActOnEndOfTranslationUnit so that all
33 /// definitions are actually read.
34 static bool MethodsAndNestedClassesComplete(const CXXRecordDecl
*RD
,
35 RecordCompleteMap
&MNCComplete
) {
36 RecordCompleteMap::iterator Cache
= MNCComplete
.find(RD
);
37 if (Cache
!= MNCComplete
.end())
39 if (!RD
->isCompleteDefinition())
42 for (DeclContext::decl_iterator I
= RD
->decls_begin(),
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())
58 if (R
->hasDefinition())
59 Complete
= MethodsAndNestedClassesComplete(R
->getDefinition(),
65 MNCComplete
[RD
] = Complete
;
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
74 /// Should only be called from ActOnEndOfTranslationUnit so that all
75 /// definitions are actually read.
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())
82 bool Complete
= MethodsAndNestedClassesComplete(RD
, MNCComplete
);
83 for (CXXRecordDecl::friend_iterator I
= RD
->friend_begin(),
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
);
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();
99 // This is a template friend, give up.
103 RecordsComplete
[RD
] = Complete
;
107 RecordCompleteMap RecordsComplete
;
108 RecordCompleteMap MNCComplete
;
110 // END code copied from LLVM's clang/lib/Sema/Sema.cpp
112 // Is all code that could see `decl` defined in this TU?
113 bool isAllRelevantCodeDefined(NamedDecl
const * decl
) {
114 switch (decl
->getAccess()) {
116 if (!cast
<CXXRecordDecl
>(decl
->getDeclContext())->hasAttr
<FinalAttr
>()) {
121 if (IsRecordFullyDefined(
122 cast
<CXXRecordDecl
>(decl
->getDeclContext()), RecordsComplete
, MNCComplete
))
130 return !decl
->isExternallyVisible();
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
,
140 // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
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
];
150 FakeBoolKind
isFakeBool(QualType type
) {
151 TypedefType
const * t
= type
->getAs
<TypedefType
>();
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
)) {
164 FakeBoolKind
isFakeBoolArray(QualType type
) {
165 auto t
= type
->getAsArrayTypeUnsafe();
169 auto const k
= isFakeBool(t
->getElementType());
173 return isFakeBoolArray(t
->getElementType());
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
180 // extern "C" { static void f(); }
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":
186 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
187 assert(decl
!= nullptr);
188 if (decl
->isExternC()) {
191 if (decl
->isInExternCContext()) {
197 enum class OverrideKind
{ NO
, YES
, MAYBE
};
199 OverrideKind
getOverrideKind(FunctionDecl
const * decl
) {
200 CXXMethodDecl
const * m
= dyn_cast
<CXXMethodDecl
>(decl
);
202 return OverrideKind::NO
;
204 if (m
->size_overridden_methods() != 0 || m
->hasAttr
<OverrideAttr
>()) {
205 return OverrideKind::YES
;
207 if (!dyn_cast
<CXXRecordDecl
>(m
->getDeclContext())->hasAnyDependentBases()) {
208 return OverrideKind::NO
;
210 return OverrideKind::MAYBE
;
213 enum class BoolOverloadKind
{ No
, Yes
, CheckNext
};
215 BoolOverloadKind
isBoolOverloadOf(
216 FunctionDecl
const * f
, FunctionDecl
const * decl
, bool mustBeDeleted
)
218 if (!mustBeDeleted
|| f
->isDeleted()) {
219 unsigned n
= decl
->getNumParams();
220 if (f
->getNumParams() == n
) {
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() };
229 ? t2
->isBooleanType()
231 ? (t2
->isReferenceType()
232 && t2
.getNonReferenceType()->isBooleanType())
233 : t2
.getCanonicalType() == t1
.getCanonicalType()))
235 return BoolOverloadKind::CheckNext
;
237 hasFB
|= isFB
|| isFBRef
;
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
244 return BoolOverloadKind::CheckNext
;
247 //TODO: current implementation is not at all general, just tests what we
248 // encounter in practice:
249 bool hasBoolOverload(FunctionDecl
const * decl
, bool mustBeDeleted
) {
250 auto ctx
= decl
->getDeclContext();
251 if (!ctx
->isLookupContext()) {
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
:
260 case BoolOverloadKind::Yes
:
262 case BoolOverloadKind::CheckNext
:
265 } else if (auto ftd
= dyn_cast
<FunctionTemplateDecl
>(*d
)) {
266 for (auto f
: ftd
->specializations()) {
267 if (f
->getTemplateSpecializationKind()
268 == TSK_ExplicitSpecialization
)
270 switch (isBoolOverloadOf(f
, decl
, mustBeDeleted
)) {
271 case BoolOverloadKind::No
:
273 case BoolOverloadKind::Yes
:
275 case BoolOverloadKind::CheckNext
:
286 public loplugin::FunctionAddress
<loplugin::FilteringRewritePlugin
<FakeBool
>>
289 explicit FakeBool(loplugin::InstantiationData
const & data
):
290 FunctionAddress(data
) {}
292 virtual void run() override
;
294 bool VisitUnaryAddrOf(UnaryOperator
const * op
);
296 bool VisitCallExpr(CallExpr
* expr
);
298 bool VisitCStyleCastExpr(CStyleCastExpr
* expr
);
300 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
);
302 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
);
304 bool VisitImplicitCastExpr(ImplicitCastExpr
* expr
);
306 bool VisitReturnStmt(ReturnStmt
const * stmt
);
308 bool WalkUpFromParmVarDecl(ParmVarDecl
const * decl
);
309 bool VisitParmVarDecl(ParmVarDecl
const * decl
);
311 bool WalkUpFromVarDecl(VarDecl
const * decl
);
312 bool VisitVarDecl(VarDecl
const * decl
);
314 bool WalkUpFromFieldDecl(FieldDecl
const * decl
);
315 bool VisitFieldDecl(FieldDecl
const * decl
);
317 bool WalkUpFromFunctionDecl(FunctionDecl
const * decl
);
318 bool VisitFunctionDecl(FunctionDecl
const * decl
);
320 bool VisitValueDecl(ValueDecl
const * decl
);
322 bool TraverseStaticAssertDecl(StaticAssertDecl
* decl
);
324 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
);
327 bool isFromCIncludeFile(SourceLocation spellingLocation
) const;
329 bool isSharedCAndCppCode(SourceLocation location
) const;
331 bool isInSpecialMainFile(SourceLocation spellingLocation
) const;
333 bool rewrite(SourceLocation location
, FakeBoolKind kind
);
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;
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) {
351 compiler
.getSourceManager().getExpansionLoc(
352 tsi
->getTypeLoc().getBeginLoc()) };
354 compiler
.getSourceManager().getExpansionLoc(
355 tsi
->getTypeLoc().getEndLoc()) };
356 assert(l
.isFileID() && end
.isFileID());
358 || compiler
.getSourceManager().isBeforeInTranslationUnit(
362 unsigned n
= Lexer::MeasureTokenLength(
363 l
, compiler
.getSourceManager(),
364 compiler
.getLangOpts());
366 compiler
.getSourceManager().getCharacterData(l
),
368 if (s
== getName(fbk
)) {
375 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
379 if (!rewrite(loc
, fbk
)) {
381 DiagnosticsEngine::Warning
,
382 "VarDecl, use \"bool\" instead of %0", loc
)
383 << decl
->getType().getLocalUnqualifiedType()
384 << decl
->getSourceRange();
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) {
393 compiler
.getSourceManager().getExpansionLoc(
394 tsi
->getTypeLoc().getBeginLoc()) };
396 compiler
.getSourceManager().getExpansionLoc(
397 tsi
->getTypeLoc().getEndLoc()) };
398 assert(l
.isFileID() && end
.isFileID());
400 || compiler
.getSourceManager().isBeforeInTranslationUnit(
404 unsigned n
= Lexer::MeasureTokenLength(
405 l
, compiler
.getSourceManager(),
406 compiler
.getLangOpts());
408 compiler
.getSourceManager().getCharacterData(l
),
410 if (s
== getName(fbk
)) {
417 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
421 if (!rewrite(loc
, fbk
)) {
423 DiagnosticsEngine::Warning
,
424 "FieldDecl, use \"bool\" instead of %0", loc
)
425 << decl
->getType().getLocalUnqualifiedType() << decl
->getSourceRange();
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()) {
435 SourceLocation loc
{ compat::getBeginLoc(decl
) };
436 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
437 if (tsi
!= nullptr) {
439 compiler
.getSourceManager().getExpansionLoc(
440 tsi
->getTypeLoc().getBeginLoc()) };
442 compiler
.getSourceManager().getExpansionLoc(
443 tsi
->getTypeLoc().getEndLoc()) };
444 assert(l
.isFileID() && end
.isFileID());
446 || (compiler
.getSourceManager()
447 .isBeforeInTranslationUnit(l
, end
)))
450 unsigned n
= Lexer::MeasureTokenLength(
451 l
, compiler
.getSourceManager(),
452 compiler
.getLangOpts());
454 compiler
.getSourceManager().getCharacterData(l
),
456 if (s
== getName(fbk
)) {
463 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
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
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
)))
490 DiagnosticsEngine::Warning
,
491 ("ParmVarDecl, use \"bool\" instead of"
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')")
500 << decl
->getSourceRange();
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()) {
509 SourceLocation loc
{ compat::getBeginLoc(decl
) };
510 SourceLocation l
{ compiler
.getSourceManager().getExpansionLoc(
512 SourceLocation end
{ compiler
.getSourceManager().getExpansionLoc(
513 decl
->getNameInfo().getLoc()) };
514 assert(l
.isFileID() && end
.isFileID());
515 if (compiler
.getSourceManager().isBeforeInTranslationUnit(l
, end
)) {
517 unsigned n
= Lexer::MeasureTokenLength(
518 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
520 compiler
.getSourceManager().getCharacterData(l
), n
};
521 if (s
== getName(fbk
)) {
525 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
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
534 if (!((compiler
.getSourceManager().isInMainFile(
535 compiler
.getSourceManager().getSpellingLoc(
536 decl
->getNameInfo().getLoc()))
537 || f
->isDefined() || f
->isPure())
538 && rewrite(loc
, fbk
)))
541 DiagnosticsEngine::Warning
,
542 "use \"bool\" instead of %0 as return type%1",
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"
550 << decl
->getSourceRange();
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());
565 } else if (auto const e3
= dyn_cast
<MemberExpr
>(e1
)) {
566 if (auto const d
= dyn_cast
<FieldDecl
>(e3
->getMemberDecl())) {
567 fieldDecls_
.erase(d
);
574 bool FakeBool::VisitCallExpr(CallExpr
* expr
) {
575 Decl
const * d
= expr
->getCalleeDecl();
576 FunctionProtoType
const * ft
= nullptr;
578 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
580 if (!hasBoolOverload(fd
, false)) {
581 clang::PointerType
const * pt
= fd
->getType()
582 ->getAs
<clang::PointerType
>();
584 pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
585 ft
= t2
->getAs
<FunctionProtoType
>();
587 ft
!= nullptr || !compiler
.getLangOpts().CPlusPlus
588 || (fd
->getBuiltinID() != Builtin::NotBuiltin
589 && isa
<FunctionNoProtoType
>(t2
)));
590 // __builtin_*s have no proto type?
593 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
595 clang::PointerType
const * pt
= vd
->getType()
596 ->getAs
<clang::PointerType
>();
597 ft
= (pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
598 ->getAs
<FunctionProtoType
>();
603 for (unsigned i
= 0; i
!= ft
->getNumParams(); ++i
) {
604 QualType
t(ft
->getParamType(i
));
606 if (t
->isLValueReferenceType()) {
607 t
= t
.getNonReferenceType();
608 b
= !t
.isConstQualified() && isFakeBool(t
) != FBK_No
;
609 } else if (t
->isPointerType()) {
611 auto t2
= t
->getAs
<clang::PointerType
>();
615 t
= t2
->getPointeeType();
617 b
= isFakeBool(t
) != FBK_No
;
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());
626 } else if (auto const e2
= dyn_cast
<MemberExpr
>(e1
)) {
627 if (auto const d
= dyn_cast
<FieldDecl
>(e2
->getMemberDecl())) {
628 fieldDecls_
.erase(d
);
637 bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr
* expr
) {
638 if (ignoreLocation(expr
)) {
641 auto const k
= isFakeBool(expr
->getType());
643 SourceLocation loc
{ compat::getBeginLoc(expr
) };
644 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
645 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
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"))
663 // Ignore sal_False/True that are directly used as
664 // arguments to CPPUNIT_ASSERT_EQUAL:
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());
675 compiler
.getSourceManager().getCharacterData(
681 callSpellLoc
, n
, b
? "true" : "false");
685 DiagnosticsEngine::Warning
,
686 "use '%select{false|true}0' instead of '%1'", callLoc
)
687 << b
<< name
<< expr
->getSourceRange();
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();
702 bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
) {
703 if (ignoreLocation(expr
)) {
706 auto const k
= isFakeBool(expr
->getType());
710 if (k
== FBK_sal_Bool
711 && isInSpecialMainFile(
712 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr
))))
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();
725 bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
) {
726 if (ignoreLocation(expr
)) {
729 if (isFakeBool(expr
->getType()) != FBK_No
) {
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();
740 bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr
* expr
) {
741 FunctionAddress::VisitImplicitCastExpr(expr
);
742 if (ignoreLocation(expr
)) {
745 auto const k
= isFakeBool(expr
->getType());
749 auto l
= compat::getBeginLoc(expr
);
750 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
751 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
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")))
764 auto e1
= expr
->getSubExprAsWritten();
765 auto t
= e1
->getType();
766 if (!t
->isFundamentalType() || loplugin::TypeCheck(t
).AnyBoolean()) {
769 auto e2
= dyn_cast
<ConditionalOperator
>(e1
);
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())
779 && ic2
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
780 && (loplugin::TypeCheck(ic2
->getSubExprAsWritten()->getType())
787 DiagnosticsEngine::Warning
, "conversion from %0 to %1",
788 compat::getBeginLoc(expr
))
789 << t
<< expr
->getType() << expr
->getSourceRange();
793 bool FakeBool::VisitReturnStmt(ReturnStmt
const * stmt
) {
794 // Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
797 // static sal_Bool const aCharClass[][nCharClassSize] = ...;
801 // return aCharClass[eCharClass];
803 if (ignoreLocation(stmt
)) {
806 auto e
= stmt
->getRetValue();
810 auto t
= e
->getType();
811 if (!t
->isPointerType()) {
815 auto t2
= t
->getAs
<clang::PointerType
>();
819 t
= t2
->getPointeeType();
821 if (isFakeBool(t
) != FBK_sal_Bool
) {
824 auto e2
= dyn_cast
<ArraySubscriptExpr
>(e
->IgnoreParenImpCasts());
828 auto e3
= dyn_cast
<DeclRefExpr
>(e2
->getBase()->IgnoreParenImpCasts());
832 auto d
= dyn_cast
<VarDecl
>(e3
->getDecl());
840 bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl
const * decl
) {
841 return VisitParmVarDecl(decl
);
844 bool FakeBool::VisitParmVarDecl(ParmVarDecl
const * decl
) {
845 if (ignoreLocation(decl
)) {
848 auto const fbk
= isFakeBool(decl
->getType().getNonReferenceType());
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)))
861 OverrideKind k
= getOverrideKind(f
);
862 if (k
!= OverrideKind::YES
) {
863 parmVarDecls_
.insert({decl
, fbk
});
871 bool FakeBool::WalkUpFromVarDecl(VarDecl
const * decl
) {
872 return VisitVarDecl(decl
);
875 bool FakeBool::VisitVarDecl(VarDecl
const * decl
) {
876 if (ignoreLocation(decl
)) {
879 if (decl
->isExternC()) {
882 auto k
= isFakeBool(decl
->getType());
884 k
= isFakeBoolArray(decl
->getType());
889 auto const loc
= compat::getBeginLoc(decl
);
890 if (k
== FBK_sal_Bool
891 && isInSpecialMainFile(
892 compiler
.getSourceManager().getSpellingLoc(loc
)))
897 while (compiler
.getSourceManager().isMacroArgExpansion(l
)) {
898 l
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l
);
900 if (compiler
.getSourceManager().isMacroBodyExpansion(l
)
901 && isSharedCAndCppCode(compiler
.getSourceManager().getImmediateMacroCallerLoc(l
)))
905 varDecls_
.insert({decl
, k
});
909 bool FakeBool::WalkUpFromFieldDecl(FieldDecl
const * decl
) {
910 return VisitFieldDecl(decl
);
913 bool FakeBool::VisitFieldDecl(FieldDecl
const * decl
) {
914 if (ignoreLocation(decl
)) {
917 auto k
= isFakeBool(decl
->getType());
919 k
= isFakeBoolArray(decl
->getType());
924 if (!isAllRelevantCodeDefined(decl
)) {
927 if (k
== FBK_sal_Bool
928 && isInSpecialMainFile(
929 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl
))))
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()))))
940 fieldDecls_
.insert({decl
, k
});
945 bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl
const * decl
) {
946 return VisitFunctionDecl(decl
);
949 bool FakeBool::VisitFunctionDecl(FunctionDecl
const * decl
) {
950 if (ignoreLocation(decl
)) {
953 auto const fbk
= isFakeBool(decl
->getReturnType().getNonReferenceType());
955 && !(decl
->isDeletedAsWritten() && isa
<CXXConversionDecl
>(decl
))
956 && isAllRelevantCodeDefined(decl
))
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
>()))))
965 functionDecls_
.insert({decl
, fbk
});
971 bool FakeBool::VisitValueDecl(ValueDecl
const * decl
) {
972 if (ignoreLocation(decl
)) {
975 auto const k
= isFakeBool(decl
->getType());
976 if (k
!= FBK_No
&& !rewrite(compat::getBeginLoc(decl
), k
)) {
978 DiagnosticsEngine::Warning
,
979 "ValueDecl, use \"bool\" instead of %0",
980 compat::getBeginLoc(decl
))
981 << decl
->getType() << decl
->getSourceRange();
986 bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl
* decl
) {
987 // Ignore special code like
989 // static_cast<sal_Bool>(true) == sal_True
991 // inside static_assert in cppu/source/uno/check.cxx:
993 loplugin::isSamePathname(
994 getFilenameOfLocation(decl
->getLocation()),
995 SRCDIR
"/cppu/source/uno/check.cxx")
996 || RecursiveASTVisitor::TraverseStaticAssertDecl(decl
);
999 bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
) {
1000 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
1002 bool ret
= RecursiveASTVisitor::TraverseLinkageSpecDecl(decl
);
1003 assert(externCContexts_
!= 0);
1008 bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation
) const {
1009 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
1011 compiler
.getSourceManager().getPresumedLoc(spellingLocation
)
1016 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:
1021 isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
1022 && (externCContexts_
!= 0
1023 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
1026 bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation
) const {
1027 if (!compiler
.getSourceManager().isInMainFile(spellingLocation
)) {
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
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(
1041 unsigned n
= Lexer::MeasureTokenLength(
1042 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
1043 if (std::string(compiler
.getSourceManager().getCharacterData(loc
), n
)
1046 return replaceText(loc
, n
, "bool");
1052 loplugin::Plugin::Registration
<FakeBool
> X("fakebool", true);
1056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */