diff --git a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf --- a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf +++ b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf @@ -340,17 +340,28 @@ static_assert-declaration := STATIC_ASSERT ( constant-expression , string-literal ) ; empty-declaration := ; # dcl.spec -decl-specifier := storage-class-specifier -decl-specifier := defining-type-specifier -decl-specifier := function-specifier -decl-specifier := FRIEND -decl-specifier := TYPEDEF -decl-specifier := CONSTEXPR -decl-specifier := CONSTEVAL -decl-specifier := CONSTINIT -decl-specifier := INLINE -decl-specifier-seq := decl-specifier -decl-specifier-seq := decl-specifier decl-specifier-seq +#! Standard grammar doesn't require a {decl,type,defining-type}-specifier-seq +#! to have only one e.g. class name. +#! This leads to ambiguity: does `foo bar;` declare: +#! - a variable named `bar` of type `foo`? +#! - nothing, with both `foo` and `bar` being types?! +#! Here we eliminate that ambiguity in the grammar: +#! exclusive-* has a type name (e.g exclusive-type-specifier ~ IDENTIFIER) +#! combining-* has no type name (e.g. combining-type-specifier ~ UNSIGNED) +#! combining-*-seq has no type name (e.g. UNSIGNED CONST) +#! *-seq has at most one type name (e.g. type-specifier-seq ~ CONST IDENTIFIER) +combining-decl-specifier := storage-class-specifier +combining-decl-specifier := combining-defining-type-specifier +combining-decl-specifier := function-specifier +combining-decl-specifier := FRIEND +combining-decl-specifier := TYPEDEF +combining-decl-specifier := CONSTEXPR +combining-decl-specifier := CONSTEVAL +combining-decl-specifier := CONSTINIT +combining-decl-specifier := INLINE +combining-decl-specifier-seq := combining-decl-specifier combining-decl-specifier-seq_opt +decl-specifier-seq := combining-decl-specifier-seq +decl-specifier-seq := combining-decl-specifier-seq_opt exclusive-defining-type-specifier combining-decl-specifier-seq_opt storage-class-specifier := STATIC storage-class-specifier := THREAD_LOCAL storage-class-specifier := EXTERN @@ -359,36 +370,42 @@ function-specifier := explicit-specifier explicit-specifier := EXPLICIT ( constant-expression ) explicit-specifier := EXPLICIT -type-specifier := simple-type-specifier -type-specifier := elaborated-type-specifier -type-specifier := typename-specifier -type-specifier := cv-qualifier -type-specifier-seq := type-specifier -type-specifier-seq := type-specifier type-specifier-seq -defining-type-specifier := type-specifier -defining-type-specifier := class-specifier -defining-type-specifier := enum-specifier -defining-type-specifier-seq := defining-type-specifier -defining-type-specifier-seq := defining-type-specifier defining-type-specifier-seq -simple-type-specifier := nested-name-specifier_opt type-name -simple-type-specifier := nested-name-specifier TEMPLATE simple-template-id -simple-type-specifier := decltype-specifier -simple-type-specifier := placeholder-type-specifier -simple-type-specifier := nested-name-specifier_opt template-name -simple-type-specifier := CHAR -simple-type-specifier := CHAR8_T -simple-type-specifier := CHAR16_T -simple-type-specifier := CHAR32_T -simple-type-specifier := WCHAR_T -simple-type-specifier := BOOL -simple-type-specifier := SHORT -simple-type-specifier := INT -simple-type-specifier := LONG -simple-type-specifier := SIGNED -simple-type-specifier := UNSIGNED -simple-type-specifier := FLOAT -simple-type-specifier := DOUBLE -simple-type-specifier := VOID +combining-type-specifier := combining-simple-type-specifier +exclusive-type-specifier := exclusive-simple-type-specifier +exclusive-type-specifier := elaborated-type-specifier +exclusive-type-specifier := typename-specifier +combining-type-specifier := cv-qualifier +combining-type-specifier-seq := combining-type-specifier combining-type-specifier-seq_opt +type-specifier-seq := combining-type-specifier-seq +type-specifier-seq := combining-type-specifier_opt exclusive-type-specifier combining-type-specifier_opt +combining-defining-type-specifier := combining-type-specifier +exclusive-defining-type-specifier := exclusive-type-specifier +exclusive-defining-type-specifier := class-specifier +exclusive-defining-type-specifier := enum-specifier +combining-defining-type-specifier-seq := combining-defining-type-specifier combining-defining-type-specifier-seq_opt +defining-type-specifier-seq := combining-defining-type-specifier-seq +defining-type-specifier-seq := combining-defining-type-specifier-seq_opt exclusive-type-specifier combining-defining-type-specifier-seq_opt +simple-type-specifier := exclusive-simple-type-specifier +simple-type-specifier := combining-simple-type-specifier +exclusive-simple-type-specifier := nested-name-specifier_opt type-name +exclusive-simple-type-specifier := nested-name-specifier TEMPLATE simple-template-id +exclusive-simple-type-specifier := decltype-specifier +exclusive-simple-type-specifier := placeholder-type-specifier +exclusive-simple-type-specifier := nested-name-specifier_opt template-name +exclusive-simple-type-specifier := CHAR +exclusive-simple-type-specifier := CHAR8_T +exclusive-simple-type-specifier := CHAR16_T +exclusive-simple-type-specifier := CHAR32_T +exclusive-simple-type-specifier := WCHAR_T +exclusive-simple-type-specifier := BOOL +combining-simple-type-specifier := SHORT +exclusive-simple-type-specifier := INT +combining-simple-type-specifier := LONG +combining-simple-type-specifier := SIGNED +combining-simple-type-specifier := UNSIGNED +exclusive-simple-type-specifier := FLOAT +exclusive-simple-type-specifier := DOUBLE +exclusive-simple-type-specifier := VOID type-name := class-name type-name := enum-name type-name := typedef-name diff --git a/clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp b/clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/pseudo/test/cxx/decl-specifier-seq.cpp @@ -0,0 +1,34 @@ +// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s +// No ambiguity: 'bar' is definitely a variable name. +const foo volatile bar; +// CHECK: simple-declaration +// CHECK-NEXT: ├─decl-specifier-seq +// CHECK-NEXT: │ ├─combining-decl-specifier-seq~CONST +// CHECK-NEXT: │ ├─exclusive-defining-type-specifier~exclusive-simple-type-specifier := +// CHECK-NEXT: │ │ ├─exclusive-simple-type-specifier~type-name := +// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER +// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER +// CHECK-NEXT: │ │ │ └─type-name~IDENTIFIER +// CHECK-NEXT: │ │ └─exclusive-simple-type-specifier~IDENTIFIER +// CHECK-NEXT: │ └─combining-decl-specifier-seq~VOLATILE +// CHECK-NEXT: ├─init-declarator-list~IDENTIFIER +// CHECK-NEXT: └─; := tok[4] +// Ambiguity: though unlikely, we may be declaring nothing. +unsigned baz; +// CHECK-NEXT: declaration~simple-declaration := +// CHECK-NEXT: ├─simple-declaration := decl-specifier-seq ; +// CHECK-NEXT: ├─decl-specifier-seq +// CHECK-NEXT: │ │ ├─combining-decl-specifier-seq := combining-decl-specifier #1 +// CHECK-NEXT: │ │ │ └─combining-decl-specifier~UNSIGNED +// CHECK-NEXT: │ │ └─exclusive-defining-type-specifier~exclusive-simple-type-specifier +// CHECK-NEXT: │ │ ├─exclusive-simple-type-specifier~type-name +// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER +// CHECK-NEXT: │ │ │ ├─type-name~IDENTIFIER +// CHECK-NEXT: │ │ │ └─type-name~IDENTIFIER +// CHECK-NEXT: │ │ └─exclusive-simple-type-specifier~IDENTIFIER +// CHECK-NEXT: │ └─; +// CHECK-NEXT: └─simple-declaration := decl-specifier-seq init-declarator-list ; +// CHECK-NEXT: ├─decl-specifier-seq~combining-decl-specifier-seq =#1 +// CHECK-NEXT: ├─init-declarator-list~IDENTIFIER +// CHECK-NEXT: └─; + diff --git a/clang-tools-extra/pseudo/test/glr.cpp b/clang-tools-extra/pseudo/test/glr.cpp --- a/clang-tools-extra/pseudo/test/glr.cpp +++ b/clang-tools-extra/pseudo/test/glr.cpp @@ -11,12 +11,12 @@ // CHECK-NEXT: │ │ └─unqualified-id~IDENTIFIER := tok[7] // CHECK-NEXT: │ └─; := tok[8] // CHECK-NEXT: └─statement~simple-declaration := decl-specifier-seq init-declarator-list ; -// CHECK-NEXT: ├─decl-specifier-seq~simple-type-specifier := -// CHECK-NEXT: │ ├─simple-type-specifier~type-name := +// CHECK-NEXT: ├─decl-specifier-seq~exclusive-simple-type-specifier := +// CHECK-NEXT: │ ├─exclusive-simple-type-specifier~type-name := // CHECK-NEXT: │ │ ├─type-name~IDENTIFIER := tok[5] // CHECK-NEXT: │ │ ├─type-name~IDENTIFIER := tok[5] // CHECK-NEXT: │ │ └─type-name~IDENTIFIER := tok[5] -// CHECK-NEXT: │ └─simple-type-specifier~IDENTIFIER := tok[5] +// CHECK-NEXT: │ └─exclusive-simple-type-specifier~IDENTIFIER := tok[5] // CHECK-NEXT: ├─init-declarator-list~ptr-declarator := ptr-operator ptr-declarator // CHECK-NEXT: │ ├─ptr-operator~* := tok[6] // CHECK-NEXT: │ └─ptr-declarator~id-expression =#1 @@ -24,7 +24,7 @@ } // CHECK: 3 Ambiguous nodes: -// CHECK-NEXT: 1 simple-type-specifier +// CHECK-NEXT: 1 exclusive-simple-type-specifier // CHECK-NEXT: 1 statement // CHECK-NEXT: 1 type-name // CHECK-EMPTY: