bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / literaltoboolconversion.cxx
blob32e820502f399697a21e5c10adac2955e448ec98
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 */
10 #include <cassert>
11 #include <limits>
13 #include "clang/Lex/Lexer.h"
15 #include "compat.hxx"
16 #include "plugin.hxx"
18 namespace {
20 class LiteralToBoolConversion:
21 public loplugin::FilteringRewritePlugin<LiteralToBoolConversion>
23 public:
24 explicit LiteralToBoolConversion(loplugin::InstantiationData const & data):
25 FilteringRewritePlugin(data) {}
27 virtual void run() override
28 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
30 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
32 bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
34 private:
35 bool isFromCIncludeFile(SourceLocation spellingLocation) const;
37 bool isSharedCAndCppCode(SourceLocation location) const;
39 void handleImplicitCastSubExpr(
40 ImplicitCastExpr const * castExpr, Expr const * subExpr);
42 unsigned int externCContexts_ = 0;
45 bool LiteralToBoolConversion::VisitImplicitCastExpr(
46 ImplicitCastExpr const * expr)
48 if (ignoreLocation(expr)) {
49 return true;
51 if (!expr->getType()->isBooleanType()) {
52 return true;
54 handleImplicitCastSubExpr(expr, expr->getSubExpr());
55 return true;
58 bool LiteralToBoolConversion::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
59 assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
60 ++externCContexts_;
61 bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
62 assert(externCContexts_ != 0);
63 --externCContexts_;
64 return ret;
67 bool LiteralToBoolConversion::isFromCIncludeFile(
68 SourceLocation spellingLocation) const
70 return !compiler.getSourceManager().isInMainFile(spellingLocation)
71 && (StringRef(
72 compiler.getSourceManager().getPresumedLoc(spellingLocation)
73 .getFilename())
74 .endswith(".h"));
77 bool LiteralToBoolConversion::isSharedCAndCppCode(SourceLocation location) const
79 // Assume that code is intended to be shared between C and C++ if it comes
80 // from an include file ending in .h, and is either in an extern "C" context
81 // or the body of a macro definition:
82 return
83 isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
84 && (externCContexts_ != 0
85 || compiler.getSourceManager().isMacroBodyExpansion(location));
88 void LiteralToBoolConversion::handleImplicitCastSubExpr(
89 ImplicitCastExpr const * castExpr, Expr const * subExpr)
91 Expr const * expr2 = subExpr;
92 // track sub-expr with potential parens, to e.g. rewrite all of expanded
94 // #define sal_False ((sal_Bool)0)
96 // including the parens
97 subExpr = expr2->IgnoreParenCasts();
98 for (;;) {
99 BinaryOperator const * op = dyn_cast<BinaryOperator>(subExpr);
100 if (op == nullptr || op->getOpcode() != BO_Comma) {
101 break;
103 expr2 = op->getRHS();
104 subExpr = expr2->IgnoreParenCasts();
106 if (subExpr->getType()->isBooleanType()) {
107 return;
109 ConditionalOperator const * op = dyn_cast<ConditionalOperator>(subExpr);
110 if (op != nullptr) {
111 handleImplicitCastSubExpr(castExpr, op->getTrueExpr());
112 handleImplicitCastSubExpr(castExpr, op->getFalseExpr());
113 return;
115 APSInt res;
116 if (!subExpr->isValueDependent()
117 && subExpr->isIntegerConstantExpr(res, compiler.getASTContext())
118 && res.getLimitedValue() <= 1)
120 SourceLocation loc { compat::getBeginLoc(subExpr) };
121 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
122 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
124 if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
125 StringRef name { Lexer::getImmediateMacroName(
126 loc, compiler.getSourceManager(), compiler.getLangOpts()) };
127 if (name == "sal_False" || name == "sal_True") {
128 loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).first;
130 if (isSharedCAndCppCode(loc)) {
131 return;
135 if (isa<clang::StringLiteral>(subExpr)) {
136 SourceLocation loc { compat::getBeginLoc(subExpr) };
137 if (compiler.getSourceManager().isMacroArgExpansion(loc)
138 && (Lexer::getImmediateMacroName(
139 loc, compiler.getSourceManager(), compiler.getLangOpts())
140 == "assert"))
142 return;
145 if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
146 || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
147 || isa<clang::StringLiteral>(subExpr))
149 bool bRewritten = false;
150 if (rewriter != nullptr) {
151 SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
152 compat::getBeginLoc(expr2)) };
153 if (compiler.getSourceManager().getExpansionLoc(compat::getEndLoc(expr2))
154 == loc)
156 char const * s = compiler.getSourceManager().getCharacterData(
157 loc);
158 unsigned n = Lexer::MeasureTokenLength(
159 compat::getEndLoc(expr2), compiler.getSourceManager(),
160 compiler.getLangOpts());
161 std::string tok { s, n };
162 if (tok == "sal_False" || tok == "0") {
163 bRewritten = replaceText(
164 compiler.getSourceManager().getExpansionLoc(
165 compat::getBeginLoc(expr2)),
166 n, "false");
167 } else if (tok == "sal_True" || tok == "1") {
168 bRewritten = replaceText(
169 compiler.getSourceManager().getExpansionLoc(
170 compat::getBeginLoc(expr2)),
171 n, "true");
175 if (!bRewritten) {
176 report(
177 DiagnosticsEngine::Warning,
178 "implicit conversion (%0) of literal of type %1 to %2",
179 compat::getBeginLoc(expr2))
180 << castExpr->getCastKindName() << subExpr->getType()
181 << castExpr->getType() << expr2->getSourceRange();
183 } else if (subExpr->isNullPointerConstant(
184 compiler.getASTContext(), Expr::NPC_ValueDependentIsNull)
185 > Expr::NPCK_ZeroExpression)
187 // The test above originally checked for != Expr::NPCK_NotNull, but in non-C++11
188 // mode we can get also Expr::NPCK_ZeroExpression inside templates, even though
189 // the expression is actually not a null pointer. Clang bug or C++98 misfeature?
190 // See Clang's NPCK_ZeroExpression declaration and beginning of isNullPointerConstant().
191 static_assert( Expr::NPCK_NotNull == 0 && Expr::NPCK_ZeroExpression == 1, "Clang API change" );
192 report(
193 DiagnosticsEngine::Warning,
194 ("implicit conversion (%0) of null pointer constant of type %1 to"
195 " %2"),
196 compat::getBeginLoc(expr2))
197 << castExpr->getCastKindName() << subExpr->getType()
198 << castExpr->getType() << expr2->getSourceRange();
199 } else if (!subExpr->isValueDependent()
200 && subExpr->isIntegerConstantExpr(res, compiler.getASTContext()))
202 report(
203 DiagnosticsEngine::Warning,
204 ("implicit conversion (%0) of integer constant expression of type"
205 " %1 with value %2 to %3"),
206 compat::getBeginLoc(expr2))
207 << castExpr->getCastKindName() << subExpr->getType()
208 << res.toString(10) << castExpr->getType()
209 << expr2->getSourceRange();
213 loplugin::Plugin::Registration<LiteralToBoolConversion> X(
214 "literaltoboolconversion", true);