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/.
15 #include "clang/AST/Attr.h"
22 bool isSalBool(QualType type
) {
23 TypedefType
const * t
= type
->getAs
<TypedefType
>();
24 return t
!= nullptr && t
->getDecl()->getNameAsString() == "sal_Bool";
27 // Clang 3.2 FunctionDecl::isInlined doesn't work as advertised ("Determine
28 // whether this function should be inlined, because it is either marked 'inline'
29 // or 'constexpr' or is a member function of a class that was defined in the
30 // class body.") but mis-classifies salhelper::Timer's isTicking, isExpired, and
31 // expiresBefore members as defined in salhelper/source/timer.cxx as inlined:
32 bool isInlined(FunctionDecl
const & decl
) {
33 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
34 return decl
.isInlined();
41 // It appears that, given a function declaration, there is no way to determine
42 // the language linkage of the function's type, only of the function's name
43 // (via FunctionDecl::isExternC); however, in a case like
45 // extern "C" { static void f(); }
47 // the function's name does not have C language linkage while the function's
48 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
49 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
50 // "Language linkage of function type":
51 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
52 assert(decl
!= nullptr);
53 if (decl
->isExternC()) {
56 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
57 if (decl
->isInExternCContext()) {
61 if (decl
->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
68 enum class OverrideKind
{ NO
, YES
, MAYBE
};
70 OverrideKind
getOverrideKind(FunctionDecl
const * decl
) {
71 CXXMethodDecl
const * m
= dyn_cast
<CXXMethodDecl
>(decl
);
73 return OverrideKind::NO
;
75 if (m
->size_overridden_methods() != 0 || m
->hasAttr
<OverrideAttr
>()) {
76 return OverrideKind::YES
;
78 if (!dyn_cast
<CXXRecordDecl
>(m
->getDeclContext())->hasAnyDependentBases()) {
79 return OverrideKind::NO
;
81 return OverrideKind::MAYBE
;
84 //TODO: current implementation is not at all general, just tests what we
85 // encounter in practice:
86 bool hasBoolOverload(FunctionDecl
const * decl
, bool mustBeDeleted
) {
87 unsigned n
= decl
->getNumParams();
88 auto res
= decl
->getDeclContext()->lookup(decl
->getDeclName());
89 for (auto d
= compat::begin(res
); d
!= compat::end(res
); ++d
) {
90 FunctionDecl
const * f
= dyn_cast
<FunctionDecl
>(*d
);
91 if (f
!= nullptr && (!mustBeDeleted
|| f
->isDeleted())) {
92 if (f
->getNumParams() == n
) {
94 for (unsigned i
= 0; i
!= n
; ++i
) {
95 QualType t1
{ decl
->getParamDecl(i
)->getType() };
96 bool isSB
= isSalBool(t1
);
97 bool isSBRef
= !isSB
&& t1
->isReferenceType()
98 && isSalBool(t1
.getNonReferenceType());
99 QualType t2
{ f
->getParamDecl(i
)->getType() };
101 ? t2
->isBooleanType()
103 ? (t2
->isReferenceType()
104 && t2
.getNonReferenceType()->isBooleanType())
109 hasSB
|= isSB
|| isSBRef
;
112 // cheaply protect against the case where decl would have no
113 // sal_Bool parameters at all and would match itself
122 public RecursiveASTVisitor
<SalBool
>, public loplugin::RewritePlugin
125 explicit SalBool(InstantiationData
const & data
): RewritePlugin(data
) {}
127 virtual void run() override
;
129 bool VisitUnaryAddrOf(UnaryOperator
const * op
);
131 bool VisitCallExpr(CallExpr
* expr
);
133 bool VisitCStyleCastExpr(CStyleCastExpr
* expr
);
135 bool VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
);
137 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
);
139 bool WalkUpFromParmVarDecl(ParmVarDecl
const * decl
);
140 bool VisitParmVarDecl(ParmVarDecl
const * decl
);
142 bool WalkUpFromVarDecl(VarDecl
const * decl
);
143 bool VisitVarDecl(VarDecl
const * decl
);
145 bool WalkUpFromFieldDecl(FieldDecl
const * decl
);
146 bool VisitFieldDecl(FieldDecl
const * decl
);
148 bool WalkUpFromFunctionDecl(FunctionDecl
const * decl
);
149 bool VisitFunctionDecl(FunctionDecl
const * decl
);
151 bool VisitValueDecl(ValueDecl
const * decl
);
154 bool isInSpecialMainFile(SourceLocation spellingLocation
) const;
156 bool rewrite(SourceLocation location
);
158 std::set
<VarDecl
const *> varDecls_
;
161 void SalBool::run() {
162 if (compiler
.getLangOpts().CPlusPlus
) {
163 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
164 for (auto decl
: varDecls_
) {
165 SourceLocation loc
{ decl
->getLocStart() };
166 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
167 if (tsi
!= nullptr) {
169 compiler
.getSourceManager().getExpansionLoc(
170 tsi
->getTypeLoc().getBeginLoc()) };
172 compiler
.getSourceManager().getExpansionLoc(
173 tsi
->getTypeLoc().getEndLoc()) };
174 assert(l
.isFileID() && end
.isFileID());
176 || compiler
.getSourceManager().isBeforeInTranslationUnit(
180 unsigned n
= Lexer::MeasureTokenLength(
181 l
, compiler
.getSourceManager(),
182 compiler
.getLangOpts());
184 compiler
.getSourceManager().getCharacterData(l
),
186 if (s
== "sal_Bool") {
193 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
199 DiagnosticsEngine::Warning
,
200 "VarDecl, use \"bool\" instead of \"sal_Bool\"", loc
)
201 << decl
->getSourceRange();
207 bool SalBool::VisitUnaryAddrOf(UnaryOperator
const * op
) {
208 Expr
const * e1
= op
->getSubExpr()->IgnoreParenCasts();
209 if (isSalBool(e1
->getType())) {
210 DeclRefExpr
const * e2
= dyn_cast
<DeclRefExpr
>(e1
);
212 VarDecl
const * d
= dyn_cast
<VarDecl
>(e2
->getDecl());
221 bool SalBool::VisitCallExpr(CallExpr
* expr
) {
222 Decl
const * d
= expr
->getCalleeDecl();
223 FunctionProtoType
const * ft
= nullptr;
225 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
227 PointerType
const * pt
= fd
->getType()->getAs
<PointerType
>();
228 QualType
t2(pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
229 ft
= t2
->getAs
<FunctionProtoType
>();
231 ft
!= nullptr || !compiler
.getLangOpts().CPlusPlus
232 || (fd
->getBuiltinID() != Builtin::NotBuiltin
233 && isa
<FunctionNoProtoType
>(t2
)));
234 // __builtin_*s have no proto type?
236 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
238 PointerType
const * pt
= vd
->getType()->getAs
<PointerType
>();
239 ft
= (pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
240 ->getAs
<FunctionProtoType
>();
245 for (unsigned i
= 0; i
!= compat::getNumParams(*ft
); ++i
) {
246 QualType
t(compat::getParamType(*ft
, i
));
247 if (t
->isLValueReferenceType()) {
248 t
= t
.getNonReferenceType();
249 if (!t
.isConstQualified() && isSalBool(t
)
250 && i
< expr
->getNumArgs())
252 DeclRefExpr
* ref
= dyn_cast
<DeclRefExpr
>(expr
->getArg(i
));
253 if (ref
!= nullptr) {
254 VarDecl
const * d
= dyn_cast
<VarDecl
>(ref
->getDecl());
266 bool SalBool::VisitCStyleCastExpr(CStyleCastExpr
* expr
) {
267 if (ignoreLocation(expr
)) {
270 if (isSalBool(expr
->getType())) {
271 SourceLocation loc
{ expr
->getLocStart() };
272 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
273 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
275 if (compat::isMacroBodyExpansion(compiler
, loc
)) {
276 StringRef name
{ Lexer::getImmediateMacroName(
277 loc
, compiler
.getSourceManager(), compiler
.getLangOpts()) };
278 if (name
== "sal_False" || name
== "sal_True") {
283 DiagnosticsEngine::Warning
,
284 "CStyleCastExpr, suspicious cast from %0 to %1",
286 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
287 << expr
->getType() << expr
->getSourceRange();
292 bool SalBool::VisitCXXStaticCastExpr(CXXStaticCastExpr
* expr
) {
293 if (ignoreLocation(expr
)) {
296 if (isSalBool(expr
->getType())) {
298 DiagnosticsEngine::Warning
,
299 "CStyleCastExpr, suspicious cast from %0 to %1",
301 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
302 << expr
->getType() << expr
->getSourceRange();
307 bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
) {
308 if (ignoreLocation(expr
)) {
311 if (isSalBool(expr
->getType())) {
313 DiagnosticsEngine::Warning
,
314 "CStyleCastExpr, suspicious cast from %0 to %1",
316 << expr
->getSubExpr()->IgnoreParenImpCasts()->getType()
317 << expr
->getType() << expr
->getSourceRange();
322 bool SalBool::WalkUpFromParmVarDecl(ParmVarDecl
const * decl
) {
323 return VisitParmVarDecl(decl
);
326 bool SalBool::VisitParmVarDecl(ParmVarDecl
const * decl
) {
327 if (ignoreLocation(decl
)) {
330 if (isSalBool(decl
->getType().getNonReferenceType())) {
331 FunctionDecl
const * f
= dyn_cast
<FunctionDecl
>(decl
->getDeclContext());
332 if (f
!= nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
333 f
= f
->getCanonicalDecl();
334 if (!(hasCLanguageLinkageType(f
)
335 || (isInUnoIncludeFile(
336 compiler
.getSourceManager().getSpellingLoc(
337 f
->getNameInfo().getLoc()))
338 && (!isInlined(*f
) || f
->hasAttr
<DeprecatedAttr
>()
339 || decl
->getType()->isReferenceType()
340 || hasBoolOverload(f
, false)))
341 || f
->isDeleted() || hasBoolOverload(f
, true)))
343 OverrideKind k
= getOverrideKind(f
);
344 if (k
!= OverrideKind::YES
) {
345 SourceLocation loc
{ decl
->getLocStart() };
346 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
347 if (tsi
!= nullptr) {
349 compiler
.getSourceManager().getExpansionLoc(
350 tsi
->getTypeLoc().getBeginLoc()) };
352 compiler
.getSourceManager().getExpansionLoc(
353 tsi
->getTypeLoc().getEndLoc()) };
354 assert(l
.isFileID() && end
.isFileID());
356 || (compiler
.getSourceManager()
357 .isBeforeInTranslationUnit(l
, end
)))
360 unsigned n
= Lexer::MeasureTokenLength(
361 l
, compiler
.getSourceManager(),
362 compiler
.getLangOpts());
364 compiler
.getSourceManager().getCharacterData(l
),
366 if (s
== "sal_Bool") {
373 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
377 // Only rewrite declarations in include files if a
378 // definition is also seen, to avoid compilation of a
379 // definition (in a main file only processed later) to fail
380 // with a "mismatch" error before the rewriter had a chance
381 // to act upon the definition (but use the heuristic of
382 // assuming pure virtual functions do not have definitions);
383 // also, do not automatically rewrite functions that could
384 // implicitly override depend base functions (and thus stop
385 // doing so after the rewrite; note that this is less
386 // dangerous for return types than for parameter types,
387 // where the function would still implicitly override and
388 // cause a compilation error due to the incompatible return
390 if (!((compat::isInMainFile(
391 compiler
.getSourceManager(),
392 compiler
.getSourceManager().getSpellingLoc(
393 dyn_cast
<FunctionDecl
>(
394 decl
->getDeclContext())
395 ->getNameInfo().getLoc()))
396 || f
->isDefined() || f
->isPure())
397 && k
!= OverrideKind::MAYBE
&& rewrite(loc
)))
400 DiagnosticsEngine::Warning
,
401 ("ParmVarDecl, use \"bool\" instead of"
404 << (k
== OverrideKind::MAYBE
405 ? (" (unless this member function overrides a"
406 " dependent base member function, even"
407 " though it is not marked 'override')")
409 << decl
->getSourceRange();
418 bool SalBool::WalkUpFromVarDecl(VarDecl
const * decl
) {
419 return VisitVarDecl(decl
);
422 bool SalBool::VisitVarDecl(VarDecl
const * decl
) {
423 if (ignoreLocation(decl
)) {
426 if (!decl
->isExternC() && isSalBool(decl
->getType())
427 && !isInSpecialMainFile(
428 compiler
.getSourceManager().getSpellingLoc(decl
->getLocStart())))
430 varDecls_
.insert(decl
);
435 bool SalBool::WalkUpFromFieldDecl(FieldDecl
const * decl
) {
436 return VisitFieldDecl(decl
);
439 bool SalBool::VisitFieldDecl(FieldDecl
const * decl
) {
440 if (ignoreLocation(decl
)) {
443 if (isSalBool(decl
->getType())) {
444 TagDecl
const * td
= dyn_cast
<TagDecl
>(decl
->getDeclContext());
445 assert(td
!= nullptr);
446 if (!(((td
->isStruct() || td
->isUnion())
447 && compat::isExternCContext(*td
))
448 || isInUnoIncludeFile(
449 compiler
.getSourceManager().getSpellingLoc(
450 decl
->getLocation()))))
452 SourceLocation loc
{ decl
->getLocStart() };
453 TypeSourceInfo
* tsi
= decl
->getTypeSourceInfo();
454 if (tsi
!= nullptr) {
456 compiler
.getSourceManager().getExpansionLoc(
457 tsi
->getTypeLoc().getBeginLoc()) };
459 compiler
.getSourceManager().getExpansionLoc(
460 tsi
->getTypeLoc().getEndLoc()) };
461 assert(l
.isFileID() && end
.isFileID());
463 || compiler
.getSourceManager().isBeforeInTranslationUnit(
467 unsigned n
= Lexer::MeasureTokenLength(
468 l
, compiler
.getSourceManager(),
469 compiler
.getLangOpts());
471 compiler
.getSourceManager().getCharacterData(l
),
473 if (s
== "sal_Bool") {
480 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
486 DiagnosticsEngine::Warning
,
487 "FieldDecl, use \"bool\" instead of \"sal_Bool\"", loc
)
488 << decl
->getSourceRange();
495 bool SalBool::WalkUpFromFunctionDecl(FunctionDecl
const * decl
) {
496 return VisitFunctionDecl(decl
);
499 bool SalBool::VisitFunctionDecl(FunctionDecl
const * decl
) {
500 if (ignoreLocation(decl
)) {
503 if (isSalBool(compat::getReturnType(*decl
).getNonReferenceType())) {
504 FunctionDecl
const * f
= decl
->getCanonicalDecl();
505 OverrideKind k
= getOverrideKind(f
);
506 if (k
!= OverrideKind::YES
507 && !(hasCLanguageLinkageType(f
)
508 || (isInUnoIncludeFile(
509 compiler
.getSourceManager().getSpellingLoc(
510 f
->getNameInfo().getLoc()))
511 && (!isInlined(*f
) || f
->hasAttr
<DeprecatedAttr
>()))))
513 SourceLocation loc
{ decl
->getLocStart() };
514 SourceLocation l
{ compiler
.getSourceManager().getExpansionLoc(
516 SourceLocation end
{ compiler
.getSourceManager().getExpansionLoc(
517 decl
->getNameInfo().getLoc()) };
518 assert(l
.isFileID() && end
.isFileID());
519 if (compiler
.getSourceManager().isBeforeInTranslationUnit(l
, end
)) {
521 unsigned n
= Lexer::MeasureTokenLength(
522 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
524 compiler
.getSourceManager().getCharacterData(l
), n
};
525 if (s
== "sal_Bool") {
529 l
= l
.getLocWithOffset(std::max
<unsigned>(n
, 1));
532 // Only rewrite declarations in include files if a definition is
533 // also seen, to avoid compilation of a definition (in a main file
534 // only processed later) to fail with a "mismatch" error before the
535 // rewriter had a chance to act upon the definition (but use the
536 // heuristic of assuming pure virtual functions do not have
538 if (!((compat::isInMainFile(
539 compiler
.getSourceManager(),
540 compiler
.getSourceManager().getSpellingLoc(
541 decl
->getNameInfo().getLoc()))
542 || f
->isDefined() || f
->isPure())
546 DiagnosticsEngine::Warning
,
547 "use \"bool\" instead of \"sal_Bool\" as return type%0",
549 << (k
== OverrideKind::MAYBE
550 ? (" (unless this member function overrides a dependent"
551 " base member function, even though it is not marked"
554 << decl
->getSourceRange();
561 bool SalBool::VisitValueDecl(ValueDecl
const * decl
) {
562 if (ignoreLocation(decl
)) {
565 if (isSalBool(decl
->getType()) && !rewrite(decl
->getLocStart())) {
567 DiagnosticsEngine::Warning
,
568 "ValueDecl, use \"bool\" instead of \"sal_Bool\"",
570 << decl
->getSourceRange();
575 bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation
) const {
576 return compat::isInMainFile(compiler
.getSourceManager(), spellingLocation
)
577 && (compiler
.getSourceManager().getFilename(spellingLocation
)
578 == SRCDIR
"/cppu/qa/test_any.cxx");
581 bool SalBool::rewrite(SourceLocation location
) {
582 if (rewriter
!= nullptr) {
583 //TODO: "::sal_Bool" -> "bool", not "::bool"
584 SourceLocation loc
{ compiler
.getSourceManager().getExpansionLoc(
586 unsigned n
= Lexer::MeasureTokenLength(
587 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
588 if (std::string(compiler
.getSourceManager().getCharacterData(loc
), n
)
591 return replaceText(loc
, n
, "bool");
597 loplugin::Plugin::Registration
<SalBool
> X("salbool", true);
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */