1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 //TODO: Make this a shared plugin for Clang 12 (and possibly even for older Clang) again.
16 #include "clang/Basic/Builtins.h"
22 // Find implicit conversions from non-'bool' constants (e.g., 'sal_False') to 'bool'.
26 class ConstToBool final
: public loplugin::FilteringPlugin
<ConstToBool
>
29 explicit ConstToBool(loplugin::InstantiationData
const& data
)
30 : FilteringPlugin(data
)
34 bool PreTraverseLinkageSpecDecl(LinkageSpecDecl
*)
36 assert(externCContexts_
!= std::numeric_limits
<unsigned int>::max()); //TODO
41 bool PostTraverseLinkageSpecDecl(LinkageSpecDecl
*, bool)
43 assert(externCContexts_
!= 0);
48 bool TraverseLinkageSpecDecl(LinkageSpecDecl
* decl
)
51 if (PreTraverseLinkageSpecDecl(decl
))
53 ret
= FilteringPlugin::TraverseLinkageSpecDecl(decl
);
54 PostTraverseLinkageSpecDecl(decl
, ret
);
59 bool PreTraverseUnaryOperator(UnaryOperator
* expr
)
61 if (expr
->getOpcode() == UO_LNot
)
63 ignoredInAssert_
.push(expr
->getSubExpr());
68 bool PostTraverseUnaryOperator(UnaryOperator
* expr
, bool)
70 if (expr
->getOpcode() == UO_LNot
)
72 assert(!ignoredInAssert_
.empty());
73 ignoredInAssert_
.pop();
78 bool TraverseUnaryOperator(UnaryOperator
* expr
)
81 if (PreTraverseUnaryOperator(expr
))
83 ret
= FilteringPlugin::TraverseUnaryOperator(expr
);
84 PostTraverseUnaryOperator(expr
, ret
);
89 bool PreTraverseBinaryOperator(BinaryOperator
* expr
)
91 if (expr
->getOpcode() == BO_LAnd
)
93 ignoredInAssert_
.push(expr
->getRHS());
98 bool PostTraverseBinaryOperator(BinaryOperator
* expr
, bool)
100 if (expr
->getOpcode() == BO_LAnd
)
102 assert(!ignoredInAssert_
.empty());
103 ignoredInAssert_
.pop();
108 bool TraverseBinaryOperator(BinaryOperator
* expr
)
111 if (PreTraverseBinaryOperator(expr
))
113 ret
= FilteringPlugin::TraverseBinaryOperator(expr
);
114 PostTraverseBinaryOperator(expr
, ret
);
119 bool VisitImplicitCastExpr(ImplicitCastExpr
const* expr
)
121 if (ignoreLocation(expr
))
125 if (!expr
->getType()->isBooleanType())
129 auto const sub
= expr
->getSubExpr();
130 auto const t
= sub
->getType();
131 if (t
->isBooleanType())
135 if (sub
->isValueDependent())
140 if (!sub
->isCXX11ConstantExpr(compiler
.getASTContext(), &res
))
144 auto const l
= expr
->getExprLoc();
145 if (!ignoredInAssert_
.empty() && expr
== ignoredInAssert_
.top())
147 if (auto const e
= dyn_cast
<clang::StringLiteral
>(sub
->IgnoreParenImpCasts()))
149 if (compat::isOrdinary(e
)) // somewhat randomly restrict to plain literals
151 if (compiler
.getSourceManager().isMacroArgExpansion(l
)
152 && Lexer::getImmediateMacroName(l
, compiler
.getSourceManager(),
153 compiler
.getLangOpts())
156 //TODO: only ignore outermost '!"..."' or '... && "..."'
163 if (compiler
.getSourceManager().isMacroBodyExpansion(l1
))
165 auto const n
= Lexer::getImmediateMacroName(l1
, compiler
.getSourceManager(),
166 compiler
.getLangOpts());
167 if (n
== "FALSE" || n
== "TRUE" || n
== "sal_False" || n
== "sal_True")
169 l1
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l1
);
171 // For example, /usr/include/glib-2.0/glib/gmacros.h from
172 // glib2-devel-2.62.1-1.fc31.x86_64 has
174 // #define TRUE (!FALSE)
176 // so handle that wrapped macro body expansion, too:
177 if (compiler
.getSourceManager().isMacroBodyExpansion(l1
)
178 && Lexer::getImmediateMacroName(l1
, compiler
.getSourceManager(),
179 compiler
.getLangOpts())
182 l1
= compiler
.getSourceManager().getImmediateMacroCallerLoc(l1
);
185 if (isSharedCAndCppCode(l1
))
187 // Cover just enough cases to handle things like `while (0)` or the use of `sal_True` in
189 // #define OSL_FAIL(m) SAL_DETAIL_WARN_IF_FORMAT(sal_True, "legacy.osl", "%s", m)
191 // in include/osl/diagnose.h:
192 if (auto const t1
= t
->getAs
<BuiltinType
>())
194 if (t1
->getKind() == BuiltinType::Int
)
196 auto const& v
= res
.getInt();
197 if (v
== 0 || v
== 1)
203 if (loplugin::TypeCheck(t
).Typedef("sal_Bool").GlobalNamespace())
208 if (auto const e
= dyn_cast
<CallExpr
>(sub
->IgnoreParenImpCasts()))
210 // Ignore use of `long __builtin_expect(long, long)`, as found in the definition of
211 // `assert` on macOS:
212 if (e
->getBuiltinCallee() == Builtin::BI__builtin_expect
)
218 bool replacement
= {};
222 replacement
= res
.getInt() != 0;
224 else if (res
.isFloat())
227 replacement
= !res
.getFloat().isZero();
229 else if (res
.isNullPointer())
234 else if (res
.isLValue())
243 report(DiagnosticsEngine::Warning
,
244 "implicit conversion of constant %0 of type %1 to 'bool'%select{|; use "
245 "'%select{false|true}3' instead}2",
247 << res
.getAsString(compiler
.getASTContext(), t
) << t
<< suggestion
<< replacement
248 << expr
->getSourceRange();
252 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
255 std::stack
<Expr
const*> ignoredInAssert_
;
256 unsigned int externCContexts_
= 0;
262 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
266 bool isFromCIncludeFile(SourceLocation spellingLocation
) const
268 return !compiler
.getSourceManager().isInMainFile(spellingLocation
)
270 compiler
.getSourceManager().getPresumedLoc(spellingLocation
).getFilename())
274 bool isSharedCAndCppCode(SourceLocation location
) const
276 while (compiler
.getSourceManager().isMacroArgExpansion(location
))
278 location
= compiler
.getSourceManager().getImmediateMacroCallerLoc(location
);
280 // Assume that code is intended to be shared between C and C++ if it comes from an include
281 // file ending in .h, and is either in an extern "C" context or the body of a macro
283 return isFromCIncludeFile(compiler
.getSourceManager().getSpellingLoc(location
))
284 && (externCContexts_
!= 0
285 || compiler
.getSourceManager().isMacroBodyExpansion(location
));
289 loplugin::Plugin::Registration
<ConstToBool
> consttobool("consttobool");
292 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */