Repository: edgedb/edgedb
Issue: Restrict `VERBOSE` usage in `DESCRIBE`

The only usage of `VERBOSE` flag that has any effect is with the `DESCRIBE ... AS TEXT VERBOSE`. The grammar should reflect that.

Changed files:
tests/test_type_coverage.py, edb/edgeql/parser/grammar/statements.py, tests/test_edgeql_syntax.py

diff --git a/edb/edgeql/parser/grammar/statements.py b/edb/edgeql/parser/grammar/statements.py
index 3a7457649..d33d87282 100644
--- a/edb/edgeql/parser/grammar/statements.py
+++ b/edb/edgeql/parser/grammar/statements.py
@@ -18,6 +18,8 @@
 
 from __future__ import annotations
 
+import typing
+
 from edb import errors
 
 from edb.edgeql import ast as qlast
@@ -132,73 +134,68 @@ class TransactionStmt(Nonterm):
         self.val = qlast.ReleaseSavepoint(name=kids[2].val)
 
 
+class DescribeFmt(typing.NamedTuple):
+    language: typing.Optional[qltypes.DescribeLanguage] = None
+    options: qlast.Options = None
+
+
 class DescribeFormat(Nonterm):
 
     def reduce_empty(self, *kids):
-        self.val = qltypes.DescribeLanguage.DDL
+        self.val = DescribeFmt(
+            language=qltypes.DescribeLanguage.DDL,
+            options=qlast.Options(),
+        )
 
     def reduce_AS_DDL(self, *kids):
-        self.val = qltypes.DescribeLanguage.DDL
+        self.val = DescribeFmt(
+            language=qltypes.DescribeLanguage.DDL,
+            options=qlast.Options(),
+        )
 
     def reduce_AS_SDL(self, *kids):
-        self.val = qltypes.DescribeLanguage.SDL
+        self.val = DescribeFmt(
+            language=qltypes.DescribeLanguage.SDL,
+            options=qlast.Options(),
+        )
 
     def reduce_AS_TEXT(self, *kids):
-        self.val = qltypes.DescribeLanguage.TEXT
-
-
-class DescribeOption(Nonterm):
-
-    def reduce_VERBOSE(self, *kids):
-        self.val = qlast.Flag(name='VERBOSE', val=True)
-
-
-class DescribeOptions(ListNonterm, element=DescribeOption):
-    def _reduce_list(self, lst, el):
-        self.val = qlast.Options(
-            options={**lst.val.options, el.val.name: el.val})
-
-    def _reduce_el(self, el):
-        self.val = qlast.Options(options={el.val.name: el.val})
-
-
-class OptDescribeOptions(Nonterm):
-
-    def reduce_DescribeOptions(self, *kids):
-        self.val = kids[0].val
+        self.val = DescribeFmt(
+            language=qltypes.DescribeLanguage.TEXT,
+            options=qlast.Options(),
+        )
 
-    def reduce_empty(self, *kids):
-        self.val = qlast.Options()
+    def reduce_AS_TEXT_VERBOSE(self, *kids):
+        self.val = DescribeFmt(
+            language=qltypes.DescribeLanguage.TEXT,
+            options=qlast.Options(
+                options={'VERBOSE': qlast.Flag(name='VERBOSE', val=True)}
+            ),
+        )
 
 
 class DescribeStmt(Nonterm):
 
     def reduce_DESCRIBE_SCHEMA(self, *kids):
-        """%reduce DESCRIBE SCHEMA DescribeFormat
-                   OptDescribeOptions
-        """
+        """%reduce DESCRIBE SCHEMA DescribeFormat"""
         self.val = qlast.DescribeStmt(
-            language=kids[2].val,
             object=None,
-            options=kids[3].val,
+            language=kids[2].val.language,
+            options=kids[2].val.options,
         )
 
     def reduce_DESCRIBE_SchemaItem(self, *kids):
-        """%reduce DESCRIBE SchemaItem DescribeFormat
-                   OptDescribeOptions
-        """
+        """%reduce DESCRIBE SchemaItem DescribeFormat"""
         self.val = qlast.DescribeStmt(
-            language=kids[2].val,
             object=kids[1].val,
-            options=kids[3].val,
+            language=kids[2].val.language,
+            options=kids[2].val.options,
         )
 
     def reduce_DESCRIBE_OBJECT(self, *kids):
-        """%reduce DESCRIBE OBJECT NodeName DescribeFormat
-                   OptDescribeOptions
-        """
+        """%reduce DESCRIBE OBJECT NodeName DescribeFormat"""
         self.val = qlast.DescribeStmt(
-            language=kids[3].val,
             object=kids[2].val,
-            options=kids[4].val,
+            language=kids[3].val.language,
+            options=kids[3].val.options,
         )
diff --git a/tests/test_edgeql_syntax.py b/tests/test_edgeql_syntax.py
index d51581588..129a0745e 100644
--- a/tests/test_edgeql_syntax.py
+++ b/tests/test_edgeql_syntax.py
@@ -3771,6 +3771,9 @@ aa';
         DESCRIBE TYPE foo::Bar AS TEXT VERBOSE;
         """
 
+    @tb.must_fail(errors.EdgeQLSyntaxError,
+                  r"Unexpected 'VERBOSE'",
+                  line=2, col=39)
     def test_edgeql_syntax_describe_04(self):
         """
         DESCRIBE TYPE foo::Bar AS DDL VERBOSE;
diff --git a/tests/test_type_coverage.py b/tests/test_type_coverage.py
index 8df72dd40..42c28175a 100644
--- a/tests/test_type_coverage.py
+++ b/tests/test_type_coverage.py
@@ -206,7 +206,7 @@ class TypeCoverageTests(unittest.TestCase):
         self.assertFunctionCoverage(EDB_DIR / "common" / "markup", 0)
 
     def test_cqa_type_coverage_edgeql(self) -> None:
-        self.assertFunctionCoverage(EDB_DIR / "edgeql", 41.79)
+        self.assertFunctionCoverage(EDB_DIR / "edgeql", 41.94)
 
     def test_cqa_type_coverage_edgeql_compiler(self) -> None:
         self.assertFunctionCoverage(EDB_DIR / "edgeql" / "compiler", 100.00)