cid#1555694 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / compilerplugins / clang / literaltoboolconversion.cxx
blob3d3a3615ec68afda82ea80af4ea623f744b13bcf
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 && compat::ends_with(
85 StringRef(compiler.getSourceManager().getPresumedLoc(spellingLocation).getFilename()),
86 ".h");
89 bool LiteralToBoolConversion::isSharedCAndCppCode(SourceLocation location) const
91 // Assume that code is intended to be shared between C and C++ if it comes
92 // from an include file ending in .h, and is either in an extern "C" context
93 // or the body of a macro definition:
94 return
95 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
96 && (externCContexts_ != 0
97 || compiler.getSourceManager().isMacroBodyExpansion(location));
100 void LiteralToBoolConversion::handleImplicitCastSubExpr(
101 ImplicitCastExpr const * castExpr, Expr const * subExpr)
103 Expr const * expr2 = subExpr;
104 // track sub-expr with potential parens, to e.g. rewrite all of expanded
106 // #define sal_False ((sal_Bool)0)
108 // including the parens
109 subExpr = expr2->IgnoreParenCasts();
110 for (;;) {
111 BinaryOperator const * op = dyn_cast<BinaryOperator>(subExpr);
112 if (op == nullptr || op->getOpcode() != BO_Comma) {
113 break;
115 expr2 = op->getRHS();
116 subExpr = expr2->IgnoreParenCasts();
118 if (subExpr->getType()->isBooleanType()) {
119 return;
121 ConditionalOperator const * op = dyn_cast<ConditionalOperator>(subExpr);
122 if (op != nullptr) {
123 handleImplicitCastSubExpr(castExpr, op->getTrueExpr());
124 handleImplicitCastSubExpr(castExpr, op->getFalseExpr());
125 return;
127 if (!subExpr->isValueDependent()) {
128 if (auto const res = subExpr->getIntegerConstantExpr(compiler.getASTContext())) {
129 if (res->getLimitedValue() <= 1)
131 SourceLocation loc { subExpr->getBeginLoc() };
132 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
133 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
135 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
136 StringRef name { Lexer::getImmediateMacroName(
137 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
138 if (name == "sal_False" || name == "sal_True") {
139 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc)
140 .first;
142 if (isSharedCAndCppCode(loc)) {
143 return;
149 if (isa<clang::StringLiteral>(subExpr)) {
150 SourceLocation loc { subExpr->getBeginLoc() };
151 if (compiler.getSourceManager().isMacroArgExpansion(loc)
152 && (Lexer::getImmediateMacroName(
153 loc, compiler.getSourceManager(), compiler.getLangOpts())
154 == "assert"))
156 return;
159 if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
160 || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
161 || isa<clang::StringLiteral>(subExpr))
163 bool bRewritten = false;
164 if (rewriter != nullptr) {
165 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
166 expr2->getBeginLoc()) };
167 if (compiler.getSourceManager().getExpansionLoc(expr2->getEndLoc())
168 == loc)
170 char const * s = compiler.getSourceManager().getCharacterData(
171 loc);
172 unsigned n = Lexer::MeasureTokenLength(
173 expr2->getEndLoc(), compiler.getSourceManager(),
174 compiler.getLangOpts());
175 std::string tok { s, n };
176 if (tok == "sal_False" || tok == "0") {
177 bRewritten = replaceText(
178 compiler.getSourceManager().getExpansionLoc(
179 expr2->getBeginLoc()),
180 n, "false");
181 } else if (tok == "sal_True" || tok == "1") {
182 bRewritten = replaceText(
183 compiler.getSourceManager().getExpansionLoc(
184 expr2->getBeginLoc()),
185 n, "true");
189 if (!bRewritten) {
190 report(
191 DiagnosticsEngine::Warning,
192 "implicit conversion (%0) of literal of type %1 to %2",
193 expr2->getBeginLoc())
194 << castExpr->getCastKindName() << subExpr->getType()
195 << castExpr->getType() << expr2->getSourceRange();
197 } else if (subExpr->isNullPointerConstant(
198 compiler.getASTContext(), Expr::NPC_ValueDependentIsNull)
199 > Expr::NPCK_ZeroExpression)
201 // The test above originally checked for != Expr::NPCK_NotNull, but in non-C++11
202 // mode we can get also Expr::NPCK_ZeroExpression inside templates, even though
203 // the expression is actually not a null pointer. Clang bug or C++98 misfeature?
204 // See Clang's NPCK_ZeroExpression declaration and beginning of isNullPointerConstant().
205 static_assert( Expr::NPCK_NotNull == 0 && Expr::NPCK_ZeroExpression == 1, "Clang API change" );
206 report(
207 DiagnosticsEngine::Warning,
208 ("implicit conversion (%0) of null pointer constant of type %1 to"
209 " %2"),
210 expr2->getBeginLoc())
211 << castExpr->getCastKindName() << subExpr->getType()
212 << castExpr->getType() << expr2->getSourceRange();
213 } else if (!subExpr->isValueDependent()) {
214 if (auto const res = subExpr->getIntegerConstantExpr(compiler.getASTContext())) {
215 report(
216 DiagnosticsEngine::Warning,
217 ("implicit conversion (%0) of integer constant expression of type"
218 " %1 with value %2 to %3"),
219 expr2->getBeginLoc())
220 << castExpr->getCastKindName() << subExpr->getType()
221 << compat::toString(*res, 10) << castExpr->getType()
222 << expr2->getSourceRange();
227 loplugin::Plugin::Registration<LiteralToBoolConversion> literaltoboolconversion("literaltoboolconversion");
229 } // namespace
231 #endif // LO_CLANG_SHARED_PLUGINS