d2c5490210
> compilerplugins/clang/casttovoid.cxx:452:18: error: 'endswith' is deprecated: Use ends_with instead [-Werror,-Wdeprecated-declarations] > .endswith(".h")); > ^~~~~~~~ > ends_with > ~/llvm/inst/include/llvm/ADT/StringRef.h:276:19: note: 'endswith' has been explicitly marked deprecated here > [[nodiscard]] LLVM_DEPRECATED( > ^ etc. after <5ac12951b4
> "[ADT] Deprecate StringRef::{starts,ends}with (#75491)" on Clang 18 trunk, where <1b97645e56
> "[ADT] Introduce StringRef::{starts,ends}_width{,_insensitive}" had been added towards Clang 16 Change-Id: Icb3e43b7d6be6f877815285913d846f766eddebf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160919 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
293 lines
9.1 KiB
C++
293 lines
9.1 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
/*
|
|
* 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/.
|
|
*/
|
|
|
|
//TODO: Make this a shared plugin for Clang 12 (and possibly even for older Clang) again.
|
|
|
|
#include <cassert>
|
|
#include <limits>
|
|
#include <stack>
|
|
|
|
#include "clang/Basic/Builtins.h"
|
|
|
|
#include "check.hxx"
|
|
#include "compat.hxx"
|
|
#include "plugin.hxx"
|
|
|
|
// Find implicit conversions from non-'bool' constants (e.g., 'sal_False') to 'bool'.
|
|
|
|
namespace
|
|
{
|
|
class ConstToBool final : public loplugin::FilteringPlugin<ConstToBool>
|
|
{
|
|
public:
|
|
explicit ConstToBool(loplugin::InstantiationData const& data)
|
|
: FilteringPlugin(data)
|
|
{
|
|
}
|
|
|
|
bool PreTraverseLinkageSpecDecl(LinkageSpecDecl*)
|
|
{
|
|
assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
|
|
++externCContexts_;
|
|
return true;
|
|
}
|
|
|
|
bool PostTraverseLinkageSpecDecl(LinkageSpecDecl*, bool)
|
|
{
|
|
assert(externCContexts_ != 0);
|
|
--externCContexts_;
|
|
return true;
|
|
}
|
|
|
|
bool TraverseLinkageSpecDecl(LinkageSpecDecl* decl)
|
|
{
|
|
bool ret = true;
|
|
if (PreTraverseLinkageSpecDecl(decl))
|
|
{
|
|
ret = FilteringPlugin::TraverseLinkageSpecDecl(decl);
|
|
PostTraverseLinkageSpecDecl(decl, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool PreTraverseUnaryOperator(UnaryOperator* expr)
|
|
{
|
|
if (expr->getOpcode() == UO_LNot)
|
|
{
|
|
ignoredInAssert_.push(expr->getSubExpr());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PostTraverseUnaryOperator(UnaryOperator* expr, bool)
|
|
{
|
|
if (expr->getOpcode() == UO_LNot)
|
|
{
|
|
assert(!ignoredInAssert_.empty());
|
|
ignoredInAssert_.pop();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TraverseUnaryOperator(UnaryOperator* expr)
|
|
{
|
|
bool ret = true;
|
|
if (PreTraverseUnaryOperator(expr))
|
|
{
|
|
ret = FilteringPlugin::TraverseUnaryOperator(expr);
|
|
PostTraverseUnaryOperator(expr, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool PreTraverseBinaryOperator(BinaryOperator* expr)
|
|
{
|
|
if (expr->getOpcode() == BO_LAnd)
|
|
{
|
|
ignoredInAssert_.push(expr->getRHS());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PostTraverseBinaryOperator(BinaryOperator* expr, bool)
|
|
{
|
|
if (expr->getOpcode() == BO_LAnd)
|
|
{
|
|
assert(!ignoredInAssert_.empty());
|
|
ignoredInAssert_.pop();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TraverseBinaryOperator(BinaryOperator* expr)
|
|
{
|
|
bool ret = true;
|
|
if (PreTraverseBinaryOperator(expr))
|
|
{
|
|
ret = FilteringPlugin::TraverseBinaryOperator(expr);
|
|
PostTraverseBinaryOperator(expr, ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool VisitImplicitCastExpr(ImplicitCastExpr const* expr)
|
|
{
|
|
if (ignoreLocation(expr))
|
|
{
|
|
return true;
|
|
}
|
|
if (!expr->getType()->isBooleanType())
|
|
{
|
|
return true;
|
|
}
|
|
auto const sub = expr->getSubExpr();
|
|
auto const t = sub->getType();
|
|
if (t->isBooleanType())
|
|
{
|
|
return true;
|
|
}
|
|
if (sub->isValueDependent())
|
|
{
|
|
return true;
|
|
}
|
|
APValue res;
|
|
if (!sub->isCXX11ConstantExpr(compiler.getASTContext(), &res))
|
|
{
|
|
return true;
|
|
}
|
|
auto const l = expr->getExprLoc();
|
|
if (!ignoredInAssert_.empty() && expr == ignoredInAssert_.top())
|
|
{
|
|
if (auto const e = dyn_cast<clang::StringLiteral>(sub->IgnoreParenImpCasts()))
|
|
{
|
|
if (compat::isOrdinary(e)) // somewhat randomly restrict to plain literals
|
|
{
|
|
if (compiler.getSourceManager().isMacroArgExpansion(l)
|
|
&& Lexer::getImmediateMacroName(l, compiler.getSourceManager(),
|
|
compiler.getLangOpts())
|
|
== "assert")
|
|
{
|
|
//TODO: only ignore outermost '!"..."' or '... && "..."'
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
auto l1 = l;
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(l1))
|
|
{
|
|
auto const n = Lexer::getImmediateMacroName(l1, compiler.getSourceManager(),
|
|
compiler.getLangOpts());
|
|
if (n == "FALSE" || n == "TRUE" || n == "sal_False" || n == "sal_True")
|
|
{
|
|
l1 = compiler.getSourceManager().getImmediateMacroCallerLoc(l1);
|
|
}
|
|
// For example, /usr/include/glib-2.0/glib/gmacros.h from
|
|
// glib2-devel-2.62.1-1.fc31.x86_64 has
|
|
//
|
|
// #define TRUE (!FALSE)
|
|
//
|
|
// so handle that wrapped macro body expansion, too:
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(l1)
|
|
&& Lexer::getImmediateMacroName(l1, compiler.getSourceManager(),
|
|
compiler.getLangOpts())
|
|
== "TRUE")
|
|
{
|
|
l1 = compiler.getSourceManager().getImmediateMacroCallerLoc(l1);
|
|
}
|
|
}
|
|
if (isSharedCAndCppCode(l1))
|
|
{
|
|
// Cover just enough cases to handle things like `while (0)` or the use of `sal_True` in
|
|
//
|
|
// #define OSL_FAIL(m) SAL_DETAIL_WARN_IF_FORMAT(sal_True, "legacy.osl", "%s", m)
|
|
//
|
|
// in include/osl/diagnose.h:
|
|
if (auto const t1 = t->getAs<BuiltinType>())
|
|
{
|
|
if (t1->getKind() == BuiltinType::Int)
|
|
{
|
|
auto const& v = res.getInt();
|
|
if (v == 0 || v == 1)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (loplugin::TypeCheck(t).Typedef("sal_Bool").GlobalNamespace())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (auto const e = dyn_cast<CallExpr>(sub->IgnoreParenImpCasts()))
|
|
{
|
|
// Ignore use of `long __builtin_expect(long, long)`, as found in the definition of
|
|
// `assert` on macOS:
|
|
if (e->getBuiltinCallee() == Builtin::BI__builtin_expect)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
bool suggestion;
|
|
bool replacement = {};
|
|
if (res.isInt())
|
|
{
|
|
suggestion = true;
|
|
replacement = res.getInt() != 0;
|
|
}
|
|
else if (res.isFloat())
|
|
{
|
|
suggestion = true;
|
|
replacement = !res.getFloat().isZero();
|
|
}
|
|
else if (res.isNullPointer())
|
|
{
|
|
suggestion = true;
|
|
replacement = false;
|
|
}
|
|
else if (res.isLValue())
|
|
{
|
|
suggestion = true;
|
|
replacement = true;
|
|
}
|
|
else
|
|
{
|
|
suggestion = false;
|
|
}
|
|
report(DiagnosticsEngine::Warning,
|
|
"implicit conversion of constant %0 of type %1 to 'bool'%select{|; use "
|
|
"'%select{false|true}3' instead}2",
|
|
l)
|
|
<< res.getAsString(compiler.getASTContext(), t) << t << suggestion << replacement
|
|
<< expr->getSourceRange();
|
|
return true;
|
|
}
|
|
|
|
bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
|
|
|
|
private:
|
|
std::stack<Expr const*> ignoredInAssert_;
|
|
unsigned int externCContexts_ = 0;
|
|
|
|
void run() override
|
|
{
|
|
if (preRun())
|
|
{
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
}
|
|
|
|
bool isFromCIncludeFile(SourceLocation spellingLocation) const
|
|
{
|
|
return !compiler.getSourceManager().isInMainFile(spellingLocation)
|
|
&& compat::ends_with(StringRef(compiler.getSourceManager()
|
|
.getPresumedLoc(spellingLocation)
|
|
.getFilename()),
|
|
".h");
|
|
}
|
|
|
|
bool isSharedCAndCppCode(SourceLocation location) const
|
|
{
|
|
while (compiler.getSourceManager().isMacroArgExpansion(location))
|
|
{
|
|
location = compiler.getSourceManager().getImmediateMacroCallerLoc(location);
|
|
}
|
|
// 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));
|
|
}
|
|
};
|
|
|
|
loplugin::Plugin::Registration<ConstToBool> consttobool("consttobool");
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|