/* -*- 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/.
*/
/** Simplify boolean expressions involving smart pointers e.g. if (x.get()) can be if (x)
*/ //TODO: Make this a shared plugin for Clang 12 (and possibly even for older Clang) again.
namespace
{ class SimplifyPointerToBool : public loplugin::FilteringRewritePlugin<SimplifyPointerToBool>
{ public: explicit SimplifyPointerToBool(loplugin::InstantiationData const& data)
: FilteringRewritePlugin(data)
{
}
virtualvoid run() override
{ if (preRun())
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
// Get the source range starting at the "."or "->" (plus any preceding non-comment white space):
SourceRange getCallSourceRange(CXXMemberCallExpr const* expr) const
{ if (expr->getImplicitObjectArgument() == nullptr)
{ //TODO: Arguably, such a call of a `get` member function from within some member // function (so that syntactically no caller is mentioned) should already be handled // differently when reporting it (just "drop the get()" does not make sense), instead of // being filtered here: return {};
} // CXXMemberCallExpr::getExprLoc happens to return the location following the "." or "->": auto start = compiler.getSourceManager().getSpellingLoc(expr->getExprLoc()); if (!start.isValid())
{ return {};
} for (;;)
{
start = Lexer::GetBeginningOfToken(start.getLocWithOffset(-1),
compiler.getSourceManager(), compiler.getLangOpts()); autoconst s = StringRef(compiler.getSourceManager().getCharacterData(start),
Lexer::MeasureTokenLength(start, compiler.getSourceManager(),
compiler.getLangOpts())); if (s.empty() || compat::starts_with(s, "\\\n"))
{ continue;
} if (s != "." && s != "->")
{ return {};
} break;
} for (;;)
{ auto start1 = Lexer::GetBeginningOfToken(
start.getLocWithOffset(-1), compiler.getSourceManager(), compiler.getLangOpts()); autoconst s = StringRef(compiler.getSourceManager().getCharacterData(start1),
Lexer::MeasureTokenLength(start1, compiler.getSourceManager(),
compiler.getLangOpts())); if (!(s.empty() || compat::starts_with(s, "\\\n")))
{ break;
}
start = start1;
} return SourceRange(start, compiler.getSourceManager().getSpellingLoc(expr->getEndLoc()));
}
//TODO: There are some more places where an expression is contextually converted to bool, but // those are probably not relevant for our needs here.
std::deque<Expr const*> contextuallyConvertedExprs_;
};
bool SimplifyPointerToBool::VisitImplicitCastExpr(ImplicitCastExpr const* castExpr)
{ if (ignoreLocation(castExpr)) returntrue; if (castExpr->getCastKind() != CK_PointerToBoolean) returntrue; auto memberCallExpr
= dyn_cast<CXXMemberCallExpr>(castExpr->getSubExpr()->IgnoreParenImpCasts()); if (!memberCallExpr) returntrue; auto methodDecl = memberCallExpr->getMethodDecl(); if (!methodDecl || !methodDecl->getIdentifier() || methodDecl->getName() != "get") returntrue; // castExpr->dump(); // methodDecl->getParent()->getTypeForDecl()->dump(); if (!loplugin::isSmartPointerType(memberCallExpr->getImplicitObjectArgument())) returntrue; // if (isa<CXXOperatorCallExpr>(callExpr)) // return true; // const FunctionDecl* functionDecl; // if (isa<CXXMemberCallExpr>(callExpr)) // { // functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl(); // } // else // { // functionDecl = callExpr->getDirectCallee(); // } // if (!functionDecl) // return true; // // unsigned len = std::min(callExpr->getNumArgs(), functionDecl->getNumParams()); // for (unsigned i = 0; i < len; ++i) // { // auto param = functionDecl->getParamDecl(i); // auto paramTC = loplugin::TypeCheck(param->getType()); // if (!paramTC.AnyBoolean()) // continue; // auto arg = callExpr->getArg(i)->IgnoreImpCasts(); // auto argTC = loplugin::TypeCheck(arg->getType()); // if (argTC.AnyBoolean()) // continue; // // sal_Bool is sometimes disguised // if (isa<SubstTemplateTypeParmType>(arg->getType())) // if (arg->getType()->getUnqualifiedDesugaredType()->isSpecificBuiltinType( // clang::BuiltinType::UChar)) // continue; // if (arg->getType()->isDependentType()) // continue; // if (arg->getType()->isIntegerType()) // { // auto ret = getCallValue(arg); // if (ret.hasValue() && (ret.getValue() == 1 || ret.getValue() == 0)) // continue; // // something like: priv->m_nLOKFeatures & LOK_FEATURE_DOCUMENT_PASSWORD // if (isa<BinaryOperator>(arg->IgnoreParenImpCasts())) // continue; // // something like: pbEmbolden ? FcTrue : FcFalse // if (isa<ConditionalOperator>(arg->IgnoreParenImpCasts())) // continue; // } if (isContextuallyConverted(memberCallExpr))
{ if (rewriter)
{ autoconst range = getCallSourceRange(memberCallExpr); if (range.isValid() && removeText(range))
{ returntrue;
}
}
report(DiagnosticsEngine::Warning, "simplify, drop the get()", memberCallExpr->getExprLoc())
<< memberCallExpr->getSourceRange();
} elseif (isa<ParenExpr>(castExpr->getSubExpr()))
{ if (rewriter)
{ autoconst loc
= compiler.getSourceManager().getSpellingLoc(memberCallExpr->getBeginLoc()); autoconst range = getCallSourceRange(memberCallExpr); if (loc.isValid() && range.isValid() && insertText(loc, "bool") && removeText(range))
{ //TODO: atomically only change both or neither returntrue;
}
}
report(DiagnosticsEngine::Warning, "simplify, drop the get() and turn the surrounding parentheses into a functional " "cast to bool",
memberCallExpr->getExprLoc())
<< memberCallExpr->getSourceRange();
report(DiagnosticsEngine::Note, "surrounding parentheses here",
castExpr->getSubExpr()->getExprLoc())
<< castExpr->getSubExpr()->getSourceRange();
} else
{ if (rewriter)
{ autoconst loc
= compiler.getSourceManager().getSpellingLoc(memberCallExpr->getBeginLoc()); autoconst range = getCallSourceRange(memberCallExpr); if (loc.isValid() && range.isValid() && insertText(loc, "bool(")
&& replaceText(range, ")"))
{ //TODO: atomically only change both or neither returntrue;
}
}
report(DiagnosticsEngine::Warning, "simplify, drop the get() and wrap the expression in a functional cast to bool",
memberCallExpr->getExprLoc())
<< memberCallExpr->getSourceRange();
} // report(DiagnosticsEngine::Note, "method here", param->getLocation()) // << param->getSourceRange(); returntrue;
}
bool SimplifyPointerToBool::VisitBinaryOperator(BinaryOperator const* binOp)
{ if (ignoreLocation(binOp)) returntrue; auto opCode = binOp->getOpcode(); if (opCode != BO_EQ && opCode != BO_NE) returntrue; const Expr* possibleMemberCall = nullptr; if (isa<CXXNullPtrLiteralExpr>(binOp->getLHS()->IgnoreParenImpCasts()))
possibleMemberCall = binOp->getRHS(); elseif (isa<CXXNullPtrLiteralExpr>(binOp->getRHS()->IgnoreParenImpCasts()))
possibleMemberCall = binOp->getLHS(); else returntrue; auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(possibleMemberCall); if (!memberCallExpr) returntrue; auto methodDecl = memberCallExpr->getMethodDecl(); if (!methodDecl || !methodDecl->getIdentifier() || methodDecl->getName() != "get") returntrue; if (!loplugin::isSmartPointerType(memberCallExpr->getImplicitObjectArgument())) returntrue;
report(DiagnosticsEngine::Warning,
std::string("simplify, convert to ") + (opCode == BO_EQ ? "'!x'" : "'x'"),
binOp->getExprLoc())
<< binOp->getSourceRange(); returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.