Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / literaltoboolconversion.cxx
blob9894daec51b6980cb97575ece8df53b4fd39c8b3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
11 #include <cassert>
12 #include <limits>
14 #include "clang/Lex/Lexer.h"
16 #include "compat.hxx"
17 #include "plugin.hxx"
19 namespace {
21 class LiteralToBoolConversion:
22 public loplugin::FilteringRewritePlugin<LiteralToBoolConversion>
24 public:
25 explicit LiteralToBoolConversion(loplugin::InstantiationData const & data):
26 FilteringRewritePlugin(data) {}
28 virtual void run() override
29 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
31 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
33 bool PreTraverseLinkageSpecDecl(LinkageSpecDecl * decl);
34 bool PostTraverseLinkageSpecDecl(LinkageSpecDecl * decl, bool);
35 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
37 private:
38 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
40 bool isSharedCAndCppCode(SourceLocation location) const;
42 void handleImplicitCastSubExpr(
43 ImplicitCastExpr const * castExpr, Expr const * subExpr);
45 unsigned int externCContexts_ = 0;
48 bool LiteralToBoolConversion::VisitImplicitCastExpr(
49 ImplicitCastExpr const * expr)
51 if (ignoreLocation(expr)) {
52 return true;
54 if (!expr->getType()->isBooleanType()) {
55 return true;
57 handleImplicitCastSubExpr(expr, expr->getSubExpr());
58 return true;
61 bool LiteralToBoolConversion::PreTraverseLinkageSpecDecl(LinkageSpecDecl *) {
62 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
63 ++externCContexts_;
64 return true;
67 bool LiteralToBoolConversion::PostTraverseLinkageSpecDecl(LinkageSpecDecl *, bool) {
68 assert(externCContexts_ != 0);
69 --externCContexts_;
70 return true;
73 bool LiteralToBoolConversion::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
74 PreTraverseLinkageSpecDecl(decl);
75 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
76 PostTraverseLinkageSpecDecl(decl, ret);
77 return ret;
80 bool LiteralToBoolConversion::isFromCIncludeFile(
81 SourceLocation spellingLocation) const
83 return !compiler.getSourceManager().isInMainFile(spellingLocation)
84 && (StringRef(
85 compiler.getSourceManager().getPresumedLoc(spellingLocation)
86 .getFilename())
87 .endswith(".h"));
90 bool LiteralToBoolConversion::isSharedCAndCppCode(SourceLocation location) const
92 // Assume that code is intended to be shared between C and C++ if it comes
93 // from an include file ending in .h, and is either in an extern "C" context
94 // or the body of a macro definition:
95 return
96 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
97 && (externCContexts_ != 0
98 || compiler.getSourceManager().isMacroBodyExpansion(location));
101 void LiteralToBoolConversion::handleImplicitCastSubExpr(
102 ImplicitCastExpr const * castExpr, Expr const * subExpr)
104 Expr const * expr2 = subExpr;
105 // track sub-expr with potential parens, to e.g. rewrite all of expanded
107 // #define sal_False ((sal_Bool)0)
109 // including the parens
110 subExpr = expr2->IgnoreParenCasts();
111 for (;;) {
112 BinaryOperator const * op = dyn_cast<BinaryOperator>(subExpr);
113 if (op == nullptr || op->getOpcode() != BO_Comma) {
114 break;
116 expr2 = op->getRHS();
117 subExpr = expr2->IgnoreParenCasts();
119 if (subExpr->getType()->isBooleanType()) {
120 return;
122 ConditionalOperator const * op = dyn_cast<ConditionalOperator>(subExpr);
123 if (op != nullptr) {
124 handleImplicitCastSubExpr(castExpr, op->getTrueExpr());
125 handleImplicitCastSubExpr(castExpr, op->getFalseExpr());
126 return;
128 if (!subExpr->isValueDependent()) {
129 if (auto const res = subExpr->getIntegerConstantExpr(compiler.getASTContext())) {
130 if (res->getLimitedValue() <= 1)
132 SourceLocation loc { subExpr->getBeginLoc() };
133 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
134 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
136 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
137 StringRef name { Lexer::getImmediateMacroName(
138 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
139 if (name == "sal_False" || name == "sal_True") {
140 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc)
141 .first;
143 if (isSharedCAndCppCode(loc)) {
144 return;
150 if (isa<clang::StringLiteral>(subExpr)) {
151 SourceLocation loc { subExpr->getBeginLoc() };
152 if (compiler.getSourceManager().isMacroArgExpansion(loc)
153 && (Lexer::getImmediateMacroName(
154 loc, compiler.getSourceManager(), compiler.getLangOpts())
155 == "assert"))
157 return;
160 if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
161 || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
162 || isa<clang::StringLiteral>(subExpr))
164 bool bRewritten = false;
165 if (rewriter != nullptr) {
166 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
167 expr2->getBeginLoc()) };
168 if (compiler.getSourceManager().getExpansionLoc(expr2->getEndLoc())
169 == loc)
171 char const * s = compiler.getSourceManager().getCharacterData(
172 loc);
173 unsigned n = Lexer::MeasureTokenLength(
174 expr2->getEndLoc(), compiler.getSourceManager(),
175 compiler.getLangOpts());
176 std::string tok { s, n };
177 if (tok == "sal_False" || tok == "0") {
178 bRewritten = replaceText(
179 compiler.getSourceManager().getExpansionLoc(
180 expr2->getBeginLoc()),
181 n, "false");
182 } else if (tok == "sal_True" || tok == "1") {
183 bRewritten = replaceText(
184 compiler.getSourceManager().getExpansionLoc(
185 expr2->getBeginLoc()),
186 n, "true");
190 if (!bRewritten) {
191 report(
192 DiagnosticsEngine::Warning,
193 "implicit conversion (%0) of literal of type %1 to %2",
194 expr2->getBeginLoc())
195 << castExpr->getCastKindName() << subExpr->getType()
196 << castExpr->getType() << expr2->getSourceRange();
198 } else if (subExpr->isNullPointerConstant(
199 compiler.getASTContext(), Expr::NPC_ValueDependentIsNull)
200 > Expr::NPCK_ZeroExpression)
202 // The test above originally checked for != Expr::NPCK_NotNull, but in non-C++11
203 // mode we can get also Expr::NPCK_ZeroExpression inside templates, even though
204 // the expression is actually not a null pointer. Clang bug or C++98 misfeature?
205 // See Clang's NPCK_ZeroExpression declaration and beginning of isNullPointerConstant().
206 static_assert( Expr::NPCK_NotNull == 0 && Expr::NPCK_ZeroExpression == 1, "Clang API change" );
207 report(
208 DiagnosticsEngine::Warning,
209 ("implicit conversion (%0) of null pointer constant of type %1 to"
210 " %2"),
211 expr2->getBeginLoc())
212 << castExpr->getCastKindName() << subExpr->getType()
213 << castExpr->getType() << expr2->getSourceRange();
214 } else if (!subExpr->isValueDependent()) {
215 if (auto const res = subExpr->getIntegerConstantExpr(compiler.getASTContext())) {
216 report(
217 DiagnosticsEngine::Warning,
218 ("implicit conversion (%0) of integer constant expression of type"
219 " %1 with value %2 to %3"),
220 expr2->getBeginLoc())
221 << castExpr->getCastKindName() << subExpr->getType()
222 << compat::toString(*res, 10) << castExpr->getType()
223 << expr2->getSourceRange();
228 loplugin::Plugin::Registration<LiteralToBoolConversion> literaltoboolconversion("literaltoboolconversion");
230 } // namespace
232 #endif // LO_CLANG_SHARED_PLUGINS