[libcxx-commits] [libcxxabi] ad46cf1 - [demangler] Stricter NestedName parsing

Nathan Sidwell via libcxx-commits libcxx-commits at lists.llvm.org
Mon Feb 7 08:21:13 PST 2022


Author: Nathan Sidwell
Date: 2022-02-07T08:20:45-08:00
New Revision: ad46cf14d408018edad17defe6d2911093d35503

URL: https://github.com/llvm/llvm-project/commit/ad46cf14d408018edad17defe6d2911093d35503
DIFF: https://github.com/llvm/llvm-project/commit/ad46cf14d408018edad17defe6d2911093d35503.diff

LOG: [demangler] Stricter NestedName parsing

The parsing of nested names is a little lax.  This corrects that.

1) The 'L' local name prefix cannot appear before a NestedName -- only
within it.  Let's remove that check from parseName, and then adjust
parseUnscopedName to allow it with or without the 'St' prefix.

2) In a nested name, a <template-param>, <decltype> or <substitution>
can only appear as the first element.  Let's enforce that.  Note I do
not remove these from the loop, to make the change easier to follow
(such a change will come later).

3) Given that, there's no need to special case 'St' outside of the
loop, handle it with the other 'S' elements.

4) There's no need to reset 'EndsWithTemplateArgs' after each
non-template-arg component.  Rather, always clear it and then set it
in the template-args case.

5) An template-args cannot immediately follow a template-args.

6) The parsing of a CDtor name with ABITags would attach the tags to
the NestedName node, rather than the CDTor node.  This is different to
how ABITags are attached to an unscopedName.  Make it consistent.

7) We remain with only CDTor and UnscopedName requireing construction
of a NestedName, so let's drop the PushComponent lambda.

8) Add some tests to catch the new rejected manglings.

Reviewed By: ChuanqiXu

Differential Revision: https://reviews.llvm.org/D118132

Added: 
    

Modified: 
    libcxxabi/src/demangle/ItaniumDemangle.h
    libcxxabi/test/test_demangle.pass.cpp
    llvm/include/llvm/Demangle/ItaniumDemangle.h

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index c8a77adc9faf..d0eaf6d66480 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -2572,8 +2572,6 @@ const char* parse_discriminator(const char* first, const char* last);
 //                          ::= <substitution>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) {
-  consumeIf('L'); // extension
-
   if (look() == 'N')
     return getDerived().parseNestedName(State);
   if (look() == 'Z')
@@ -2649,15 +2647,14 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
   return make<LocalName>(Encoding, Entity);
 }
 
