Fix parsing macros with only a variadic argument

Function-like macros with only a variadic argument (e.g. MACRO(...))
were not parsed correctly, so linking to them would fail.

Change-Id: I1126f76473d82b717629f70d0d82245ce46dae86
Reviewed-on: https://pigweed-review.googlesource.com/c/third_party/github/sphinx-contrib/doxylink/+/386618
diff --git a/sphinxcontrib/doxylink/parsing.py b/sphinxcontrib/doxylink/parsing.py
index ee6e848..4cafcc0 100644
--- a/sphinxcontrib/doxylink/parsing.py
+++ b/sphinxcontrib/doxylink/parsing.py
@@ -2,7 +2,7 @@
 
 from pyparsing import Word, Literal, nums, alphanums, OneOrMore, Opt, \
     SkipTo, ParseException, Group, Combine, delimitedList, quotedString, \
-    nestedExpr, ParseResults, oneOf, ungroup, Keyword, ZeroOrMore
+    nestedExpr, ParseResults, oneOf, ungroup, Keyword, ZeroOrMore, Empty, replaceWith
 
 # define punctuation - reuse of expressions helps packratting work better
 LPAR, RPAR, LBRACK, RBRACK, LCBRACK, RCBRACK, COMMA, EQ = map(Literal, "()[]{},=")
@@ -56,8 +56,10 @@
 default_value = Literal('=') + OneOrMore(number | quotedString | input_type | parentheses_pair | angle_bracket_pair | square_bracket_pair | curly_bracket_pair | Word('|&^'))
 
 # A combination building up the interesting bit -- the argument type, e.g. 'const QString &', 'int' or 'char*'
-argument_type = Opt(qualifier, default='')("qualifier1") + \
-                input_type("input_type").setParseAction(' '.join) + \
+argument_type_prefix = (qualifier("qualifier1") + input_type("input_type").setParseAction(' '.join)) | \
+                       (Empty().setParseAction(replaceWith(''))("qualifier1") + input_type("input_type").setParseAction(' '.join))
+
+argument_type = argument_type_prefix + \
                 Opt(qualifier, default='')("qualifier2") + \
                 Group(ZeroOrMore(pointer_or_reference))("pointer_or_references") + \
                 Opt('...')("parameter_pack")
@@ -66,7 +68,7 @@
 argument = Group(argument_type('argument_type') + Opt(input_name) + Opt(default_value))
 
 # List of arguments in parentheses with an optional 'const' on the end
-arglist = LPAR + delimitedList(argument)('arg_list') + Opt(COMMA + '...')('var_args') + RPAR
+arglist = LPAR + Opt(delimitedList(argument)('arg_list')) + Opt(COMMA) + Opt('...')('var_args') + RPAR
 
 
 def normalise(symbol: str) -> Tuple[str, str]:
diff --git a/tests/test_e2e.py b/tests/test_e2e.py
index 27fdde9..fe511af 100644
--- a/tests/test_e2e.py
+++ b/tests/test_e2e.py
@@ -65,10 +65,20 @@
 /// Simple macro.
 #define SIMPLE_MACRO 42
 
-/// A function-like macro.
-#define FUNCTION_LIKE_MACRO(int x, int y);
+/// Function-like macro.
+#define FUNCTION_LIKE_MACRO_1(x)
 
+/// Function-like macro.
+#define FUNCTION_LIKE_MACRO_2(x, y);
 
+/// Variadic macro.
+#define VARIADIC_FUNCTION_LIKE_MACRO_1(...)
+
+/// Variadic macro.
+#define VARIADIC_FUNCTION_LIKE_MACRO_2(x, ...)
+
+/// Macro with a keyword as its parameter.
+#define MACRO_WITH_KEYWORD_PARAM(enum)
 """
 
 _CXX_NAMES = (
@@ -87,7 +97,11 @@
     ("secondary", "TestFunction"),
     ("secondary", "ambiguous_var"),
     (None, "SIMPLE_MACRO"),
-    (None, "FUNCTION_LIKE_MACRO"),
+    (None, "FUNCTION_LIKE_MACRO_1"),
+    (None, "FUNCTION_LIKE_MACRO_2"),
+    (None, "VARIADIC_FUNCTION_LIKE_MACRO_1"),
+    (None, "VARIADIC_FUNCTION_LIKE_MACRO_2"),
+    (None, "MACRO_WITH_KEYWORD_PARAM"),
 )
 
 # Assert that all names above are present in _CXX_SOURCE
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 5838301..985b4ac 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -7,6 +7,8 @@
 # List of tuples of: (input, correct output)
 # Input is a string, output is a tuple.
 arglists = [
+    ('(enum)', ('', '(enum)')),
+    ('(const)', ('', '(const)')),
     ('( QUrl source )', ('', '(QUrl)')),
     ('( QUrl * source )', ('', '(QUrl*)')),
     ('( QUrl ** source )', ('', '(QUrl**)')),
@@ -62,6 +64,8 @@
     ('fprintf( std::FILE* stream, const char* format, ... )', ('fprintf', '(std::FILE*, const char*, ...)')),
     ('sprintf( char* buffer, const char* format, ... )', ('sprintf', '(char*, const char*, ...)')),
     ('snprintf( char* buffer, std::size_t buf_size, const char* format, ... )', ('snprintf', '(char*, std::size_t, const char*, ...)')),
+    ('MACRO(...)', ('MACRO', '(...)')),
+    ('(...)', ('', '(...)')),
 ]
 
 multiple_qualifiers = [