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>
706 lines
27 KiB
C++
706 lines
27 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 <algorithm>
|
|
#include <cassert>
|
|
#include <limits>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include "compat.hxx"
|
|
#include "plugin.hxx"
|
|
|
|
//
|
|
// We don't like using C-style casts in C++ code. Similarly, warn about function-style casts (which
|
|
// are semantically equivalent to C-style casts) that are not semantically equivalent to static_cast
|
|
// and should rather be written as const_cast or reinterpret_cast.
|
|
//
|
|
|
|
namespace {
|
|
|
|
bool areSimilar(QualType type1, QualType type2) {
|
|
auto t1 = type1.getCanonicalType().getTypePtr();
|
|
auto t2 = type2.getCanonicalType().getTypePtr();
|
|
for (;;) {
|
|
if (t1->isPointerType()) {
|
|
if (!t2->isPointerType()) {
|
|
return false;
|
|
}
|
|
auto t1a = t1->getAs<clang::PointerType>();
|
|
auto t2a = t2->getAs<clang::PointerType>();
|
|
t1 = t1a->getPointeeType().getTypePtr();
|
|
t2 = t2a->getPointeeType().getTypePtr();
|
|
} else if (t1->isMemberPointerType()) {
|
|
if (!t2->isMemberPointerType()) {
|
|
return false;
|
|
}
|
|
auto t1a = t1->getAs<MemberPointerType>();
|
|
auto t2a = t2->getAs<MemberPointerType>();
|
|
if (t1a->getClass()->getCanonicalTypeInternal()
|
|
!= t2a->getClass()->getCanonicalTypeInternal())
|
|
{
|
|
return false;
|
|
}
|
|
t1 = t1a->getPointeeType().getTypePtr();
|
|
t2 = t2a->getPointeeType().getTypePtr();
|
|
} else if (t1->isConstantArrayType()) {
|
|
if (!t2->isConstantArrayType()) {
|
|
return false;
|
|
}
|
|
auto t1a = static_cast<ConstantArrayType const *>(
|
|
t1->getAsArrayTypeUnsafe());
|
|
auto t2a = static_cast<ConstantArrayType const *>(
|
|
t2->getAsArrayTypeUnsafe());
|
|
if (t1a->getSize() != t2a->getSize()) {
|
|
return false;
|
|
}
|
|
t1 = t1a->getElementType().getTypePtr();
|
|
t2 = t2a->getElementType().getTypePtr();
|
|
} else if (t1->isIncompleteArrayType()) {
|
|
if (!t2->isIncompleteArrayType()) {
|
|
return false;
|
|
}
|
|
auto t1a = static_cast<IncompleteArrayType const *>(
|
|
t1->getAsArrayTypeUnsafe());
|
|
auto t2a = static_cast<IncompleteArrayType const *>(
|
|
t2->getAsArrayTypeUnsafe());
|
|
t1 = t1a->getElementType().getTypePtr();
|
|
t2 = t2a->getElementType().getTypePtr();
|
|
} else {
|
|
return false;
|
|
}
|
|
if (t1 == t2) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
QualType resolvePointers(QualType type) {
|
|
while (type->isPointerType()) {
|
|
type = type->getAs<clang::PointerType>()->getPointeeType();
|
|
}
|
|
return type;
|
|
}
|
|
|
|
bool isLiteralLike(Expr const * expr) {
|
|
expr = expr->IgnoreParenImpCasts();
|
|
if (isa<IntegerLiteral>(expr) || isa<CharacterLiteral>(expr) || isa<FloatingLiteral>(expr)
|
|
|| isa<ImaginaryLiteral>(expr) || isa<CXXBoolLiteralExpr>(expr)
|
|
|| isa<CXXNullPtrLiteralExpr>(expr) || isa<ObjCBoolLiteralExpr>(expr))
|
|
{
|
|
return true;
|
|
}
|
|
if (auto const e = dyn_cast<DeclRefExpr>(expr)) {
|
|
auto const d = e->getDecl();
|
|
if (isa<EnumConstantDecl>(d)) {
|
|
return true;
|
|
}
|
|
if (auto const v = dyn_cast<VarDecl>(d)) {
|
|
if (d->getType().isConstQualified()) {
|
|
if (auto const init = v->getAnyInitializer()) {
|
|
return isLiteralLike(init);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
if (auto const e = dyn_cast<UnaryExprOrTypeTraitExpr>(expr)) {
|
|
auto const k = e->getKind();
|
|
return k == UETT_SizeOf || k == UETT_AlignOf;
|
|
}
|
|
if (auto const e = dyn_cast<UnaryOperator>(expr)) {
|
|
auto const k = e->getOpcode();
|
|
if (k == UO_Plus || k == UO_Minus || k == UO_Not || k == UO_LNot) {
|
|
return isLiteralLike(e->getSubExpr());
|
|
}
|
|
return false;
|
|
}
|
|
if (auto const e = dyn_cast<BinaryOperator>(expr)) {
|
|
auto const k = e->getOpcode();
|
|
if (k == BO_Mul || k == BO_Div || k == BO_Rem || k == BO_Add || k == BO_Sub || k == BO_Shl
|
|
|| k == BO_Shr || k == BO_And || k == BO_Xor || k == BO_Or)
|
|
{
|
|
return isLiteralLike(e->getLHS()) && isLiteralLike(e->getRHS());
|
|
}
|
|
return false;
|
|
}
|
|
if (auto const e = dyn_cast<ExplicitCastExpr>(expr)) {
|
|
auto const t = e->getTypeAsWritten();
|
|
return (t->isArithmeticType() || t->isEnumeralType())
|
|
&& isLiteralLike(e->getSubExprAsWritten());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) {
|
|
// Must be <simple-type-specifier> or <typename-specifier>, lets approximate that here:
|
|
assert(info != nullptr);
|
|
auto const type = info->getType();
|
|
if (type.hasLocalQualifiers()) {
|
|
return false;
|
|
}
|
|
if (auto const t = dyn_cast<BuiltinType>(type)) {
|
|
if (!(t->isInteger() || t->isFloatingPoint())) {
|
|
return false;
|
|
}
|
|
auto const loc = info->getTypeLoc().castAs<BuiltinTypeLoc>();
|
|
return
|
|
(int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec())
|
|
+ int(loc.hasWrittenTypeSpec()))
|
|
== 1;
|
|
}
|
|
if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
|
|
|| isa<DecltypeType>(type) || isa<TypedefType>(type))
|
|
{
|
|
return true;
|
|
}
|
|
if (auto const t = dyn_cast<ElaboratedType>(type)) {
|
|
return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
class CStyleCast:
|
|
public loplugin::FilteringRewritePlugin<CStyleCast>
|
|
{
|
|
public:
|
|
explicit CStyleCast(loplugin::InstantiationData const & data): FilteringRewritePlugin(data)
|
|
{}
|
|
|
|
virtual void run() override {
|
|
if (compiler.getLangOpts().CPlusPlus) {
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
}
|
|
|
|
bool TraverseInitListExpr(InitListExpr * expr, DataRecursionQueue * queue = nullptr) {
|
|
return WalkUpFromInitListExpr(expr)
|
|
&& TraverseSynOrSemInitListExpr(
|
|
expr->isSemanticForm() ? expr : expr->getSemanticForm(), queue);
|
|
}
|
|
|
|
bool TraverseLinkageSpecDecl(LinkageSpecDecl * decl);
|
|
|
|
bool VisitCStyleCastExpr(const CStyleCastExpr * expr);
|
|
|
|
bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr);
|
|
|
|
private:
|
|
bool isConstCast(QualType from, QualType to);
|
|
|
|
bool isFromCIncludeFile(SourceLocation spellingLocation) const;
|
|
|
|
bool isSharedCAndCppCode(SourceLocation location) const;
|
|
|
|
bool isLastTokenOfImmediateMacroBodyExpansion(
|
|
SourceLocation loc, SourceLocation * macroEnd = nullptr) const;
|
|
|
|
bool rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement);
|
|
|
|
void reportCast(ExplicitCastExpr const * expr, char const * performsHint);
|
|
|
|
unsigned int externCContexts_ = 0;
|
|
std::set<SourceLocation> rewritten_;
|
|
// needed when rewriting in macros, in general to avoid "double code replacement, possible
|
|
// plugin error" warnings, and in particular to avoid adding multiple sets of parens around
|
|
// sub-exprs
|
|
std::set<CStyleCastExpr const *> rewrittenSubExprs_;
|
|
};
|
|
|
|
const char * recommendedFix(clang::CastKind ck) {
|
|
switch(ck) {
|
|
case CK_IntegralToPointer: return "reinterpret_cast";
|
|
case CK_PointerToIntegral: return "reinterpret_cast";
|
|
case CK_BaseToDerived: return "static_cast";
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
bool CStyleCast::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 CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
// casting to void is typically used when a parameter or field is only used in
|
|
// debug mode, and we want to eliminate an "unused" warning
|
|
if( expr->getCastKind() == CK_ToVoid ) {
|
|
return true;
|
|
}
|
|
if (isSharedCAndCppCode(expr->getBeginLoc())) {
|
|
return true;
|
|
}
|
|
char const * perf = nullptr;
|
|
if( expr->getCastKind() == CK_IntegralCast ) {
|
|
if (rewriteArithmeticCast(expr, &perf)) {
|
|
return true;
|
|
}
|
|
} else if( expr->getCastKind() == CK_NoOp ) {
|
|
if (!((expr->getSubExpr()->getType()->isPointerType()
|
|
&& expr->getType()->isPointerType())
|
|
|| expr->getTypeAsWritten()->isReferenceType()))
|
|
{
|
|
if (rewriteArithmeticCast(expr, &perf)) {
|
|
return true;
|
|
}
|
|
}
|
|
if (isConstCast(
|
|
expr->getSubExprAsWritten()->getType(),
|
|
expr->getTypeAsWritten()))
|
|
{
|
|
perf = "const_cast";
|
|
}
|
|
}
|
|
reportCast(expr, perf);
|
|
return true;
|
|
}
|
|
|
|
bool CStyleCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
char const * perf = nullptr;
|
|
switch (expr->getCastKind()) {
|
|
case CK_ConstructorConversion:
|
|
case CK_Dependent: //TODO: really filter out all of these?
|
|
case CK_IntegralCast:
|
|
case CK_IntegralToBoolean:
|
|
case CK_ToVoid:
|
|
return true;
|
|
case CK_NoOp:
|
|
if (isConstCast(
|
|
expr->getSubExprAsWritten()->getType(),
|
|
expr->getTypeAsWritten()))
|
|
{
|
|
perf = "const_cast";
|
|
break;
|
|
}
|
|
return true; //TODO: really filter out all of these?
|
|
default:
|
|
break;
|
|
}
|
|
reportCast(expr, perf);
|
|
return true;
|
|
}
|
|
|
|
bool CStyleCast::isConstCast(QualType from, QualType to) {
|
|
if (to->isReferenceType()
|
|
&& to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
|
|
{
|
|
if (!from->isObjectType()) {
|
|
return false;
|
|
}
|
|
from = compiler.getASTContext().getPointerType(from);
|
|
to = compiler.getASTContext().getPointerType(
|
|
to->getAs<ReferenceType>()->getPointeeType());
|
|
} else {
|
|
if (from->isArrayType()) {
|
|
from = compiler.getASTContext().getPointerType(
|
|
from->getAsArrayTypeUnsafe()->getElementType());
|
|
} else if (from->isFunctionType()) {
|
|
compiler.getASTContext().getPointerType(from);
|
|
}
|
|
}
|
|
return areSimilar(from, to);
|
|
}
|
|
|
|
bool CStyleCast::isFromCIncludeFile(SourceLocation spellingLocation) const {
|
|
return !compiler.getSourceManager().isInMainFile(spellingLocation)
|
|
&& compat::ends_with(
|
|
StringRef(compiler.getSourceManager().getPresumedLoc(spellingLocation).getFilename()),
|
|
".h");
|
|
}
|
|
|
|
bool CStyleCast::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));
|
|
}
|
|
|
|
bool CStyleCast::isLastTokenOfImmediateMacroBodyExpansion(
|
|
SourceLocation loc, SourceLocation * macroEnd) const
|
|
{
|
|
assert(compiler.getSourceManager().isMacroBodyExpansion(loc));
|
|
auto const spell = compiler.getSourceManager().getSpellingLoc(loc);
|
|
auto name = Lexer::getImmediateMacroName(
|
|
loc, compiler.getSourceManager(), compiler.getLangOpts());
|
|
while (compat::starts_with(name, "\\\n")) {
|
|
name = name.drop_front(2);
|
|
while (!name.empty()
|
|
&& (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
|
|
|| name.front() == '\v' || name.front() == '\f'))
|
|
{
|
|
name = name.drop_front(1);
|
|
}
|
|
}
|
|
auto const MI
|
|
= (compiler.getPreprocessor().getMacroDefinitionAtLoc(
|
|
&compiler.getASTContext().Idents.get(name), spell)
|
|
.getMacroInfo());
|
|
assert(MI != nullptr);
|
|
if (spell == MI->getDefinitionEndLoc()) {
|
|
if (macroEnd != nullptr) {
|
|
*macroEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).second;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CStyleCast::rewriteArithmeticCast(CStyleCastExpr const * expr, char const ** replacement) {
|
|
assert(replacement != nullptr);
|
|
auto const sub = expr->getSubExprAsWritten();
|
|
auto const functional = isLiteralLike(sub)
|
|
&& canBeUsedForFunctionalCast(expr->getTypeInfoAsWritten());
|
|
*replacement = functional ? "functional cast" : "static_cast";
|
|
if (rewriter == nullptr) {
|
|
return false;
|
|
}
|
|
// Doing modifications for a chain of C-style casts as in
|
|
//
|
|
// (foo)(bar)(baz)x
|
|
//
|
|
// leads to unpredictable results, so only rewrite them one at a time, starting with the
|
|
// outermost:
|
|
if (auto const e = dyn_cast<CStyleCastExpr>(sub)) {
|
|
rewrittenSubExprs_.insert(e);
|
|
}
|
|
if (rewrittenSubExprs_.find(expr) != rewrittenSubExprs_.end()) {
|
|
return false;
|
|
}
|
|
// Two or four ranges to replace:
|
|
// First is the CStyleCast's LParen, plus following whitespace, replaced with either "" or
|
|
// "static_cast<". (TODO: insert space before "static_cast<" when converting "else(int)...".)
|
|
// Second is the CStyleCast's RParen, plus preceding and following whitespace, replaced with
|
|
// either "" or ">".
|
|
// If the sub expr is not a ParenExpr, third is the sub expr's begin, inserting "(", and fourth
|
|
// is the sub expr's end, inserting ")".
|
|
// (The reason the second and third are not combined is in case there's a comment between them.)
|
|
auto firstBegin = expr->getLParenLoc();
|
|
auto secondBegin = expr->getRParenLoc();
|
|
while (compiler.getSourceManager().isMacroArgExpansion(firstBegin)
|
|
&& compiler.getSourceManager().isMacroArgExpansion(secondBegin)
|
|
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), firstBegin)
|
|
== compat::getImmediateExpansionRange(compiler.getSourceManager(), secondBegin)))
|
|
{
|
|
firstBegin = compiler.getSourceManager().getImmediateSpellingLoc(firstBegin);
|
|
secondBegin = compiler.getSourceManager().getImmediateSpellingLoc(secondBegin);
|
|
}
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(firstBegin)
|
|
&& compiler.getSourceManager().isMacroBodyExpansion(secondBegin)
|
|
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(firstBegin)
|
|
== compiler.getSourceManager().getImmediateMacroCallerLoc(secondBegin)))
|
|
{
|
|
firstBegin = compiler.getSourceManager().getSpellingLoc(firstBegin);
|
|
secondBegin = compiler.getSourceManager().getSpellingLoc(secondBegin);
|
|
}
|
|
auto third = sub->getBeginLoc();
|
|
auto fourth = sub->getEndLoc();
|
|
bool macro = false;
|
|
// Ensure that
|
|
//
|
|
// #define FOO(x) (int)x
|
|
// FOO(y)
|
|
//
|
|
// is changed to
|
|
//
|
|
// #define FOO(x) static_cast<int>(x)
|
|
// FOO(y)
|
|
//
|
|
// instead of
|
|
//
|
|
// #define FOO(x) static_cast<int>x
|
|
// FOO((y))
|
|
while (compiler.getSourceManager().isMacroArgExpansion(third)
|
|
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
|
|
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
|
|
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth))
|
|
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third))
|
|
//TODO: check fourth is at end of immediate macro expansion, but
|
|
// SourceManager::isAtEndOfImmediateMacroExpansion requires a location pointing at the
|
|
// character end of the last token
|
|
{
|
|
auto const range = compat::getImmediateExpansionRange(compiler.getSourceManager(), third);
|
|
third = range.first;
|
|
fourth = range.second;
|
|
macro = true;
|
|
assert(third.isValid());
|
|
}
|
|
while (compiler.getSourceManager().isMacroArgExpansion(third)
|
|
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
|
|
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
|
|
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth)))
|
|
{
|
|
third = compiler.getSourceManager().getImmediateSpellingLoc(third);
|
|
fourth = compiler.getSourceManager().getImmediateSpellingLoc(fourth);
|
|
}
|
|
if (isa<ParenExpr>(sub)) {
|
|
// Ensure that with
|
|
//
|
|
// #define FOO (x)
|
|
//
|
|
// a cast like
|
|
//
|
|
// (int) FOO
|
|
//
|
|
// is changed to
|
|
//
|
|
// static_cast<int>(FOO)
|
|
//
|
|
// instead of
|
|
//
|
|
// static_cast<int>FOO
|
|
for (;; macro = true) {
|
|
if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
|
|
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
|
|
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
|
|
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
|
|
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)
|
|
&& isLastTokenOfImmediateMacroBodyExpansion(fourth)))
|
|
{
|
|
if (!macro) {
|
|
third = fourth = SourceLocation();
|
|
}
|
|
break;
|
|
}
|
|
auto const range = compat::getImmediateExpansionRange(
|
|
compiler.getSourceManager(), third);
|
|
third = range.first;
|
|
fourth = range.second;
|
|
assert(third.isValid());
|
|
}
|
|
if (third.isValid() && compiler.getSourceManager().isMacroBodyExpansion(third)
|
|
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
|
|
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
|
|
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
|
|
{
|
|
third = compiler.getSourceManager().getSpellingLoc(third);
|
|
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
|
|
assert(third.isValid());
|
|
}
|
|
} else {
|
|
// Ensure that a cast like
|
|
//
|
|
// (int)LONG_MAX
|
|
//
|
|
// (where LONG_MAX expands to __LONG_MAX__, which in turn is a built-in expanding to a value
|
|
// like 9223372036854775807L) is changed to
|
|
//
|
|
// int(LONG_MAX)
|
|
//
|
|
// instead of trying to add the parentheses to the built-in __LONG_MAX__ definition:
|
|
for (;;) {
|
|
if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
|
|
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
|
|
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
|
|
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
|
|
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)))
|
|
// TODO: check that fourth is at end of immediate macro expansion (but
|
|
// SourceManager::isAtEndOfImmediateMacroExpansion wants a location pointing at the
|
|
// character end)
|
|
{
|
|
break;
|
|
}
|
|
auto const range = compat::getImmediateExpansionRange(
|
|
compiler.getSourceManager(), third);
|
|
third = range.first;
|
|
fourth = range.second;
|
|
}
|
|
// ...and additionally asymmetrically unwind macros only at the start or end, for code like
|
|
//
|
|
// (long)ubidi_getVisualIndex(...)
|
|
//
|
|
// (in editeng/source/editeng/impedit2.cxx) where ubidi_getVisualIndex is an object-like
|
|
// macro, or
|
|
//
|
|
// #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
|
|
//
|
|
// (in hwpfilter/source/lexer.cxx):
|
|
if (!fourth.isMacroID()) {
|
|
while (compiler.getSourceManager().isMacroBodyExpansion(third)
|
|
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third))
|
|
{}
|
|
} else if (compiler.getSourceManager().isMacroBodyExpansion(fourth)) {
|
|
while (compiler.getSourceManager().isMacroArgExpansion(third)
|
|
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third)) {}
|
|
}
|
|
if (!third.isMacroID()) {
|
|
while (compiler.getSourceManager().isMacroBodyExpansion(fourth)
|
|
&& isLastTokenOfImmediateMacroBodyExpansion(fourth, &fourth))
|
|
{}
|
|
} else if (compiler.getSourceManager().isMacroBodyExpansion(third)) {
|
|
while (compiler.getSourceManager().isMacroArgExpansion(fourth, &fourth)) {}
|
|
}
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(third)
|
|
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
|
|
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
|
|
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
|
|
{
|
|
third = compiler.getSourceManager().getSpellingLoc(third);
|
|
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
|
|
}
|
|
assert(third.isValid());
|
|
}
|
|
if (firstBegin.isMacroID() || secondBegin.isMacroID() || (third.isValid() && third.isMacroID())
|
|
|| (fourth.isValid() && fourth.isMacroID()))
|
|
{
|
|
if (isDebugMode()) {
|
|
report(
|
|
DiagnosticsEngine::Fatal,
|
|
"TODO: cannot rewrite C-style cast in macro, needs investigation",
|
|
expr->getExprLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
unsigned firstLen = Lexer::MeasureTokenLength(
|
|
firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
|
|
for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
|
|
l = l.getLocWithOffset(1))
|
|
{
|
|
unsigned n = Lexer::MeasureTokenLength(
|
|
l, compiler.getSourceManager(), compiler.getLangOpts());
|
|
if (n != 0) {
|
|
break;
|
|
}
|
|
++firstLen;
|
|
}
|
|
unsigned secondLen = Lexer::MeasureTokenLength(
|
|
secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
|
|
for (auto l = secondBegin.getLocWithOffset(std::max<unsigned>(secondLen, 1));;
|
|
l = l.getLocWithOffset(1))
|
|
{
|
|
unsigned n = Lexer::MeasureTokenLength(
|
|
l, compiler.getSourceManager(), compiler.getLangOpts());
|
|
if (n != 0) {
|
|
break;
|
|
}
|
|
++secondLen;
|
|
}
|
|
for (;;) {
|
|
auto l = secondBegin.getLocWithOffset(-1);
|
|
auto const c = compiler.getSourceManager().getCharacterData(l)[0];
|
|
if (c == '\n') {
|
|
if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
|
|
break;
|
|
}
|
|
} else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
|
|
break;
|
|
}
|
|
secondBegin = l;
|
|
++secondLen;
|
|
}
|
|
if (rewritten_.insert(firstBegin).second) {
|
|
if (!replaceText(firstBegin, firstLen, functional ? "" : "static_cast<")) {
|
|
if (isDebugMode()) {
|
|
report(
|
|
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #1, needs investigation",
|
|
firstBegin);
|
|
report(
|
|
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
if (!replaceText(secondBegin, secondLen, functional ? "" : ">")) {
|
|
//TODO: roll back
|
|
if (isDebugMode()) {
|
|
report(
|
|
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #2, needs investigation",
|
|
secondBegin);
|
|
report(
|
|
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
if (third.isValid()) {
|
|
if (rewritten_.insert(third).second) {
|
|
if (!insertTextBefore(third, "(")) {
|
|
//TODO: roll back
|
|
if (isDebugMode()) {
|
|
report(
|
|
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #3, needs investigation",
|
|
third);
|
|
report(
|
|
DiagnosticsEngine::Note, "when rewriting this C-style cast",
|
|
expr->getExprLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
if (!insertTextAfterToken(fourth, ")")) {
|
|
//TODO: roll back
|
|
if (isDebugMode()) {
|
|
report(
|
|
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #4, needs investigation",
|
|
third);
|
|
report(
|
|
DiagnosticsEngine::Note, "when rewriting this C-style cast",
|
|
expr->getExprLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CStyleCast::reportCast(ExplicitCastExpr const * expr, char const * performsHint) {
|
|
std::string incompFrom;
|
|
std::string incompTo;
|
|
if( expr->getCastKind() == CK_BitCast ) {
|
|
if (resolvePointers(expr->getSubExprAsWritten()->getType())
|
|
->isIncompleteType())
|
|
{
|
|
incompFrom = "incomplete ";
|
|
}
|
|
if (resolvePointers(expr->getType())->isIncompleteType()) {
|
|
incompTo = "incomplete ";
|
|
}
|
|
}
|
|
if (performsHint == nullptr) {
|
|
performsHint = recommendedFix(expr->getCastKind());
|
|
}
|
|
std::string performs;
|
|
if (performsHint != nullptr) {
|
|
performs = std::string(" (performs: ") + performsHint + ")";
|
|
}
|
|
report(
|
|
DiagnosticsEngine::Warning, "%select{C|Function}0-style cast from %1%2 to %3%4%5 (%6)",
|
|
expr->getSourceRange().getBegin())
|
|
<< isa<CXXFunctionalCastExpr>(expr)
|
|
<< incompFrom << expr->getSubExprAsWritten()->getType()
|
|
<< incompTo << expr->getTypeAsWritten() << performs
|
|
<< expr->getCastKindName()
|
|
<< expr->getSourceRange();
|
|
}
|
|
|
|
loplugin::Plugin::Registration< CStyleCast > X("cstylecast", true);
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|