compilerplugins/clang/test/unnecessaryparen.cxx |   23 +++++++++
 compilerplugins/clang/unnecessaryparen.cxx      |   59 ++++++++++++++++++++++--
 2 files changed, 79 insertions(+), 3 deletions(-)

New commits:
commit e235e4bb7ce79d6738e1e6267c965f71960466a6
Author:     Stephan Bergmann <[email protected]>
AuthorDate: Wed Aug 23 22:09:07 2023 +0200
Commit:     Stephan Bergmann <[email protected]>
CommitDate: Mon Aug 28 19:55:57 2023 +0200

    Adapt loplugin:unnecessaryparen to user-defined string literals
    
    Change-Id: Ieab59fa456bce13145b61cb5e8b2fb9ff9b4c573
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156193
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <[email protected]>

diff --git a/compilerplugins/clang/test/unnecessaryparen.cxx 
b/compilerplugins/clang/test/unnecessaryparen.cxx
index ccc2b4ce6556..89ca84da6ab2 100644
--- a/compilerplugins/clang/test/unnecessaryparen.cxx
+++ b/compilerplugins/clang/test/unnecessaryparen.cxx
@@ -7,6 +7,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <cstddef>
 #include <memory>
 #include <string>
 #include <rtl/ustring.hxx>
@@ -34,6 +35,13 @@ template <> struct typed_flags<BrowseMode> : 
is_typed_flags<BrowseMode, 0xf>
 };
 }
 
+void f(char const *);
+char const * operator ""_s1(char const *, std::size_t);
+#if __cplusplus >= 202002L
+struct Str { constexpr Str(char const *) {} };
+template<Str> char const * operator ""_s2();
+#endif
+
 int main()
 {
     int x = 1;
@@ -115,6 +123,21 @@ int main()
     (void)nBits;
 
     OUString::number((v2+1)); // expected-error {{parentheses immediately 
inside single-arg call [loplugin:unnecessaryparen]}}
+
+    (void) ("foo"); // expected-error {{unnecessary parentheses around 
single-token string literal [loplugin:unnecessaryparen]}}
+    (void) ("foo" "bar");
+    f(("foo")); // expected-error {{parentheses immediately inside single-arg 
call [loplugin:unnecessaryparen]}}
+    f(("foo" "bar"));
+    (void) ("foo"_s1); // expected-error {{unnecessary parentheses around 
single-token string literal [loplugin:unnecessaryparen]}}
+    (void) ("foo" "bar"_s1);
+    f(("foo"_s1)); // expected-error {{parentheses immediately inside 
single-arg call [loplugin:unnecessaryparen]}}
+    f(("foo" "bar"_s1));
+#if __cplusplus >= 202002L
+    (void) ("foo"_s2); //TODO: expected error {{unnecessary parentheses around 
single-token string literal [loplugin:unnecessaryparen]}}
+    (void) ("foo" "bar"_s2);
+    f(("foo"_s2)); // TODO: expected error {{parentheses immediately inside 
single-arg call [loplugin:unnecessaryparen]}}
+    f(("foo" "bar"_s2));
+#endif
 };
 
 struct B { operator bool() const; };
diff --git a/compilerplugins/clang/unnecessaryparen.cxx 
b/compilerplugins/clang/unnecessaryparen.cxx
index 1d11aca4ab47..f31d570b9bdd 100644
--- a/compilerplugins/clang/unnecessaryparen.cxx
+++ b/compilerplugins/clang/unnecessaryparen.cxx
@@ -143,6 +143,55 @@ private:
 
     bool removeParens(ParenExpr const * expr);
 
+    // Returns 0 if not a string literal at all:
+    unsigned getStringLiteralTokenCount(Expr const * expr, Expr const * 
parenExpr) {
+        if (auto const e = dyn_cast<clang::StringLiteral>(expr)) {
+            if (parenExpr == nullptr || !isPrecededBy_BAD_CAST(parenExpr)) {
+                return e->getNumConcatenated();
+            }
+        } else if (auto const e = dyn_cast<UserDefinedLiteral>(expr)) {
+            clang::StringLiteral const * lit = nullptr;
+            switch (e->getLiteralOperatorKind()) {
+            case UserDefinedLiteral::LOK_Template:
+                {
+                    auto const decl = e->getDirectCallee();
+                    assert(decl != nullptr);
+                    auto const args = decl->getTemplateSpecializationArgs();
+                    assert(args != nullptr);
+                    if (args->size() == 1 && (*args)[0].getKind() == 
TemplateArgument::Declaration)
+                    {
+                        if (auto const d
+                            = 
dyn_cast<TemplateParamObjectDecl>((*args)[0].getAsDecl()))
+                        {
+                            if (d->getValue().isStruct() || 
d->getValue().isUnion()) {
+                                //TODO: There appears to be no way currently 
to get at the original
+                                // clang::StringLiteral expression from which 
this struct/union
+                                // non-type template argument was 
constructued, so no way to tell
+                                // whether it was written as a single literal 
(=> in which case we
+                                // should warn about unnecessary parentheses) 
or as a concatenation
+                                // of multiple literals (=> in which case we 
should not warn).  So
+                                // be conservative and not warn at all (by 
pretending to have more
+                                // than one token):
+                                return 2;
+                            }
+                        }
+                    }
+                    break;
+                }
+            case UserDefinedLiteral::LOK_String:
+                assert(e->getNumArgs() == 2);
+                lit = 
dyn_cast<clang::StringLiteral>(e->getArg(0)->IgnoreImplicit());
+                break;
+            default:
+                break;
+            }
+            if (lit != nullptr) {
+                return lit->getNumConcatenated();
+            }
+        }
+        return 0;
+    }
+
     std::unordered_set<ParenExpr const *> handled_;
 };
 
@@ -217,8 +266,8 @@ bool UnnecessaryParen::VisitParenExpr(const ParenExpr* 
parenExpr)
             parenExpr->getBeginLoc())
             << parenExpr->getSourceRange();
         handled_.insert(parenExpr);
-    } else if (auto const e = dyn_cast<clang::StringLiteral>(subExpr)) {
-        if (e->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr)) 
{
+    } else if (isa<clang::StringLiteral>(subExpr) || 
isa<UserDefinedLiteral>(subExpr)) {
+        if (getStringLiteralTokenCount(subExpr, parenExpr) == 1) {
             report(
                 DiagnosticsEngine::Warning,
                 "unnecessary parentheses around single-token string literal",
@@ -320,7 +369,8 @@ bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* 
returnStmt)
 
     // only non-operator-calls for now
     auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
-    if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
+    if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr)
+        && !isa<UserDefinedLiteral>(subExpr))
     {
         report(
             DiagnosticsEngine::Warning, "parentheses immediately inside return 
statement",
@@ -384,6 +434,9 @@ bool UnnecessaryParen::VisitCallExpr(const CallExpr* 
callExpr)
     auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
     if (binaryOp && binaryOp->getOpcode() == BO_Assign)
         return true;
+    if (getStringLiteralTokenCount(parenExpr->getSubExpr()->IgnoreImplicit(), 
nullptr) > 1) {
+        return true;
+    }
     report(
         DiagnosticsEngine::Warning, "parentheses immediately inside single-arg 
call",
         parenExpr->getBeginLoc())

Reply via email to