libreoffice/compilerplugins/clang/literaltoboolconversion.cxx
Stephan Bergmann b72a31b37f Adapt to Clang trunk SourceManager::getImmediateExpansionRange changes
...of <http://llvm.org/viewvc/llvm-project?view=revision&revision=331155>
"PR37189 Fix incorrect end source location and spelling for a split '>>' token",
changing (among others) the return type of getImmediateExpansionRange from a
std::pair of token locations to CharSourceRange (which will typically also
represent token locations, but might also represent char locations).

For now, map the return value of getImmediateExpansionRange back to a std::pair
(as expected by our compilerplugins code in its current form), and mark the
char location case with a TODO (which will need to be addressed if any of our
plugins starts to produce wrong results due to not handling that char location
case).  In the long run, we should instead adapt our code to use the new return
type of getImmediateExpansionRange directly.

Change-Id: Idc2f5dc43830af4798b55bf605976c4ab146c522
Reviewed-on: https://gerrit.libreoffice.org/53817
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2018-05-04 13:56:51 +02:00

218 lines
8.0 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <cassert>
#include <limits>
#include "clang/Lex/Lexer.h"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
class LiteralToBoolConversion:
public RecursiveASTVisitor<LiteralToBoolConversion>,
public loplugin::RewritePlugin
{
public:
explicit LiteralToBoolConversion(loplugin::InstantiationData const & data):
RewritePlugin(data) {}
virtual void run() override
{ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
private:
bool isFromCIncludeFile(SourceLocation spellingLocation) const;
bool isSharedCAndCppCode(SourceLocation location) const;
void handleImplicitCastSubExpr(
ImplicitCastExpr const * castExpr, Expr const * subExpr);
unsigned int externCContexts_ = 0;
};
bool LiteralToBoolConversion::VisitImplicitCastExpr(
ImplicitCastExpr const * expr)
{
if (ignoreLocation(expr)) {
return true;
}
if (!expr->getType()->isBooleanType()) {
return true;
}
handleImplicitCastSubExpr(expr, expr->getSubExpr());
return true;
}
bool LiteralToBoolConversion::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
++externCContexts_;
bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
assert(externCContexts_ != 0);
--externCContexts_;
return ret;
}
bool LiteralToBoolConversion::isFromCIncludeFile(
SourceLocation spellingLocation) const
{
return !compiler.getSourceManager().isInMainFile(spellingLocation)
&& (StringRef(
compiler.getSourceManager().getPresumedLoc(spellingLocation)
.getFilename())
.endswith(".h"));
}
bool LiteralToBoolConversion::isSharedCAndCppCode(SourceLocation location) const
{
// Assume that code is intended to be shared between C and C++ if it comes
// from an include file ending in .h, and is either in an extern "C" context
// or the body of a macro definition:
return
isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
&& (externCContexts_ != 0
|| compiler.getSourceManager().isMacroBodyExpansion(location));
}
void LiteralToBoolConversion::handleImplicitCastSubExpr(
ImplicitCastExpr const * castExpr, Expr const * subExpr)
{
Expr const * expr2 = subExpr;
// track sub-expr with potential parens, to e.g. rewrite all of expanded
//
// #define sal_False ((sal_Bool)0)
//
// including the parens
subExpr = expr2->IgnoreParenCasts();
for (;;) {
BinaryOperator const * op = dyn_cast<BinaryOperator>(subExpr);
if (op == nullptr || op->getOpcode() != BO_Comma) {
break;
}
expr2 = op->getRHS();
subExpr = expr2->IgnoreParenCasts();
}
if (subExpr->getType()->isBooleanType()) {
return;
}
ConditionalOperator const * op = dyn_cast<ConditionalOperator>(subExpr);
if (op != nullptr) {
handleImplicitCastSubExpr(castExpr, op->getTrueExpr());
handleImplicitCastSubExpr(castExpr, op->getFalseExpr());
return;
}
APSInt res;
if (!subExpr->isValueDependent()
&& subExpr->isIntegerConstantExpr(res, compiler.getASTContext())
&& res.getLimitedValue() <= 1)
{
SourceLocation loc { subExpr->getLocStart() };
while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
}
if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
StringRef name { Lexer::getImmediateMacroName(
loc, compiler.getSourceManager(), compiler.getLangOpts()) };
if (name == "sal_False" || name == "sal_True") {
loc = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).first;
}
if (isSharedCAndCppCode(loc)) {
return;
}
}
}
if (isa<clang::StringLiteral>(subExpr)) {
SourceLocation loc { subExpr->getLocStart() };
if (compiler.getSourceManager().isMacroArgExpansion(loc)
&& (Lexer::getImmediateMacroName(
loc, compiler.getSourceManager(), compiler.getLangOpts())
== "assert"))
{
return;
}
}
if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
|| isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
|| isa<clang::StringLiteral>(subExpr))
{
bool bRewritten = false;
if (rewriter != nullptr) {
SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
expr2->getLocStart()) };
if (compiler.getSourceManager().getExpansionLoc(expr2->getLocEnd())
== loc)
{
char const * s = compiler.getSourceManager().getCharacterData(
loc);
unsigned n = Lexer::MeasureTokenLength(
expr2->getLocEnd(), compiler.getSourceManager(),
compiler.getLangOpts());
std::string tok { s, n };
if (tok == "sal_False" || tok == "0") {
bRewritten = replaceText(
compiler.getSourceManager().getExpansionLoc(
expr2->getLocStart()),
n, "false");
} else if (tok == "sal_True" || tok == "1") {
bRewritten = replaceText(
compiler.getSourceManager().getExpansionLoc(
expr2->getLocStart()),
n, "true");
}
}
}
if (!bRewritten) {
report(
DiagnosticsEngine::Warning,
"implicit conversion (%0) of literal of type %1 to %2",
expr2->getLocStart())
<< castExpr->getCastKindName() << subExpr->getType()
<< castExpr->getType() << expr2->getSourceRange();
}
} else if (subExpr->isNullPointerConstant(
compiler.getASTContext(), Expr::NPC_ValueDependentIsNull)
> Expr::NPCK_ZeroExpression)
{
// The test above originally checked for != Expr::NPCK_NotNull, but in non-C++11
// mode we can get also Expr::NPCK_ZeroExpression inside templates, even though
// the expression is actually not a null pointer. Clang bug or C++98 misfeature?
// See Clang's NPCK_ZeroExpression declaration and beginning of isNullPointerConstant().
static_assert( Expr::NPCK_NotNull == 0 && Expr::NPCK_ZeroExpression == 1, "Clang API change" );
report(
DiagnosticsEngine::Warning,
("implicit conversion (%0) of null pointer constant of type %1 to"
" %2"),
expr2->getLocStart())
<< castExpr->getCastKindName() << subExpr->getType()
<< castExpr->getType() << expr2->getSourceRange();
} else if (!subExpr->isValueDependent()
&& subExpr->isIntegerConstantExpr(res, compiler.getASTContext()))
{
report(
DiagnosticsEngine::Warning,
("implicit conversion (%0) of integer constant expression of type"
" %1 with value %2 to %3"),
expr2->getLocStart())
<< castExpr->getCastKindName() << subExpr->getType()
<< res.toString(10) << castExpr->getType()
<< expr2->getSourceRange();
}
}
loplugin::Plugin::Registration<LiteralToBoolConversion> X(
"literaltoboolconversion", true);
}