-// <unscoped-name> ::= <unqualified-name>
-//                 ::= St <unqualified-name>   # ::std::
-// extension       ::= StL<unqualified-name>
+// <unscoped-name> ::= [L]* <unqualified-name>
+//                 ::= St [L]* <unqualified-name>   # ::std::
+// [*] extension
 template <typename Derived, typename Alloc>
 Node *
 AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State) {
   bool IsStd = consumeIf("St");
-  if (IsStd)
-    consumeIf('L');
+  consumeIf('L');
 
   Node *Result = getDerived().parseUnqualifiedName(State);
   if (Result == nullptr)
@@ -3148,14 +3145,14 @@ AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar,
 // <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
 //               ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
 //
-// <prefix> ::= <prefix> <unqualified-name>
+// <prefix> ::= <prefix> [L]* <unqualified-name>
 //          ::= <template-prefix> <template-args>
 //          ::= <template-param>
 //          ::= <decltype>
 //          ::= # empty
 //          ::= <substitution>
 //          ::= <prefix> <data-member-prefix>
-//  extension ::= L
+// [*] extension
 //
 // <data-member-prefix> := <member source-name> [<template-args>] M
 //
@@ -3175,88 +3172,85 @@ AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {
     if (State) State->ReferenceQualifier = FrefQualRValue;
   } else if (consumeIf('R')) {
     if (State) State->ReferenceQualifier = FrefQualLValue;
-  } else
+  } else {
     if (State) State->ReferenceQualifier = FrefQualNone;
-
-  Node *SoFar = nullptr;
-  auto PushComponent = [&](Node *Comp) {
-    if (!Comp) return false;
-    if (SoFar) SoFar = make<NestedName>(SoFar, Comp);
-    else       SoFar = Comp;
-    if (State) State->EndsWithTemplateArgs = false;
-    return SoFar != nullptr;
-  };
-
-  if (consumeIf("St")) {
-    SoFar = make<NameType>("std");
-    if (!SoFar)
-      return nullptr;
   }
 
+  Node *SoFar = nullptr;
   while (!consumeIf('E')) {
     consumeIf('L'); // extension
 
-    // <data-member-prefix> := <member source-name> [<template-args>] M
     if (consumeIf('M')) {
+      // <data-member-prefix> := <member source-name> [<template-args>] M
       if (SoFar == nullptr)
         return nullptr;
       continue;
     }
 
-    //          ::= <template-param>
-    if (look() == 'T') {
-      if (!PushComponent(getDerived().parseTemplateParam()))
-        return nullptr;
-      Subs.push_back(SoFar);
-      continue;
-    }
+    if (State)
+      // Only set end-with-template on the case that does that.
+      State->EndsWithTemplateArgs = false;
 
-    //          ::= <template-prefix> <template-args>
-    if (look() == 'I') {
+    if (look() == 'T') {
+      //          ::= <template-param>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      SoFar = getDerived().parseTemplateParam();
+    } else if (look() == 'I') {
+      //          ::= <template-prefix> <template-args>
+      if (SoFar == nullptr)
+        return nullptr; // Must have a prefix.
       Node *TA = getDerived().parseTemplateArgs(State != nullptr);
-      if (TA == nullptr || SoFar == nullptr)
-        return nullptr;
-      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
-      if (!SoFar)
-        return nullptr;
-      if (State) State->EndsWithTemplateArgs = true;
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <decltype>
-    if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
-      if (!PushComponent(getDerived().parseDecltype()))
+      if (TA == nullptr)
         return nullptr;
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <substitution>
-    if (look() == 'S' && look(1) != 't') {
-      Node *S = getDerived().parseSubstitution();
-      if (!PushComponent(S))
+      if (SoFar->getKind() == Node::KNameWithTemplateArgs)
+        // Semantically <template-args> <template-args> cannot be generated by a
+        // C++ entity.  There will always be [something like] a name between
+        // them.
         return nullptr;
-      if (SoFar != S)
-        Subs.push_back(S);
-      continue;
-    }
-
-    // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
-    if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
+      if (State)
+        State->EndsWithTemplateArgs = true;
+      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
+    } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
+      //          ::= <decltype>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      SoFar = getDerived().parseDecltype();
+    } else if (look() == 'S') {
+      //          ::= <substitution>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      if (look(1) == 't') {
+        // parseSubstition does not handle 'St'.
+        First += 2;
+        SoFar = make<NameType>("std");
+      } else {
+        SoFar = getDerived().parseSubstitution();
+      }
       if (SoFar == nullptr)
         return nullptr;
-      if (!PushComponent(getDerived().parseCtorDtorName(SoFar, State)))
-        return nullptr;
-      SoFar = getDerived().parseAbiTags(SoFar);
-      if (SoFar == nullptr)
+      continue; // Do not push a new substitution.
+    } else {
+      Node *N = nullptr;
+      if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
+        // An <unqualified-name> that's actually a <ctor-dtor-name>.
+        if (SoFar == nullptr)
+          return nullptr;
+        N = getDerived().parseCtorDtorName(SoFar, State);
+        if (N != nullptr)
+          N = getDerived().parseAbiTags(N);
+      } else {
+        //          ::= <prefix> <unqualified-name>
+        N = getDerived().parseUnqualifiedName(State);
+      }
+      if (N == nullptr)
         return nullptr;
-      Subs.push_back(SoFar);
-      continue;
+      if (SoFar)
+        SoFar = make<NestedName>(SoFar, N);
+      else
+        SoFar = N;
     }
