loplugin:oncevar: empty strings
...which showed that checking the parent statement (which may be too large to)
in OnceVar::VisitDeclRefExpr is inadequate.
Change-Id: I07fb8ba9e2dfbd0c65a2723737d14abcddcefec4
Reviewed-on: https://gerrit.libreoffice.org/39757
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Tested-by: Stephan Bergmann <sbergman@redhat.com>
diff --git a/compilerplugins/clang/check.cxx b/compilerplugins/clang/check.cxx
index 1ea251b..8a468e4 100644
--- a/compilerplugins/clang/check.cxx
+++ b/compilerplugins/clang/check.cxx
@@ -14,6 +14,14 @@
namespace loplugin {
TypeCheck TypeCheck::NonConst() const {
return !type_.isNull() && !type_.isConstQualified()
? *this : TypeCheck();
// returning TypeCheck(type_.getUnqualifiedType()) instead of *this
// may look tempting, but could remove sugar we might be interested in
// checking for
}
TypeCheck TypeCheck::NonConstVolatile() const {
return
(!type_.isNull() && !type_.isConstQualified()
diff --git a/compilerplugins/clang/check.hxx b/compilerplugins/clang/check.hxx
index e2867c8..46b915f 100644
--- a/compilerplugins/clang/check.hxx
+++ b/compilerplugins/clang/check.hxx
@@ -37,6 +37,8 @@ public:
explicit operator bool() const { return !type_.isNull(); }
TypeCheck NonConst() const;
TypeCheck NonConstVolatile() const;
TypeCheck Const() const;
diff --git a/compilerplugins/clang/oncevar.cxx b/compilerplugins/clang/oncevar.cxx
index 7fae0e1..1810558 100644
--- a/compilerplugins/clang/oncevar.cxx
+++ b/compilerplugins/clang/oncevar.cxx
@@ -29,6 +29,15 @@ bool startsWith(const std::string& rStr, const char* pSubStr) {
return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
}
Expr const * lookThroughInitListExpr(Expr const * expr) {
if (auto const ile = dyn_cast<InitListExpr>(expr->IgnoreParenImpCasts())) {
if (ile->getNumInits() == 1) {
return ile->getInit(0);
}
}
return expr;
}
class ConstantValueDependentExpressionVisitor:
public ConstStmtVisitor<ConstantValueDependentExpressionVisitor, bool>
{
@@ -130,6 +139,107 @@ public:
}
}
bool VisitMemberExpr(MemberExpr const * expr) {
// ignore cases like:
// const OUString("xxx") xxx;
// rtl_something(xxx.pData);
// where we cannot inline the declaration.
if (isa<FieldDecl>(expr->getMemberDecl())) {
recordIgnore(expr);
}
return true;
}
bool VisitUnaryOperator(UnaryOperator const * expr) {
// if we take the address of it, or we modify it, ignore it
UnaryOperator::Opcode op = expr->getOpcode();
if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc
|| op == UO_PreDec || op == UO_PostDec)
{
recordIgnore(expr->getSubExpr());
}
return true;
}
bool VisitBinaryOperator(BinaryOperator const * expr) {
// if we assign it another value, or modify it, ignore it
BinaryOperator::Opcode op = expr->getOpcode();
if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
|| op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
|| op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
|| op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign)
{
recordIgnore(expr->getLHS());
}
return true;
}
bool VisitCallExpr(CallExpr const * expr) {
unsigned firstArg = 0;
if (auto const cmce = dyn_cast<CXXMemberCallExpr>(expr)) {
if (auto const e1 = cmce->getMethodDecl()) {
if (!(e1->isConst() || e1->isStatic())) {
recordIgnore(cmce->getImplicitObjectArgument());
}
} else if (auto const e2 = dyn_cast<BinaryOperator>(
cmce->getCallee()->IgnoreParenImpCasts()))
{
switch (e2->getOpcode()) {
case BO_PtrMemD:
case BO_PtrMemI:
if (!e2->getRHS()->getType()->getAs<MemberPointerType>()
->getPointeeType()->getAs<FunctionProtoType>()
->isConst())
{
recordIgnore(e2->getLHS());
}
break;
default:
break;
}
}
} else if (auto const coce = dyn_cast<CXXOperatorCallExpr>(expr)) {
if (auto const cmd = dyn_cast_or_null<CXXMethodDecl>(
coce->getDirectCallee()))
{
if (!cmd->isStatic()) {
assert(coce->getNumArgs() != 0);
if (!cmd->isConst()) {
recordIgnore(coce->getArg(0));
}
firstArg = 1;
}
}
}
// ignore those ones we are passing by reference
const FunctionDecl* calleeFunctionDecl = expr->getDirectCallee();
if (calleeFunctionDecl) {
for (unsigned i = firstArg; i < expr->getNumArgs(); ++i) {
if (i < calleeFunctionDecl->getNumParams()) {
QualType qt { calleeFunctionDecl->getParamDecl(i)->getType() };
if (loplugin::TypeCheck(qt).LvalueReference().NonConst()) {
recordIgnore(expr->getArg(i));
}
}
}
}
return true;
}
bool VisitCXXConstructExpr(CXXConstructExpr const * expr) {
// ignore those ones we are passing by reference
const CXXConstructorDecl* cxxConstructorDecl = expr->getConstructor();
for (unsigned i = 0; i < expr->getNumArgs(); ++i) {
if (i < cxxConstructorDecl->getNumParams()) {
QualType qt { cxxConstructorDecl->getParamDecl(i)->getType() };
if (loplugin::TypeCheck(qt).LvalueReference().NonConst()) {
recordIgnore(expr->getArg(i));
}
}
}
return true;
}
bool VisitDeclRefExpr( const DeclRefExpr* );
bool VisitVarDecl( const VarDecl* );
@@ -143,6 +253,38 @@ private:
return ConstantValueDependentExpressionVisitor(compiler.getASTContext())
.Visit(expr);
}
void recordIgnore(Expr const * expr) {
for (;;) {
expr = expr->IgnoreParenImpCasts();
if (auto const e = dyn_cast<MemberExpr>(expr)) {
if (isa<FieldDecl>(e->getMemberDecl())) {
expr = e->getBase();
continue;
}
}
if (auto const e = dyn_cast<ArraySubscriptExpr>(expr)) {
expr = e->getBase();
continue;
}
if (auto const e = dyn_cast<BinaryOperator>(expr)) {
if (e->getOpcode() == BO_PtrMemD) {
expr = e->getLHS();
continue;
}
}
break;
}
auto const dre = dyn_cast<DeclRefExpr>(expr);
if (dre == nullptr) {
return;
}
auto const var = dyn_cast<VarDecl>(dre->getDecl());
if (var == nullptr) {
return;
}
maVarDeclToIgnoreSet.insert(var);
}
};
bool OnceVar::VisitVarDecl( const VarDecl* varDecl )
@@ -150,6 +292,9 @@ bool OnceVar::VisitVarDecl( const VarDecl* varDecl )
if (ignoreLocation(varDecl)) {
return true;
}
if (auto const init = varDecl->getInit()) {
recordIgnore(lookThroughInitListExpr(init));
}
if (varDecl->isExceptionVariable() || isa<ParmVarDecl>(varDecl)) {
return true;
}
@@ -197,7 +342,9 @@ bool OnceVar::VisitVarDecl( const VarDecl* varDecl )
return true;
}
} else if (auto constructExpr = dyn_cast<CXXConstructExpr>(initExpr)) {
if (constructExpr->getNumArgs() > 0) {
if (constructExpr->getNumArgs() == 0) {
foundStringLiteral = true; // i.e., empty string
} else {
auto stringLit2 = dyn_cast<clang::StringLiteral>(constructExpr->getArg(0));
foundStringLiteral = stringLit2 != nullptr;
// ignore long literals, helps to make the code more legible
@@ -237,84 +384,6 @@ bool OnceVar::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
return true;
}
Stmt const * parent = parentStmt(declRefExpr);
// ignore cases like:
// const OUString("xxx") xxx;
// rtl_something(xxx.pData);
// and
// foo(&xxx);
// where we cannot inline the declaration.
auto const tc = loplugin::TypeCheck(varDecl->getType());
if (tc.Class("OUString").Namespace("rtl").GlobalNamespace()
&& parent && (isa<MemberExpr>(parent) || isa<UnaryOperator>(parent)))
{
maVarDeclToIgnoreSet.insert(varDecl);
return true;
}
// if we take the address of it, or we modify it, ignore it
if (auto unaryOp = dyn_cast_or_null<UnaryOperator>(parent)) {
UnaryOperator::Opcode op = unaryOp->getOpcode();
if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc
|| op == UO_PreDec || op == UO_PostDec)
{
maVarDeclToIgnoreSet.insert(varDecl);
return true;
}
}
// if we assign it another value, or modify it, ignore it
if (auto binaryOp = dyn_cast_or_null<BinaryOperator>(parent)) {
if (binaryOp->getLHS() == declRefExpr)
{
BinaryOperator::Opcode op = binaryOp->getOpcode();
if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
|| op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
|| op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
|| op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign)
{
maVarDeclToIgnoreSet.insert(varDecl);
return true;
}
}
}
// ignore those ones we are passing by reference
if (auto callExpr = dyn_cast_or_null<CallExpr>(parent)) {
const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee();
if (calleeFunctionDecl) {
for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
if (callExpr->getArg(i) == declRefExpr) {
if (i < calleeFunctionDecl->getNumParams()) {
QualType qt { calleeFunctionDecl->getParamDecl(i)->getType() };
if (loplugin::TypeCheck(qt).LvalueReference()) {
maVarDeclToIgnoreSet.insert(varDecl);
return true;
}
}
break;
}
}
}
}
// ignore those ones we are passing by reference
if (auto cxxConstructExpr = dyn_cast_or_null<CXXConstructExpr>(parent)) {
const CXXConstructorDecl* cxxConstructorDecl = cxxConstructExpr->getConstructor();
for (unsigned i = 0; i < cxxConstructExpr->getNumArgs(); ++i) {
if (cxxConstructExpr->getArg(i) == declRefExpr) {
if (i < cxxConstructorDecl->getNumParams()) {
QualType qt { cxxConstructorDecl->getParamDecl(i)->getType() };
if (loplugin::TypeCheck(qt).LvalueReference()) {
maVarDeclToIgnoreSet.insert(varDecl);
return true;
}
}
break;
}
}
return true;
}
if (maVarUsesMap.find(varDecl) == maVarUsesMap.end()) {
maVarUsesMap[varDecl] = 1;
maVarUseSourceRangeMap[varDecl] = declRefExpr->getSourceRange();