summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/stringconstant.cxx
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2015-11-06 12:33:41 +0100
committerStephan Bergmann <sbergman@redhat.com>2015-11-06 12:33:41 +0100
commit938f670928683ae3251119c896a894d7204b24af (patch)
tree431902253bf39b702c7e22fcd3829413763650a9 /compilerplugins/clang/stringconstant.cxx
parentd964a43f37056d4f478fb2c69fa1637b6a14ed26 (diff)
loplugin:stringconstant: elide explicit ctor usage
Change-Id: I962db9583ef9cada42a61b6a95eeea818fceeead
Diffstat (limited to 'compilerplugins/clang/stringconstant.cxx')
-rw-r--r--compilerplugins/clang/stringconstant.cxx227
1 files changed, 161 insertions, 66 deletions
diff --git a/compilerplugins/clang/stringconstant.cxx b/compilerplugins/clang/stringconstant.cxx
index 79d7c61c7156..8fbed25a975f 100644
--- a/compilerplugins/clang/stringconstant.cxx
+++ b/compilerplugins/clang/stringconstant.cxx
@@ -7,6 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+#include <algorithm>
#include <cassert>
#include <limits>
#include <stack>
@@ -48,6 +49,36 @@ SourceLocation getMemberLocation(Expr const * expr) {
return e2 == nullptr ? expr->getExprLoc()/*TODO*/ : e2->getMemberLoc();
}
+bool isLhsOfAssignment(FunctionDecl const * decl, unsigned parameter) {
+ if (parameter != 0) {
+ return false;
+ }
+ auto oo = decl->getOverloadedOperator();
+ return oo == OO_Equal
+ || (oo >= OO_PlusEqual && oo <= OO_GreaterGreaterEqual);
+}
+
+bool hasOverloads(FunctionDecl const * decl, unsigned arguments) {
+ int n = 0;
+ auto ctx = decl->getDeclContext();
+ if (ctx->getDeclKind() == Decl::LinkageSpec) {
+ ctx = ctx->getParent();
+ }
+ auto res = ctx->lookup(decl->getDeclName());
+ for (auto d = compat::begin(res); d != compat::end(res); ++d) {
+ FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
+ if (f != nullptr && f->getMinRequiredArguments() <= arguments
+ && f->getNumParams() >= arguments)
+ {
+ ++n;
+ if (n == 2) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
class StringConstant:
public RecursiveASTVisitor<StringConstant>, public loplugin::RewritePlugin
{
@@ -104,9 +135,8 @@ private:
TreatEmpty treatEmpty);
void handleOUStringCtor(
- CallExpr const * expr, unsigned arg, std::string const & qname);
- void handleOUStringCtor2(
- CallExpr const * expr, unsigned arg, std::string const & qname);
+ CallExpr const * expr, unsigned arg, std::string const & qname,
+ bool explicitFunctionalCastNotation);
std::stack<Expr const *> calls_;
};
@@ -195,6 +225,35 @@ bool StringConstant::VisitCallExpr(CallExpr const * expr) {
return true;
}
std::string qname(fdecl->getQualifiedNameAsString());
+ for (unsigned i = 0; i != fdecl->getNumParams(); ++i) {
+ auto t = fdecl->getParamDecl(i)->getType();
+ if (t->isLValueReferenceType()
+ && t->getAs<SubstTemplateTypeParmType>() == nullptr)
+ {
+ t = t->getAs<LValueReferenceType>()->getPointeeType();
+ if (t.isConstQualified() && !t.isVolatileQualified()
+ && t->isClassType()
+ && t->getAs<SubstTemplateTypeParmType>() == nullptr)
+ {
+ auto td = t->getAsTagDecl();
+ auto id = td->getIdentifier();
+ if (id != nullptr && id->isStr("OUString")) {
+ auto nd = dyn_cast<NamespaceDecl>(td->getParent());
+ if (nd != nullptr) {
+ id = nd->getIdentifier();
+ if (id != nullptr && id->isStr("rtl")) {
+ //TODO: check rtl is outermost namespace
+ if (!(isLhsOfAssignment(fdecl, i)
+ || hasOverloads(fdecl, expr->getNumArgs())))
+ {
+ handleOUStringCtor(expr, i, qname, true);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
//TODO: u.compareToAscii("foo") -> u.???("foo")
//TODO: u.compareToIgnoreAsciiCaseAscii("foo") -> u.???("foo")
if (qname == "rtl::OUString::createFromAscii" && fdecl->getNumParams() == 1)
@@ -305,69 +364,69 @@ bool StringConstant::VisitCallExpr(CallExpr const * expr) {
if (qname == "rtl::OUString::reverseCompareTo"
&& fdecl->getNumParams() == 1)
{
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::equalsIgnoreAsciiCase"
&& fdecl->getNumParams() == 1)
{
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::match" && fdecl->getNumParams() == 2) {
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::matchIgnoreAsciiCase"
&& fdecl->getNumParams() == 2)
{
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::startsWith" && fdecl->getNumParams() == 2) {
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::startsWithIgnoreAsciiCase"
&& fdecl->getNumParams() == 2)
{
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::endsWith" && fdecl->getNumParams() == 2) {
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::endsWithIgnoreAsciiCase"
&& fdecl->getNumParams() == 2)
{
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::indexOf" && fdecl->getNumParams() == 2) {
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::lastIndexOf" && fdecl->getNumParams() == 1) {
- handleOUStringCtor(expr, 0, qname);
+ handleOUStringCtor(expr, 0, qname, false);
return true;
}
if (qname == "rtl::OUString::replaceFirst" && fdecl->getNumParams() == 3) {
- handleOUStringCtor(expr, 0, qname);
- handleOUStringCtor(expr, 1, qname);
+ handleOUStringCtor(expr, 0, qname, false);
+ handleOUStringCtor(expr, 1, qname, false);
return true;
}
if (qname == "rtl::OUString::replaceAll"
&& (fdecl->getNumParams() == 2 || fdecl->getNumParams() == 3))
{
- handleOUStringCtor(expr, 0, qname);
- handleOUStringCtor(expr, 1, qname);
+ handleOUStringCtor(expr, 0, qname, false);
+ handleOUStringCtor(expr, 1, qname, false);
return true;
}
if (qname == "rtl::OUString::operator+=" && fdecl->getNumParams() == 1) {
handleOUStringCtor(
expr, dyn_cast<CXXOperatorCallExpr>(expr) == nullptr ? 0 : 1,
- qname);
+ qname, false);
return true;
}
if (qname == "rtl::OUString::equals" && fdecl->getNumParams() == 1) {
@@ -549,12 +608,6 @@ bool StringConstant::VisitCallExpr(CallExpr const * expr) {
TreatEmpty::Error);
return true;
}
- // For places where we are calling a method with a 'const OUString&' param
- for (unsigned i=0; i < fdecl->getNumParams(); ++i)
- {
- if (fdecl->getParamDecl(i)->getType().getAsString() == "const ::rtl::OUString &")
- handleOUStringCtor2(expr, i, qname);
- }
return true;
}
@@ -1226,11 +1279,16 @@ void StringConstant::handleCharLen(
}
void StringConstant::handleOUStringCtor(
- CallExpr const * expr, unsigned arg, std::string const & qname)
+ CallExpr const * expr, unsigned arg, std::string const & qname,
+ bool explicitFunctionalCastNotation)
{
auto e0 = expr->getArg(arg)->IgnoreParenImpCasts();
auto e1 = dyn_cast<CXXFunctionalCastExpr>(e0);
- if (e1 != nullptr) {
+ if (e1 == nullptr) {
+ if (explicitFunctionalCastNotation) {
+ return;
+ }
+ } else {
e0 = e1->getSubExpr()->IgnoreParenImpCasts();
}
auto e2 = dyn_cast<CXXBindTemporaryExpr>(e0);
@@ -1261,13 +1319,18 @@ void StringConstant::handleOUStringCtor(
&& e3->getArg(0)->IgnoreParenImpCasts()->isIntegerConstantExpr(
res, compiler.getASTContext()))
{
- if (res.getZExtValue() <= 127) {
- report(
- DiagnosticsEngine::Warning,
- ("in call of %0, replace OUString constructed from an ASCII"
- " char constant with a string literal"),
- e3->getExprLoc())
- << qname << expr->getSourceRange();
+ // It may not be easy to rewrite OUString(c), esp. given there is no
+ // OUString ctor taking an OUStringLiteral1 arg, so don't warn there:
+ if (!explicitFunctionalCastNotation) {
+ uint64_t n = res.getZExtValue();
+ if (n != 0 && n <= 127) {
+ report(
+ DiagnosticsEngine::Warning,
+ ("in call of %0, replace OUString constructed from an ASCII"
+ " char constant with a string literal"),
+ e3->getExprLoc())
+ << qname << expr->getSourceRange();
+ }
}
return;
}
@@ -1284,39 +1347,71 @@ void StringConstant::handleOUStringCtor(
return;
}
//TODO: non, emb, trm
- report(
- DiagnosticsEngine::Warning,
- ("in call of %0, replace OUString constructed from a string literal"
- " directly with the string literal"),
- e3->getExprLoc())
- << qname << expr->getSourceRange();
-}
-
-// For places where we are calling a method with an 'const OUString&' param
-//
-void StringConstant::handleOUStringCtor2(
- CallExpr const * expr, unsigned arg, std::string const & qname)
-{
- auto e0 = expr->getArg(arg)->IgnoreParenImpCasts();
- auto e1 = dyn_cast<CXXFunctionalCastExpr>(e0);
- if (e1 == nullptr) {
- return;
- }
- e0 = e1->getSubExpr()->IgnoreParenImpCasts();
- auto e2 = dyn_cast<CXXBindTemporaryExpr>(e0);
- if (e2 == nullptr) {
- return;
- }
- auto e3 = dyn_cast<CXXConstructExpr>(
- e2->getSubExpr()->IgnoreParenImpCasts());
- if (e3 == nullptr) {
- return;
- }
- if (e3->getNumArgs() == 1)
- {
- std::string s = e3->getArg(0)->getType().getAsString();
- if (s == "sal_Unicode" || s == "char")
- return;
+ if (rewriter != nullptr) {
+ auto loc1 = e3->getLocStart();
+ auto range = e3->getParenOrBraceRange();
+ if (loc1.isFileID() && range.getBegin().isFileID()
+ && range.getEnd().isFileID())
+ {
+ auto loc2 = range.getBegin();
+ for (bool first = true;; first = false) {
+ unsigned n = Lexer::MeasureTokenLength(
+ loc2, compiler.getSourceManager(), compiler.getLangOpts());
+ if (!first) {
+ StringRef s(
+ compiler.getSourceManager().getCharacterData(loc2), n);
+ while (s.startswith("\\\n")) {
+ s = s.drop_front(2);
+ while (!s.empty()
+ && (s.front() == ' ' || s.front() == '\t'
+ || s.front() == '\n' || s.front() == '\v'
+ || s.front() == '\f'))
+ {
+ s = s.drop_front(1);
+ }
+ }
+ if (!(s.empty() || s.startswith("/*") || s.startswith("//")
+ || s == "\\"))
+ {
+ break;
+ }
+ }
+ loc2 = loc2.getLocWithOffset(std::max<unsigned>(n, 1));
+ }
+ auto loc3 = range.getEnd();
+ for (;;) {
+ auto l = Lexer::GetBeginningOfToken(
+ loc3.getLocWithOffset(-1), compiler.getSourceManager(),
+ compiler.getLangOpts());
+ unsigned n = Lexer::MeasureTokenLength(
+ l, compiler.getSourceManager(), compiler.getLangOpts());
+ StringRef s(compiler.getSourceManager().getCharacterData(l), n);
+ while (s.startswith("\\\n")) {
+ s = s.drop_front(2);
+ while (!s.empty()
+ && (s.front() == ' ' || s.front() == '\t'
+ || s.front() == '\n' || s.front() == '\v'
+ || s.front() == '\f'))
+ {
+ s = s.drop_front(1);
+ }
+ }
+ if (!(s.empty() || s.startswith("/*") || s.startswith("//")
+ || s == "\\"))
+ {
+ break;
+ }
+ loc3 = l;
+ }
+ if (removeText(CharSourceRange(SourceRange(loc1, loc2), false))) {
+ if (removeText(SourceRange(loc3, range.getEnd()))) {
+ return;
+ }
+ report(DiagnosticsEngine::Fatal, "Corrupt rewrite", loc3)
+ << expr->getSourceRange();
+ return;
+ }
+ }
}
report(
DiagnosticsEngine::Warning,