-
-    //          ::= <prefix> <unqualified-name>
-    if (!PushComponent(getDerived().parseUnqualifiedName(State)))
+    if (SoFar == nullptr)
       return nullptr;
     Subs.push_back(SoFar);
   }
@@ -5431,6 +5425,7 @@ bool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) {
 // <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >
 // <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >
 // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
+// The St case is handled specially in parseNestedName.
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parseSubstitution() {
   if (!consumeIf('S'))

diff  --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp
index 191b4b686969..263494af75d0 100644
--- a/libcxxabi/test/test_demangle.pass.cpp
+++ b/libcxxabi/test/test_demangle.pass.cpp
@@ -29936,6 +29936,11 @@ const char* invalid_cases[] =
     "FSiIJEENT_IoE ",
     "ZTVSiIZTVSiIZTVSiIZTVSiINIJEET_T_T_T_T_ ",
     "Ana_T_E_T_IJEffffffffffffffersfffffrsrsffffffbgE",
+
+    "_ZN3TPLS_E",
+    "_ZN3CLSIiEIiEE",
+    "_ZN3CLSDtLi0EEE",
+    "_ZN3CLSIiEEvNS_T_Ev",
 };
 
 const unsigned NI = sizeof(invalid_cases) / sizeof(invalid_cases[0]);

diff  --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 4ffac53790d0..302669ab8324 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -2572,8 +2572,6 @@ const char* parse_discriminator(const char* first, const char* last);
 //                          ::= <substitution>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) {
-  consumeIf('L'); // extension
-
   if (look() == 'N')
     return getDerived().parseNestedName(State);
   if (look() == 'Z')
@@ -2649,15 +2647,14 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
   return make<LocalName>(Encoding, Entity);
 }
 
-// <unscoped-name> ::= <unqualified-name>
-//                 ::= St <unqualified-name>   # ::std::
-// extension       ::= StL<unqualified-name>
+// <unscoped-name> ::= [L]* <unqualified-name>
+//                 ::= St [L]* <unqualified-name>   # ::std::
+// [*] extension
 template <typename Derived, typename Alloc>
 Node *
 AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State) {
   bool IsStd = consumeIf("St");
-  if (IsStd)
-    consumeIf('L');
+  consumeIf('L');
 
   Node *Result = getDerived().parseUnqualifiedName(State);
   if (Result == nullptr)
@@ -3148,14 +3145,14 @@ AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar,
 // <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
 //               ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
 //
-// <prefix> ::= <prefix> <unqualified-name>
+// <prefix> ::= <prefix> [L]* <unqualified-name>
 //          ::= <template-prefix> <template-args>
 //          ::= <template-param>
 //          ::= <decltype>
 //          ::= # empty
 //          ::= <substitution>
 //          ::= <prefix> <data-member-prefix>
-//  extension ::= L
+// [*] extension
 //
 // <data-member-prefix> := <member source-name> [<template-args>] M
 //
@@ -3175,88 +3172,85 @@ AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {
     if (State) State->ReferenceQualifier = FrefQualRValue;
   } else if (consumeIf('R')) {
     if (State) State->ReferenceQualifier = FrefQualLValue;
-  } else
+  } else {
     if (State) State->ReferenceQualifier = FrefQualNone;
-
-  Node *SoFar = nullptr;
-  auto PushComponent = [&](Node *Comp) {
-    if (!Comp) return false;
-    if (SoFar) SoFar = make<NestedName>(SoFar, Comp);
-    else       SoFar = Comp;
-    if (State) State->EndsWithTemplateArgs = false;
-    return SoFar != nullptr;
-  };
-
-  if (consumeIf("St")) {
-    SoFar = make<NameType>("std");
-    if (!SoFar)
-      return nullptr;
   }
 
+  Node *SoFar = nullptr;
   while (!consumeIf('E')) {
     consumeIf('L'); // extension
 
-    // <data-member-prefix> := <member source-name> [<template-args>] M
     if (consumeIf('M')) {
+      // <data-member-prefix> := <member source-name> [<template-args>] M
       if (SoFar == nullptr)
         return nullptr;
       continue;
     }
 
-    //          ::= <template-param>
-    if (look() == 'T') {
-      if (!PushComponent(getDerived().parseTemplateParam()))
-        return nullptr;
-      Subs.push_back(SoFar);
-      continue;
-    }
+    if (State)
+      // Only set end-with-template on the case that does that.
+      State->EndsWithTemplateArgs = false;
 
-    //          ::= <template-prefix> <template-args>
-    if (look() == 'I') {
+    if (look() == 'T') {
+      //          ::= <template-param>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      SoFar = getDerived().parseTemplateParam();
+    } else if (look() == 'I') {
+      //          ::= <template-prefix> <template-args>
+      if (SoFar == nullptr)
+        return nullptr; // Must have a prefix.
       Node *TA = getDerived().parseTemplateArgs(State != nullptr);
-      if (TA == nullptr || SoFar == nullptr)
-        return nullptr;
-      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
-      if (!SoFar)
-        return nullptr;
-      if (State) State->EndsWithTemplateArgs = true;
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <decltype>
-    if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
-      if (!PushComponent(getDerived().parseDecltype()))
+      if (TA == nullptr)
         return nullptr;
-      Subs.push_back(SoFar);
-      continue;
-    }
-
-    //          ::= <substitution>
-    if (look() == 'S' && look(1) != 't') {
-      Node *S = getDerived().parseSubstitution();
-      if (!PushComponent(S))
+      if (SoFar->getKind() == Node::KNameWithTemplateArgs)
+        // Semantically <template-args> <template-args> cannot be generated by a
+        // C++ entity.  There will always be [something like] a name between
+        // them.
         return nullptr;
-      if (SoFar != S)
-        Subs.push_back(S);
-      continue;
-    }
-
-    // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
-    if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
+      if (State)
+        State->EndsWithTemplateArgs = true;
+      SoFar = make<NameWithTemplateArgs>(SoFar, TA);
+    } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
+      //          ::= <decltype>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      SoFar = getDerived().parseDecltype();
+    } else if (look() == 'S') {
+      //          ::= <substitution>
+      if (SoFar != nullptr)
+        return nullptr; // Cannot have a prefix.
+      if (look(1) == 't') {
+        // parseSubstition does not handle 'St'.
+        First += 2;
+        SoFar = make<NameType>("std");
+      } else {
+        SoFar = getDerived().parseSubstitution();
+      }
       if (SoFar == nullptr)
         return nullptr;
-      if (!PushComponent(getDerived().parseCtorDtorName(SoFar, State)))
-        return nullptr;
-      SoFar = getDerived().parseAbiTags(SoFar);
-      if (SoFar == nullptr)
+      continue; // Do not push a new substitution.
+    } else {
+      Node *N = nullptr;
+      if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
+        // An <unqualified-name> that's actually a <ctor-dtor-name>.
+        if (SoFar == nullptr)
+          return nullptr;
+        N = getDerived().parseCtorDtorName(SoFar, State);
+        if (N != nullptr)
+          N = getDerived().parseAbiTags(N);
+      } else {
+        //          ::= <prefix> <unqualified-name>
+        N = getDerived().parseUnqualifiedName(State);
+      }
+      if (N == nullptr)
         return nullptr;
-      Subs.push_back(SoFar);
-      continue;
+      if (SoFar)
+        SoFar = make<NestedName>(SoFar, N);
+      else
+        SoFar = N;
     }
-
-    //          ::= <prefix> <unqualified-name>
-    if (!PushComponent(getDerived().parseUnqualifiedName(State)))
+    if (SoFar == nullptr)
       return nullptr;
     Subs.push_back(SoFar);
   }
@@ -5431,6 +5425,7 @@ bool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) {
 // <substitution> ::= Si # ::std::basic_istream<char,  std::char_traits<char> >
 // <substitution> ::= So # ::std::basic_ostream<char,  std::char_traits<char> >
 // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
+// The St case is handled specially in parseNestedName.
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parseSubstitution() {
   if (!consumeIf('S'))


        


More information about the libcxx-commits mailing list