1 /**
2  * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
5  *
6  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d)
10  * Documentation:  https://dlang.org/phobos/dmd_parse.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d
12  */
13 
14 module dmd.parse;
15 
16 import core.stdc.stdio;
17 import core.stdc.string;
18 import dmd.astenums;
19 import dmd.globals;
20 import dmd.id;
21 import dmd.identifier;
22 import dmd.lexer;
23 import dmd.errors;
24 import dmd.root.filename;
25 import dmd.root.outbuffer;
26 import dmd.root.rmem;
27 import dmd.root.rootobject;
28 import dmd.root.string;
29 import dmd.tokens;
30 
31 // How multiple declarations are parsed.
32 // If 1, treat as C.
33 // If 0, treat:
34 //      int *p, i;
35 // as:
36 //      int* p;
37 //      int* i;
38 private enum CDECLSYNTAX = 0;
39 
40 // Support C cast syntax:
41 //      (type)(expression)
42 private enum CCASTSYNTAX = 1;
43 
44 // Support postfix C array declarations, such as
45 //      int a[3][4];
46 private enum CARRAYDECL = 1;
47 
48 /**********************************
49  * Set operator precedence for each operator.
50  *
51  * Used by hdrgen
52  */
53 immutable PREC[TOK.max + 1] precedence =
54 [
55     TOK.type : PREC.expr,
56     TOK.error : PREC.expr,
57     TOK.objcClassReference : PREC.expr, // Objective-C class reference, same as TOK.type
58 
59     TOK.typeof_ : PREC.primary,
60     TOK.mixin_ : PREC.primary,
61 
62     TOK.import_ : PREC.primary,
63     TOK.dotVariable : PREC.primary,
64     TOK.scope_ : PREC.primary,
65     TOK.identifier : PREC.primary,
66     TOK.this_ : PREC.primary,
67     TOK.super_ : PREC.primary,
68     TOK.int64 : PREC.primary,
69     TOK.float64 : PREC.primary,
70     TOK.complex80 : PREC.primary,
71     TOK.null_ : PREC.primary,
72     TOK.string_ : PREC.primary,
73     TOK.arrayLiteral : PREC.primary,
74     TOK.assocArrayLiteral : PREC.primary,
75     TOK.classReference : PREC.primary,
76     TOK.file : PREC.primary,
77     TOK.fileFullPath : PREC.primary,
78     TOK.line : PREC.primary,
79     TOK.moduleString : PREC.primary,
80     TOK.functionString : PREC.primary,
81     TOK.prettyFunction : PREC.primary,
82     TOK.typeid_ : PREC.primary,
83     TOK.is_ : PREC.primary,
84     TOK.assert_ : PREC.primary,
85     TOK.halt : PREC.primary,
86     TOK.template_ : PREC.primary,
87     TOK.dSymbol : PREC.primary,
88     TOK.function_ : PREC.primary,
89     TOK.variable : PREC.primary,
90     TOK.symbolOffset : PREC.primary,
91     TOK.structLiteral : PREC.primary,
92     TOK.compoundLiteral : PREC.primary,
93     TOK.arrayLength : PREC.primary,
94     TOK.delegatePointer : PREC.primary,
95     TOK.delegateFunctionPointer : PREC.primary,
96     TOK.remove : PREC.primary,
97     TOK.tuple : PREC.primary,
98     TOK.traits : PREC.primary,
99     TOK.default_ : PREC.primary,
100     TOK.overloadSet : PREC.primary,
101     TOK.void_ : PREC.primary,
102     TOK.vectorArray : PREC.primary,
103     TOK._Generic : PREC.primary,
104 
105     // post
106     TOK.dotTemplateInstance : PREC.primary,
107     TOK.dotIdentifier : PREC.primary,
108     TOK.dotTemplateDeclaration : PREC.primary,
109     TOK.dot : PREC.primary,
110     TOK.dotType : PREC.primary,
111     TOK.plusPlus : PREC.primary,
112     TOK.minusMinus : PREC.primary,
113     TOK.prePlusPlus : PREC.primary,
114     TOK.preMinusMinus : PREC.primary,
115     TOK.call : PREC.primary,
116     TOK.slice : PREC.primary,
117     TOK.array : PREC.primary,
118     TOK.index : PREC.primary,
119 
120     TOK.delegate_ : PREC.unary,
121     TOK.address : PREC.unary,
122     TOK.star : PREC.unary,
123     TOK.negate : PREC.unary,
124     TOK.uadd : PREC.unary,
125     TOK.not : PREC.unary,
126     TOK.tilde : PREC.unary,
127     TOK.delete_ : PREC.unary,
128     TOK.new_ : PREC.unary,
129     TOK.newAnonymousClass : PREC.unary,
130     TOK.cast_ : PREC.unary,
131 
132     TOK.vector : PREC.unary,
133     TOK.pow : PREC.pow,
134 
135     TOK.mul : PREC.mul,
136     TOK.div : PREC.mul,
137     TOK.mod : PREC.mul,
138 
139     TOK.add : PREC.add,
140     TOK.min : PREC.add,
141     TOK.concatenate : PREC.add,
142 
143     TOK.leftShift : PREC.shift,
144     TOK.rightShift : PREC.shift,
145     TOK.unsignedRightShift : PREC.shift,
146 
147     TOK.lessThan : PREC.rel,
148     TOK.lessOrEqual : PREC.rel,
149     TOK.greaterThan : PREC.rel,
150     TOK.greaterOrEqual : PREC.rel,
151     TOK.in_ : PREC.rel,
152 
153     /* Note that we changed precedence, so that < and != have the same
154      * precedence. This change is in the parser, too.
155      */
156     TOK.equal : PREC.rel,
157     TOK.notEqual : PREC.rel,
158     TOK.identity : PREC.rel,
159     TOK.notIdentity : PREC.rel,
160 
161     TOK.and : PREC.and,
162     TOK.xor : PREC.xor,
163     TOK.or : PREC.or,
164 
165     TOK.andAnd : PREC.andand,
166     TOK.orOr : PREC.oror,
167 
168     TOK.question : PREC.cond,
169 
170     TOK.assign : PREC.assign,
171     TOK.construct : PREC.assign,
172     TOK.blit : PREC.assign,
173     TOK.addAssign : PREC.assign,
174     TOK.minAssign : PREC.assign,
175     TOK.concatenateAssign : PREC.assign,
176     TOK.concatenateElemAssign : PREC.assign,
177     TOK.concatenateDcharAssign : PREC.assign,
178     TOK.mulAssign : PREC.assign,
179     TOK.divAssign : PREC.assign,
180     TOK.modAssign : PREC.assign,
181     TOK.powAssign : PREC.assign,
182     TOK.leftShiftAssign : PREC.assign,
183     TOK.rightShiftAssign : PREC.assign,
184     TOK.unsignedRightShiftAssign : PREC.assign,
185     TOK.andAssign : PREC.assign,
186     TOK.orAssign : PREC.assign,
187     TOK.xorAssign : PREC.assign,
188 
189     TOK.comma : PREC.expr,
190     TOK.declaration : PREC.expr,
191 
192     TOK.interval : PREC.assign,
193 ];
194 
195 enum ParseStatementFlags : int
196 {
197     semi          = 1,        // empty ';' statements are allowed, but deprecated
198     scope_        = 2,        // start a new scope
199     curly         = 4,        // { } statement is required
200     curlyScope    = 8,        // { } starts a new scope
201     semiOk        = 0x10,     // empty ';' are really ok
202 }
203 
PrefixAttributes(AST)204 struct PrefixAttributes(AST)
205 {
206     StorageClass storageClass;
207     AST.Expression depmsg;
208     LINK link;
209     AST.Visibility visibility;
210     bool setAlignment;
211     AST.Expression ealign;
212     AST.Expressions* udas;
213     const(char)* comment;
214 }
215 
216 /// The result of the `ParseLinkage` function
ParsedLinkage(AST)217 struct ParsedLinkage(AST)
218 {
219     /// What linkage was specified
220     LINK link;
221     /// If `extern(C++, class|struct)`, contains the `class|struct`
222     CPPMANGLE cppmangle;
223     /// If `extern(C++, some.identifier)`, will be the identifiers
224     AST.Identifiers* idents;
225     /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
226     AST.Expressions* identExps;
227 }
228 
229 /*****************************
230  * Destructively extract storage class from pAttrs.
231  */
getStorageClass(AST)232 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
233 {
234     StorageClass stc = STC.undefined_;
235     if (pAttrs)
236     {
237         stc = pAttrs.storageClass;
238         pAttrs.storageClass = STC.undefined_;
239     }
240     return stc;
241 }
242 
243 /**************************************
244  * dump mixin expansion to file for better debugging
245  */
writeMixin(const (char)[]s,ref Loc loc)246 private bool writeMixin(const(char)[] s, ref Loc loc)
247 {
248     if (!global.params.mixinOut)
249         return false;
250 
251     OutBuffer* ob = global.params.mixinOut;
252 
253     ob.writestring("// expansion at ");
254     ob.writestring(loc.toChars());
255     ob.writenl();
256 
257     global.params.mixinLines++;
258 
259     loc = Loc(global.params.mixinFile, global.params.mixinLines + 1, loc.charnum);
260 
261     // write by line to create consistent line endings
262     size_t lastpos = 0;
263     for (size_t i = 0; i < s.length; ++i)
264     {
265         // detect LF and CRLF
266         const c = s[i];
267         if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
268         {
269             ob.writestring(s[lastpos .. i]);
270             ob.writenl();
271             global.params.mixinLines++;
272             if (c == '\r')
273                 ++i;
274             lastpos = i + 1;
275         }
276     }
277 
278     if(lastpos < s.length)
279         ob.writestring(s[lastpos .. $]);
280 
281     if (s.length == 0 || s[$-1] != '\n')
282     {
283         ob.writenl(); // ensure empty line after expansion
284         global.params.mixinLines++;
285     }
286     ob.writenl();
287     global.params.mixinLines++;
288 
289     return true;
290 }
291 
292 /***********************************************************
293  */
Parser(AST)294 class Parser(AST) : Lexer
295 {
296     AST.ModuleDeclaration* md;
297 
298     protected
299     {
300         AST.Module mod;
301         LINK linkage;
302         Loc linkLoc;
303         CPPMANGLE cppmangle;
304         Loc endloc; // set to location of last right curly
305         int inBrackets; // inside [] of array index or slice
306         Loc lookingForElse; // location of lonely if looking for an else
307     }
308 
309     /*********************
310      * Use this constructor for string mixins.
311      * Input:
312      *      loc     location in source file of mixin
313      */
314     extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment)
315     {
316         super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
317 
318         //printf("Parser::Parser()\n");
319         scanloc = loc;
320 
321         if (!writeMixin(input, scanloc) && loc.filename)
322         {
323             /* Create a pseudo-filename for the mixin string, as it may not even exist
324              * in the source file.
325              */
326             char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1);
327             sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
328             scanloc.filename = filename;
329         }
330 
331         mod = _module;
332         linkage = LINK.d;
333         //nextToken();              // start up the scanner
334     }
335 
336     extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment)
337     {
338         super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
339 
340         //printf("Parser::Parser()\n");
341         mod = _module;
342         linkage = LINK.d;
343         //nextToken();              // start up the scanner
344     }
345 
346     AST.Dsymbols* parseModule()
347     {
348         const comment = token.blockComment;
349         bool isdeprecated = false;
350         AST.Expression msg = null;
351         AST.Expressions* udas = null;
352         AST.Dsymbols* decldefs;
353         AST.Dsymbol lastDecl = mod; // for attaching ddoc unittests to module decl
354 
355         Token* tk;
356         if (skipAttributes(&token, &tk) && tk.value == TOK.module_)
357         {
358             while (token.value != TOK.module_)
359             {
360                 switch (token.value)
361                 {
362                 case TOK.deprecated_:
363                     {
364                         // deprecated (...) module ...
365                         if (isdeprecated)
366                             error("there is only one deprecation attribute allowed for module declaration");
367                         isdeprecated = true;
368                         nextToken();
369                         if (token.value == TOK.leftParenthesis)
370                         {
371                             check(TOK.leftParenthesis);
372                             msg = parseAssignExp();
373                             check(TOK.rightParenthesis);
374                         }
375                         break;
376                     }
377                 case TOK.at:
378                     {
379                         AST.Expressions* exps = null;
380                         const stc = parseAttribute(exps);
381                         if (stc & atAttrGroup)
382                         {
383                             error("`@%s` attribute for module declaration is not supported", token.toChars());
384                         }
385                         else
386                         {
387                             udas = AST.UserAttributeDeclaration.concat(udas, exps);
388                         }
389                         if (stc)
390                             nextToken();
391                         break;
392                     }
393                 default:
394                     {
395                         error("`module` expected instead of `%s`", token.toChars());
396                         nextToken();
397                         break;
398                     }
399                 }
400             }
401         }
402 
403         if (udas)
404         {
405             auto a = new AST.Dsymbols();
406             auto udad = new AST.UserAttributeDeclaration(udas, a);
407             mod.userAttribDecl = udad;
408         }
409 
410         // ModuleDeclation leads off
411         if (token.value == TOK.module_)
412         {
413             const loc = token.loc;
414 
415             nextToken();
416             if (token.value != TOK.identifier)
417             {
418                 error("identifier expected following `module`");
419                 goto Lerr;
420             }
421 
422             Identifier[] a;
423             Identifier id = token.ident;
424 
425             while (nextToken() == TOK.dot)
426             {
427                 a ~= id;
428                 nextToken();
429                 if (token.value != TOK.identifier)
430                 {
431                     error("identifier expected following `package`");
432                     goto Lerr;
433                 }
434                 id = token.ident;
435             }
436 
437             md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
438 
439             if (token.value != TOK.semicolon)
440                 error("`;` expected following module declaration instead of `%s`", token.toChars());
441             nextToken();
442             addComment(mod, comment);
443         }
444 
445         decldefs = parseDeclDefs(0, &lastDecl);
446         if (token.value != TOK.endOfFile)
447         {
448             error(token.loc, "unrecognized declaration");
449             goto Lerr;
450         }
451         return decldefs;
452 
453     Lerr:
454         while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
455             nextToken();
456         nextToken();
457         return new AST.Dsymbols();
458     }
459 
460   final:
461 
462     /**
463      * Parses a `deprecated` declaration
464      *
465      * Params:
466      *   msg = Deprecated message, if any.
467      *         Used to support overriding a deprecated storage class with
468      *         a deprecated declaration with a message, but to error
469      *         if both declaration have a message.
470      *
471      * Returns:
472      *   Whether the deprecated declaration has a message
473      */
474     private bool parseDeprecatedAttribute(ref AST.Expression msg)
475     {
476         if (peekNext() != TOK.leftParenthesis)
477             return false;
478 
479         nextToken();
480         check(TOK.leftParenthesis);
481         AST.Expression e = parseAssignExp();
482         check(TOK.rightParenthesis);
483         if (msg)
484         {
485             error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
486         }
487         msg = e;
488         return true;
489     }
490 
491     AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
492     {
493         AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
494         if (!pLastDecl)
495             pLastDecl = &lastDecl;
496 
497         const linksave = linkage; // save global state
498 
499         //printf("Parser::parseDeclDefs()\n");
500         auto decldefs = new AST.Dsymbols();
501         do
502         {
503             // parse result
504             AST.Dsymbol s = null;
505             AST.Dsymbols* a = null;
506 
507             PrefixAttributes!AST attrs;
508             if (!once || !pAttrs)
509             {
510                 pAttrs = &attrs;
511                 pAttrs.comment = token.blockComment.ptr;
512             }
513             AST.Visibility.Kind prot;
514             StorageClass stc;
515             AST.Condition condition;
516 
517             linkage = linksave;
518 
519             Loc startloc;
520 
521             switch (token.value)
522             {
523             case TOK.enum_:
524                 {
525                     /* Determine if this is a manifest constant declaration,
526                      * or a conventional enum.
527                      */
528                     const tv = peekNext();
529                     if (tv == TOK.leftCurly || tv == TOK.colon)
530                         s = parseEnum();
531                     else if (tv != TOK.identifier)
532                         goto Ldeclaration;
533                     else
534                     {
535                         const nextv = peekNext2();
536                         if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
537                             s = parseEnum();
538                         else
539                             goto Ldeclaration;
540                     }
541                     break;
542                 }
543             case TOK.import_:
544                 a = parseImport();
545                 // keep pLastDecl
546                 break;
547 
548             case TOK.template_:
549                 s = cast(AST.Dsymbol)parseTemplateDeclaration();
550                 break;
551 
552             case TOK.mixin_:
553                 {
554                     const loc = token.loc;
555                     switch (peekNext())
556                     {
557                     case TOK.leftParenthesis:
558                         {
559                             // mixin(string)
560                             nextToken();
561                             auto exps = parseArguments();
562                             check(TOK.semicolon);
563                             s = new AST.CompileDeclaration(loc, exps);
564                             break;
565                         }
566                     case TOK.template_:
567                         // mixin template
568                         nextToken();
569                         s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
570                         break;
571 
572                     default:
573                         s = parseMixin();
574                         break;
575                     }
576                     break;
577                 }
578             case TOK.wchar_:
579             case TOK.dchar_:
580             case TOK.bool_:
581             case TOK.char_:
582             case TOK.int8:
583             case TOK.uns8:
584             case TOK.int16:
585             case TOK.uns16:
586             case TOK.int32:
587             case TOK.uns32:
588             case TOK.int64:
589             case TOK.uns64:
590             case TOK.int128:
591             case TOK.uns128:
592             case TOK.float32:
593             case TOK.float64:
594             case TOK.float80:
595             case TOK.imaginary32:
596             case TOK.imaginary64:
597             case TOK.imaginary80:
598             case TOK.complex32:
599             case TOK.complex64:
600             case TOK.complex80:
601             case TOK.void_:
602             case TOK.alias_:
603             case TOK.identifier:
604             case TOK.super_:
605             case TOK.typeof_:
606             case TOK.dot:
607             case TOK.vector:
608             case TOK.struct_:
609             case TOK.union_:
610             case TOK.class_:
611             case TOK.interface_:
612             case TOK.traits:
613             Ldeclaration:
614                 a = parseDeclarations(false, pAttrs, pAttrs.comment);
615                 if (a && a.dim)
616                     *pLastDecl = (*a)[a.dim - 1];
617                 break;
618 
619             case TOK.this_:
620                 if (peekNext() == TOK.dot)
621                     goto Ldeclaration;
622                 s = parseCtor(pAttrs);
623                 break;
624 
625             case TOK.tilde:
626                 s = parseDtor(pAttrs);
627                 break;
628 
629             case TOK.invariant_:
630                 const tv = peekNext();
631                 if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
632                 {
633                     // invariant { statements... }
634                     // invariant() { statements... }
635                     // invariant (expression);
636                     s = parseInvariant(pAttrs);
637                     break;
638                 }
639                 error("invariant body expected, not `%s`", token.toChars());
640                 goto Lerror;
641 
642             case TOK.unittest_:
643                 if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
644                 {
645                     s = parseUnitTest(pAttrs);
646                     if (*pLastDecl)
647                         (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
648                 }
649                 else
650                 {
651                     // Skip over unittest block by counting { }
652                     Loc loc = token.loc;
653                     int braces = 0;
654                     while (1)
655                     {
656                         nextToken();
657                         switch (token.value)
658                         {
659                         case TOK.leftCurly:
660                             ++braces;
661                             continue;
662 
663                         case TOK.rightCurly:
664                             if (--braces)
665                                 continue;
666                             nextToken();
667                             break;
668 
669                         case TOK.endOfFile:
670                             /* { */
671                             error(loc, "closing `}` of unittest not found before end of file");
672                             goto Lerror;
673 
674                         default:
675                             continue;
676                         }
677                         break;
678                     }
679                     // Workaround 14894. Add an empty unittest declaration to keep
680                     // the number of symbols in this scope independent of -unittest.
681                     s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
682                 }
683                 break;
684 
685             case TOK.new_:
686                 s = parseNew(pAttrs);
687                 break;
688 
689             case TOK.colon:
690             case TOK.leftCurly:
691                 error("declaration expected, not `%s`", token.toChars());
692                 goto Lerror;
693 
694             case TOK.rightCurly:
695             case TOK.endOfFile:
696                 if (once)
697                     error("declaration expected, not `%s`", token.toChars());
698                 return decldefs;
699 
700             case TOK.static_:
701                 {
702                     const next = peekNext();
703                     if (next == TOK.this_)
704                         s = parseStaticCtor(pAttrs);
705                     else if (next == TOK.tilde)
706                         s = parseStaticDtor(pAttrs);
707                     else if (next == TOK.assert_)
708                         s = parseStaticAssert();
709                     else if (next == TOK.if_)
710                     {
711                         const Loc loc = token.loc;
712                         condition = parseStaticIfCondition();
713                         AST.Dsymbols* athen;
714                         if (token.value == TOK.colon)
715                             athen = parseBlock(pLastDecl);
716                         else
717                         {
718                             const lookingForElseSave = lookingForElse;
719                             lookingForElse = token.loc;
720                             athen = parseBlock(pLastDecl);
721                             lookingForElse = lookingForElseSave;
722                         }
723                         AST.Dsymbols* aelse = null;
724                         if (token.value == TOK.else_)
725                         {
726                             const elseloc = token.loc;
727                             nextToken();
728                             aelse = parseBlock(pLastDecl);
729                             checkDanglingElse(elseloc);
730                         }
731                         s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
732                     }
733                     else if (next == TOK.import_)
734                     {
735                         a = parseImport();
736                         // keep pLastDecl
737                     }
738                     else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
739                     {
740                         s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
741                     }
742                     else
743                     {
744                         stc = STC.static_;
745                         goto Lstc;
746                     }
747                     break;
748                 }
749             case TOK.const_:
750                 if (peekNext() == TOK.leftParenthesis)
751                     goto Ldeclaration;
752                 stc = STC.const_;
753                 goto Lstc;
754 
755             case TOK.immutable_:
756                 if (peekNext() == TOK.leftParenthesis)
757                     goto Ldeclaration;
758                 stc = STC.immutable_;
759                 goto Lstc;
760 
761             case TOK.shared_:
762                 {
763                     const next = peekNext();
764                     if (next == TOK.leftParenthesis)
765                         goto Ldeclaration;
766                     if (next == TOK.static_)
767                     {
768                         TOK next2 = peekNext2();
769                         if (next2 == TOK.this_)
770                         {
771                             s = parseSharedStaticCtor(pAttrs);
772                             break;
773                         }
774                         if (next2 == TOK.tilde)
775                         {
776                             s = parseSharedStaticDtor(pAttrs);
777                             break;
778                         }
779                     }
780                     stc = STC.shared_;
781                     goto Lstc;
782                 }
783             case TOK.inout_:
784                 if (peekNext() == TOK.leftParenthesis)
785                     goto Ldeclaration;
786                 stc = STC.wild;
787                 goto Lstc;
788 
789             case TOK.final_:
790                 stc = STC.final_;
791                 goto Lstc;
792 
793             case TOK.auto_:
794                 stc = STC.auto_;
795                 goto Lstc;
796 
797             case TOK.scope_:
798                 stc = STC.scope_;
799                 goto Lstc;
800 
801             case TOK.override_:
802                 stc = STC.override_;
803                 goto Lstc;
804 
805             case TOK.abstract_:
806                 stc = STC.abstract_;
807                 goto Lstc;
808 
809             case TOK.synchronized_:
810                 stc = STC.synchronized_;
811                 goto Lstc;
812 
813             case TOK.nothrow_:
814                 stc = STC.nothrow_;
815                 goto Lstc;
816 
817             case TOK.pure_:
818                 stc = STC.pure_;
819                 goto Lstc;
820 
821             case TOK.ref_:
822                 stc = STC.ref_;
823                 goto Lstc;
824 
825             case TOK.gshared:
826                 stc = STC.gshared;
827                 goto Lstc;
828 
829             case TOK.at:
830                 {
831                     AST.Expressions* exps = null;
832                     stc = parseAttribute(exps);
833                     if (stc)
834                         goto Lstc; // it's a predefined attribute
835                     // no redundant/conflicting check for UDAs
836                     pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
837                     goto Lautodecl;
838                 }
839             Lstc:
840                 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
841                 nextToken();
842 
843             Lautodecl:
844 
845                 /* Look for auto initializers:
846                  *      storage_class identifier = initializer;
847                  *      storage_class identifier(...) = initializer;
848                  */
849                 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
850                 {
851                     a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
852                     if (a && a.dim)
853                         *pLastDecl = (*a)[a.dim - 1];
854                     if (pAttrs.udas)
855                     {
856                         s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
857                         pAttrs.udas = null;
858                     }
859                     break;
860                 }
861 
862                 /* Look for return type inference for template functions.
863                  */
864                 Token* tk;
865                 if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
866                     (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
867                      tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
868                      tk.value == TOK.identifier && tk.ident == Id._body))
869                 {
870                     // @@@DEPRECATED@@@
871                     // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
872                     // Deprecated in 2.097 - Can be removed from 2.117
873                     // The deprecation period is longer than usual as `body`
874                     // was quite widely used.
875                     if (tk.value == TOK.identifier && tk.ident == Id._body)
876                         deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
877 
878                     a = parseDeclarations(true, pAttrs, pAttrs.comment);
879                     if (a && a.dim)
880                         *pLastDecl = (*a)[a.dim - 1];
881                     if (pAttrs.udas)
882                     {
883                         s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
884                         pAttrs.udas = null;
885                     }
886                     break;
887                 }
888 
889                 a = parseBlock(pLastDecl, pAttrs);
890                 auto stc2 = getStorageClass!AST(pAttrs);
891                 if (stc2 != STC.undefined_)
892                 {
893                     s = new AST.StorageClassDeclaration(stc2, a);
894                 }
895                 if (pAttrs.udas)
896                 {
897                     if (s)
898                     {
899                         a = new AST.Dsymbols();
900                         a.push(s);
901                     }
902                     s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
903                     pAttrs.udas = null;
904                 }
905                 break;
906 
907             case TOK.deprecated_:
908                 {
909                     stc |= STC.deprecated_;
910                     if (!parseDeprecatedAttribute(pAttrs.depmsg))
911                         goto Lstc;
912 
913                     a = parseBlock(pLastDecl, pAttrs);
914                     s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
915                     pAttrs.depmsg = null;
916                     break;
917                 }
918             case TOK.leftBracket:
919                 {
920                     if (peekNext() == TOK.rightBracket)
921                         error("empty attribute list is not allowed");
922                     error("use `@(attributes)` instead of `[attributes]`");
923                     AST.Expressions* exps = parseArguments();
924                     // no redundant/conflicting check for UDAs
925 
926                     pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
927                     a = parseBlock(pLastDecl, pAttrs);
928                     if (pAttrs.udas)
929                     {
930                         s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
931                         pAttrs.udas = null;
932                     }
933                     break;
934                 }
935             case TOK.extern_:
936                 {
937                     if (peekNext() != TOK.leftParenthesis)
938                     {
939                         stc = STC.extern_;
940                         goto Lstc;
941                     }
942 
943                     const linkLoc = token.loc;
944                     auto res = parseLinkage();
945                     if (pAttrs.link != LINK.default_)
946                     {
947                         if (pAttrs.link != res.link)
948                         {
949                             error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
950                         }
951                         else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
952                         {
953                             // Allow:
954                             //      extern(C++, foo) extern(C++, bar) void foo();
955                             // to be equivalent with:
956                             //      extern(C++, foo.bar) void foo();
957                             // Allow also:
958                             //      extern(C++, "ns") extern(C++, class) struct test {}
959                             //      extern(C++, class) extern(C++, "ns") struct test {}
960                         }
961                         else
962                             error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
963                     }
964                     pAttrs.link = res.link;
965                     this.linkage = res.link;
966                     this.linkLoc = linkLoc;
967                     a = parseBlock(pLastDecl, pAttrs);
968                     if (res.idents)
969                     {
970                         assert(res.link == LINK.cpp);
971                         assert(res.idents.dim);
972                         for (size_t i = res.idents.dim; i;)
973                         {
974                             Identifier id = (*res.idents)[--i];
975                             if (s)
976                             {
977                                 a = new AST.Dsymbols();
978                                 a.push(s);
979                             }
980                             s = new AST.Nspace(linkLoc, id, null, a);
981                         }
982                         pAttrs.link = LINK.default_;
983                     }
984                     else if (res.identExps)
985                     {
986                         assert(res.link == LINK.cpp);
987                         assert(res.identExps.dim);
988                         for (size_t i = res.identExps.dim; i;)
989                         {
990                             AST.Expression exp = (*res.identExps)[--i];
991                             if (s)
992                             {
993                                 a = new AST.Dsymbols();
994                                 a.push(s);
995                             }
996                             s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
997                         }
998                         pAttrs.link = LINK.default_;
999                     }
1000                     else if (res.cppmangle != CPPMANGLE.def)
1001                     {
1002                         assert(res.link == LINK.cpp);
1003                         s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
1004                     }
1005                     else if (pAttrs.link != LINK.default_)
1006                     {
1007                         s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
1008                         pAttrs.link = LINK.default_;
1009                     }
1010                     break;
1011                 }
1012 
1013             case TOK.private_:
1014                 prot = AST.Visibility.Kind.private_;
1015                 goto Lprot;
1016 
1017             case TOK.package_:
1018                 prot = AST.Visibility.Kind.package_;
1019                 goto Lprot;
1020 
1021             case TOK.protected_:
1022                 prot = AST.Visibility.Kind.protected_;
1023                 goto Lprot;
1024 
1025             case TOK.public_:
1026                 prot = AST.Visibility.Kind.public_;
1027                 goto Lprot;
1028 
1029             case TOK.export_:
1030                 prot = AST.Visibility.Kind.export_;
1031                 goto Lprot;
1032             Lprot:
1033                 {
1034                     if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1035                     {
1036                         if (pAttrs.visibility.kind != prot)
1037                             error("conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
1038                         else
1039                             error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
1040                     }
1041                     pAttrs.visibility.kind = prot;
1042 
1043                     nextToken();
1044 
1045                     // optional qualified package identifier to bind
1046                     // visibility to
1047                     Identifier[] pkg_prot_idents;
1048                     if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
1049                     {
1050                         pkg_prot_idents = parseQualifiedIdentifier("protection package");
1051                         if (pkg_prot_idents)
1052                             check(TOK.rightParenthesis);
1053                         else
1054                         {
1055                             while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1056                                 nextToken();
1057                             nextToken();
1058                             break;
1059                         }
1060                     }
1061 
1062                     const attrloc = token.loc;
1063                     a = parseBlock(pLastDecl, pAttrs);
1064                     if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1065                     {
1066                         if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
1067                             s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
1068                         else
1069                             s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
1070 
1071                         pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
1072                     }
1073                     break;
1074                 }
1075             case TOK.align_:
1076                 {
1077                     const attrLoc = token.loc;
1078 
1079                     nextToken();
1080 
1081                     AST.Expression e = null; // default
1082                     if (token.value == TOK.leftParenthesis)
1083                     {
1084                         nextToken();
1085                         e = parseAssignExp();
1086                         check(TOK.rightParenthesis);
1087                     }
1088 
1089                     if (pAttrs.setAlignment)
1090                     {
1091                         if (e)
1092                             error("redundant alignment attribute `align(%s)`", e.toChars());
1093                         else
1094                             error("redundant alignment attribute `align`");
1095                     }
1096 
1097                     pAttrs.setAlignment = true;
1098                     pAttrs.ealign = e;
1099                     a = parseBlock(pLastDecl, pAttrs);
1100                     if (pAttrs.setAlignment)
1101                     {
1102                         s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
1103                         pAttrs.setAlignment = false;
1104                         pAttrs.ealign = null;
1105                     }
1106                     break;
1107                 }
1108             case TOK.pragma_:
1109                 {
1110                     AST.Expressions* args = null;
1111                     const loc = token.loc;
1112 
1113                     nextToken();
1114                     check(TOK.leftParenthesis);
1115                     if (token.value != TOK.identifier)
1116                     {
1117                         error("`pragma(identifier)` expected");
1118                         goto Lerror;
1119                     }
1120                     Identifier ident = token.ident;
1121                     nextToken();
1122                     if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
1123                         args = parseArguments(); // pragma(identifier, args...)
1124                     else
1125                         check(TOK.rightParenthesis); // pragma(identifier)
1126 
1127                     AST.Dsymbols* a2 = null;
1128                     if (token.value == TOK.semicolon)
1129                     {
1130                         /* https://issues.dlang.org/show_bug.cgi?id=2354
1131                          * Accept single semicolon as an empty
1132                          * DeclarationBlock following attribute.
1133                          *
1134                          * Attribute DeclarationBlock
1135                          * Pragma    DeclDef
1136                          *           ;
1137                          */
1138                         nextToken();
1139                     }
1140                     else
1141                         a2 = parseBlock(pLastDecl);
1142                     s = new AST.PragmaDeclaration(loc, ident, args, a2);
1143                     break;
1144                 }
1145             case TOK.debug_:
1146                 startloc = token.loc;
1147                 nextToken();
1148                 if (token.value == TOK.assign)
1149                 {
1150                     nextToken();
1151                     if (token.value == TOK.identifier)
1152                         s = new AST.DebugSymbol(token.loc, token.ident);
1153                     else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1154                         s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
1155                     else
1156                     {
1157                         error("identifier or integer expected, not `%s`", token.toChars());
1158                         s = null;
1159                     }
1160                     nextToken();
1161                     if (token.value != TOK.semicolon)
1162                         error("semicolon expected");
1163                     nextToken();
1164                     break;
1165                 }
1166 
1167                 condition = parseDebugCondition();
1168                 goto Lcondition;
1169 
1170             case TOK.version_:
1171                 startloc = token.loc;
1172                 nextToken();
1173                 if (token.value == TOK.assign)
1174                 {
1175                     nextToken();
1176                     if (token.value == TOK.identifier)
1177                         s = new AST.VersionSymbol(token.loc, token.ident);
1178                     else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1179                         s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
1180                     else
1181                     {
1182                         error("identifier or integer expected, not `%s`", token.toChars());
1183                         s = null;
1184                     }
1185                     nextToken();
1186                     if (token.value != TOK.semicolon)
1187                         error("semicolon expected");
1188                     nextToken();
1189                     break;
1190                 }
1191                 condition = parseVersionCondition();
1192                 goto Lcondition;
1193 
1194             Lcondition:
1195                 {
1196                     AST.Dsymbols* athen;
1197                     if (token.value == TOK.colon)
1198                         athen = parseBlock(pLastDecl);
1199                     else
1200                     {
1201                         const lookingForElseSave = lookingForElse;
1202                         lookingForElse = token.loc;
1203                         athen = parseBlock(pLastDecl);
1204                         lookingForElse = lookingForElseSave;
1205                     }
1206                     AST.Dsymbols* aelse = null;
1207                     if (token.value == TOK.else_)
1208                     {
1209                         const elseloc = token.loc;
1210                         nextToken();
1211                         aelse = parseBlock(pLastDecl);
1212                         checkDanglingElse(elseloc);
1213                     }
1214                     s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
1215                     break;
1216                 }
1217             case TOK.semicolon:
1218                 // empty declaration
1219                 //error("empty declaration");
1220                 nextToken();
1221                 continue;
1222 
1223             default:
1224                 error("declaration expected, not `%s`", token.toChars());
1225             Lerror:
1226                 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1227                     nextToken();
1228                 nextToken();
1229                 s = null;
1230                 continue;
1231             }
1232 
1233             if (s)
1234             {
1235                 if (!s.isAttribDeclaration())
1236                     *pLastDecl = s;
1237                 decldefs.push(s);
1238                 addComment(s, pAttrs.comment);
1239             }
1240             else if (a && a.dim)
1241             {
1242                 decldefs.append(a);
1243             }
1244         }
1245         while (!once);
1246 
1247         linkage = linksave;
1248 
1249         return decldefs;
1250     }
1251 
1252     /*****************************************
1253      * Parse auto declarations of the form:
1254      *   storageClass ident = init, ident = init, ... ;
1255      * and return the array of them.
1256      * Starts with token on the first ident.
1257      * Ends with scanner past closing ';'
1258      */
1259     private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1260     {
1261         //printf("parseAutoDeclarations\n");
1262         auto a = new AST.Dsymbols();
1263 
1264         while (1)
1265         {
1266             const loc = token.loc;
1267             Identifier ident = token.ident;
1268             nextToken(); // skip over ident
1269 
1270             AST.TemplateParameters* tpl = null;
1271             if (token.value == TOK.leftParenthesis)
1272                 tpl = parseTemplateParameterList();
1273 
1274             check(TOK.assign);   // skip over '='
1275             AST.Initializer _init = parseInitializer();
1276             auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1277 
1278             AST.Dsymbol s = v;
1279             if (tpl)
1280             {
1281                 auto a2 = new AST.Dsymbols();
1282                 a2.push(v);
1283                 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1284                 s = tempdecl;
1285             }
1286             a.push(s);
1287             switch (token.value)
1288             {
1289             case TOK.semicolon:
1290                 nextToken();
1291                 addComment(s, comment);
1292                 break;
1293 
1294             case TOK.comma:
1295                 nextToken();
1296                 if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1297                 {
1298                     error("identifier expected following comma");
1299                     break;
1300                 }
1301                 addComment(s, comment);
1302                 continue;
1303 
1304             default:
1305                 error("semicolon expected following auto declaration, not `%s`", token.toChars());
1306                 break;
1307             }
1308             break;
1309         }
1310         return a;
1311     }
1312 
1313     /********************************************
1314      * Parse declarations after an align, visibility, or extern decl.
1315      */
1316     private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1317     {
1318         AST.Dsymbols* a = null;
1319 
1320         //printf("parseBlock()\n");
1321         switch (token.value)
1322         {
1323         case TOK.semicolon:
1324             error("declaration expected following attribute, not `;`");
1325             nextToken();
1326             break;
1327 
1328         case TOK.endOfFile:
1329             error("declaration expected following attribute, not end of file");
1330             break;
1331 
1332         case TOK.leftCurly:
1333             {
1334                 const lookingForElseSave = lookingForElse;
1335                 lookingForElse = Loc();
1336 
1337                 nextToken();
1338                 a = parseDeclDefs(0, pLastDecl);
1339                 if (token.value != TOK.rightCurly)
1340                 {
1341                     /* { */
1342                     error("matching `}` expected, not `%s`", token.toChars());
1343                 }
1344                 else
1345                     nextToken();
1346                 lookingForElse = lookingForElseSave;
1347                 break;
1348             }
1349         case TOK.colon:
1350             nextToken();
1351             a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1352             break;
1353 
1354         default:
1355             a = parseDeclDefs(1, pLastDecl, pAttrs);
1356             break;
1357         }
1358         return a;
1359     }
1360 
1361     /**
1362      * Provide an error message if `added` contains storage classes which are
1363      * redundant with those in `orig`; otherwise, return the combination.
1364      *
1365      * Params:
1366      *   orig = The already applied storage class.
1367      *   added = The new storage class to add to `orig`.
1368      *
1369      * Returns:
1370      *   The combination of both storage classes (`orig | added`).
1371      */
1372     private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
1373     {
1374         if (orig & added)
1375         {
1376             OutBuffer buf;
1377             AST.stcToBuffer(&buf, added);
1378             error("redundant attribute `%s`", buf.peekChars());
1379             return orig | added;
1380         }
1381 
1382         const Redundant = (STC.const_ | STC.scope_ |
1383                            (global.params.previewIn ? STC.ref_ : 0));
1384         orig |= added;
1385 
1386         if ((orig & STC.in_) && (added & Redundant))
1387         {
1388             if (added & STC.const_)
1389                 error("attribute `const` is redundant with previously-applied `in`");
1390             else if (global.params.previewIn)
1391             {
1392                 error("attribute `%s` is redundant with previously-applied `in`",
1393                       (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
1394             }
1395             else
1396                 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1397             return orig;
1398         }
1399 
1400         if ((added & STC.in_) && (orig & Redundant))
1401         {
1402             if (orig & STC.const_)
1403                 error("attribute `in` cannot be added after `const`: remove `const`");
1404             else if (global.params.previewIn)
1405             {
1406                 // Windows `printf` does not support `%1$s`
1407                 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1408                 error("attribute `in` cannot be added after `%s`: remove `%s`",
1409                       stc_str, stc_str);
1410             }
1411             else
1412                 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1413             return orig;
1414         }
1415 
1416         if (added & (STC.const_ | STC.immutable_ | STC.manifest))
1417         {
1418             StorageClass u = orig & (STC.const_ | STC.immutable_ | STC.manifest);
1419             if (u & (u - 1))
1420                 error("conflicting attribute `%s`", Token.toChars(token.value));
1421         }
1422         if (added & (STC.gshared | STC.shared_ | STC.tls))
1423         {
1424             StorageClass u = orig & (STC.gshared | STC.shared_ | STC.tls);
1425             if (u & (u - 1))
1426                 error("conflicting attribute `%s`", Token.toChars(token.value));
1427         }
1428         if (added & STC.safeGroup)
1429         {
1430             StorageClass u = orig & STC.safeGroup;
1431             if (u & (u - 1))
1432                 error("conflicting attribute `@%s`", token.toChars());
1433         }
1434 
1435         return orig;
1436     }
1437 
1438     /***********************************************
1439      * Parse attribute(s), lexer is on '@'.
1440      *
1441      * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1442      * or be user-defined (UDAs). In the former case, we return the storage
1443      * class via the return value, while in thelater case we return `0`
1444      * and set `pudas`.
1445      *
1446      * Params:
1447      *   pudas = An array of UDAs to append to
1448      *
1449      * Returns:
1450      *   If the attribute is builtin, the return value will be non-zero.
1451      *   Otherwise, 0 is returned, and `pudas` will be appended to.
1452      */
1453     private StorageClass parseAttribute(ref AST.Expressions* udas)
1454     {
1455         nextToken();
1456         if (token.value == TOK.identifier)
1457         {
1458             // If we find a builtin attribute, we're done, return immediately.
1459             if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1460                 return stc;
1461 
1462             // Allow identifier, template instantiation, or function call
1463             // for `@Argument` (single UDA) form.
1464             AST.Expression exp = parsePrimaryExp();
1465             if (token.value == TOK.leftParenthesis)
1466             {
1467                 const loc = token.loc;
1468                 exp = new AST.CallExp(loc, exp, parseArguments());
1469             }
1470 
1471             if (udas is null)
1472                 udas = new AST.Expressions();
1473             udas.push(exp);
1474             return 0;
1475         }
1476 
1477         if (token.value == TOK.leftParenthesis)
1478         {
1479             // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1480             if (peekNext() == TOK.rightParenthesis)
1481                 error("empty attribute list is not allowed");
1482             udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
1483             return 0;
1484         }
1485 
1486         if (token.isKeyword())
1487             error("`%s` is a keyword, not an `@` attribute", token.toChars());
1488         else
1489             error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1490 
1491         return 0;
1492     }
1493 
1494     /***********************************************
1495      * Parse const/immutable/shared/inout/nothrow/pure postfix
1496      */
1497     private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1498     {
1499         while (1)
1500         {
1501             StorageClass stc;
1502             switch (token.value)
1503             {
1504             case TOK.const_:
1505                 stc = STC.const_;
1506                 break;
1507 
1508             case TOK.immutable_:
1509                 stc = STC.immutable_;
1510                 break;
1511 
1512             case TOK.shared_:
1513                 stc = STC.shared_;
1514                 break;
1515 
1516             case TOK.inout_:
1517                 stc = STC.wild;
1518                 break;
1519 
1520             case TOK.nothrow_:
1521                 stc = STC.nothrow_;
1522                 break;
1523 
1524             case TOK.pure_:
1525                 stc = STC.pure_;
1526                 break;
1527 
1528             case TOK.return_:
1529                 stc = STC.return_;
1530                 break;
1531 
1532             case TOK.scope_:
1533                 stc = STC.scope_;
1534                 break;
1535 
1536             case TOK.at:
1537                 {
1538                     AST.Expressions* udas = null;
1539                     stc = parseAttribute(udas);
1540                     if (udas)
1541                     {
1542                         if (pudas)
1543                             *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1544                         else
1545                         {
1546                             // Disallow:
1547                             //      void function() @uda fp;
1548                             //      () @uda { return 1; }
1549                             error("user-defined attributes cannot appear as postfixes");
1550                         }
1551                         continue;
1552                     }
1553                     break;
1554                 }
1555             default:
1556                 return storageClass;
1557             }
1558             storageClass = appendStorageClass(storageClass, stc);
1559             nextToken();
1560         }
1561     }
1562 
1563     private StorageClass parseTypeCtor()
1564     {
1565         StorageClass storageClass = STC.undefined_;
1566 
1567         while (1)
1568         {
1569             if (peekNext() == TOK.leftParenthesis)
1570                 return storageClass;
1571 
1572             StorageClass stc;
1573             switch (token.value)
1574             {
1575             case TOK.const_:
1576                 stc = STC.const_;
1577                 break;
1578 
1579             case TOK.immutable_:
1580                 stc = STC.immutable_;
1581                 break;
1582 
1583             case TOK.shared_:
1584                 stc = STC.shared_;
1585                 break;
1586 
1587             case TOK.inout_:
1588                 stc = STC.wild;
1589                 break;
1590 
1591             default:
1592                 return storageClass;
1593             }
1594             storageClass = appendStorageClass(storageClass, stc);
1595             nextToken();
1596         }
1597     }
1598 
1599     /**************************************
1600      * Parse constraint.
1601      * Constraint is of the form:
1602      *      if ( ConstraintExpression )
1603      */
1604     private AST.Expression parseConstraint()
1605     {
1606         AST.Expression e = null;
1607         if (token.value == TOK.if_)
1608         {
1609             nextToken(); // skip over 'if'
1610             check(TOK.leftParenthesis);
1611             e = parseExpression();
1612             check(TOK.rightParenthesis);
1613         }
1614         return e;
1615     }
1616 
1617     /**************************************
1618      * Parse a TemplateDeclaration.
1619      */
1620     private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1621     {
1622         AST.TemplateDeclaration tempdecl;
1623         Identifier id;
1624         AST.TemplateParameters* tpl;
1625         AST.Dsymbols* decldefs;
1626         AST.Expression constraint = null;
1627         const loc = token.loc;
1628 
1629         nextToken();
1630         if (token.value != TOK.identifier)
1631         {
1632             error("identifier expected following `template`");
1633             goto Lerr;
1634         }
1635         id = token.ident;
1636         nextToken();
1637         tpl = parseTemplateParameterList();
1638         if (!tpl)
1639             goto Lerr;
1640 
1641         constraint = parseConstraint();
1642 
1643         if (token.value != TOK.leftCurly)
1644         {
1645             error("members of template declaration expected");
1646             goto Lerr;
1647         }
1648         decldefs = parseBlock(null);
1649 
1650         tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1651         return tempdecl;
1652 
1653     Lerr:
1654         return null;
1655     }
1656 
1657     /******************************************
1658      * Parse template parameter list.
1659      * Input:
1660      *      flag    0: parsing "( list )"
1661      *              1: parsing non-empty "list $(RPAREN)"
1662      */
1663     private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1664     {
1665         auto tpl = new AST.TemplateParameters();
1666 
1667         if (!flag && token.value != TOK.leftParenthesis)
1668         {
1669             error("parenthesized template parameter list expected following template identifier");
1670             goto Lerr;
1671         }
1672         nextToken();
1673 
1674         // Get array of TemplateParameters
1675         if (flag || token.value != TOK.rightParenthesis)
1676         {
1677             while (token.value != TOK.rightParenthesis)
1678             {
1679                 AST.TemplateParameter tp;
1680                 Loc loc;
1681                 Identifier tp_ident = null;
1682                 AST.Type tp_spectype = null;
1683                 AST.Type tp_valtype = null;
1684                 AST.Type tp_defaulttype = null;
1685                 AST.Expression tp_specvalue = null;
1686                 AST.Expression tp_defaultvalue = null;
1687 
1688                 // Get TemplateParameter
1689 
1690                 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1691                 const tv = peekNext();
1692                 if (token.value == TOK.alias_)
1693                 {
1694                     // AliasParameter
1695                     nextToken();
1696                     loc = token.loc; // todo
1697                     AST.Type spectype = null;
1698                     if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1699                     {
1700                         spectype = parseType(&tp_ident);
1701                     }
1702                     else
1703                     {
1704                         if (token.value != TOK.identifier)
1705                         {
1706                             error("identifier expected for template `alias` parameter");
1707                             goto Lerr;
1708                         }
1709                         tp_ident = token.ident;
1710                         nextToken();
1711                     }
1712                     RootObject spec = null;
1713                     if (token.value == TOK.colon) // : Type
1714                     {
1715                         nextToken();
1716                         if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1717                             spec = parseType();
1718                         else
1719                             spec = parseCondExp();
1720                     }
1721                     RootObject def = null;
1722                     if (token.value == TOK.assign) // = Type
1723                     {
1724                         nextToken();
1725                         if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1726                             def = parseType();
1727                         else
1728                             def = parseCondExp();
1729                     }
1730                     tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1731                 }
1732                 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1733                 {
1734                     // TypeParameter
1735                     if (token.value != TOK.identifier)
1736                     {
1737                         error("identifier expected for template type parameter");
1738                         goto Lerr;
1739                     }
1740                     loc = token.loc;
1741                     tp_ident = token.ident;
1742                     nextToken();
1743                     if (token.value == TOK.colon) // : Type
1744                     {
1745                         nextToken();
1746                         tp_spectype = parseType();
1747                     }
1748                     if (token.value == TOK.assign) // = Type
1749                     {
1750                         nextToken();
1751                         tp_defaulttype = parseType();
1752                     }
1753                     tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1754                 }
1755                 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1756                 {
1757                     // ident...
1758                     loc = token.loc;
1759                     tp_ident = token.ident;
1760                     nextToken();
1761                     nextToken();
1762                     tp = new AST.TemplateTupleParameter(loc, tp_ident);
1763                 }
1764                 else if (token.value == TOK.this_)
1765                 {
1766                     // ThisParameter
1767                     nextToken();
1768                     if (token.value != TOK.identifier)
1769                     {
1770                         error("identifier expected for template `this` parameter");
1771                         goto Lerr;
1772                     }
1773                     loc = token.loc;
1774                     tp_ident = token.ident;
1775                     nextToken();
1776                     if (token.value == TOK.colon) // : Type
1777                     {
1778                         nextToken();
1779                         tp_spectype = parseType();
1780                     }
1781                     if (token.value == TOK.assign) // = Type
1782                     {
1783                         nextToken();
1784                         tp_defaulttype = parseType();
1785                     }
1786                     tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1787                 }
1788                 else
1789                 {
1790                     // ValueParameter
1791                     loc = token.loc; // todo
1792                     tp_valtype = parseType(&tp_ident);
1793                     if (!tp_ident)
1794                     {
1795                         error("identifier expected for template value parameter");
1796                         tp_ident = Identifier.idPool("error");
1797                     }
1798                     if (token.value == TOK.colon) // : CondExpression
1799                     {
1800                         nextToken();
1801                         tp_specvalue = parseCondExp();
1802                     }
1803                     if (token.value == TOK.assign) // = CondExpression
1804                     {
1805                         nextToken();
1806                         tp_defaultvalue = parseDefaultInitExp();
1807                     }
1808                     tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1809                 }
1810                 tpl.push(tp);
1811                 if (token.value != TOK.comma)
1812                     break;
1813                 nextToken();
1814             }
1815         }
1816         check(TOK.rightParenthesis);
1817 
1818     Lerr:
1819         return tpl;
1820     }
1821 
1822     /******************************************
1823      * Parse template mixin.
1824      *      mixin Foo;
1825      *      mixin Foo!(args);
1826      *      mixin a.b.c!(args).Foo!(args);
1827      *      mixin Foo!(args) identifier;
1828      *      mixin typeof(expr).identifier!(args);
1829      */
1830     private AST.Dsymbol parseMixin()
1831     {
1832         AST.TemplateMixin tm;
1833         Identifier id;
1834         AST.Objects* tiargs;
1835 
1836         //printf("parseMixin()\n");
1837         const locMixin = token.loc;
1838         nextToken(); // skip 'mixin'
1839 
1840         auto loc = token.loc;
1841         AST.TypeQualified tqual = null;
1842         if (token.value == TOK.dot)
1843         {
1844             id = Id.empty;
1845         }
1846         else
1847         {
1848             if (token.value == TOK.typeof_)
1849             {
1850                 tqual = parseTypeof();
1851                 check(TOK.dot);
1852             }
1853             if (token.value != TOK.identifier)
1854             {
1855                 error("identifier expected, not `%s`", token.toChars());
1856                 id = Id.empty;
1857             }
1858             else
1859                 id = token.ident;
1860             nextToken();
1861         }
1862 
1863         while (1)
1864         {
1865             tiargs = null;
1866             if (token.value == TOK.not)
1867             {
1868                 tiargs = parseTemplateArguments();
1869             }
1870 
1871             if (tiargs && token.value == TOK.dot)
1872             {
1873                 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1874                 if (!tqual)
1875                     tqual = new AST.TypeInstance(loc, tempinst);
1876                 else
1877                     tqual.addInst(tempinst);
1878                 tiargs = null;
1879             }
1880             else
1881             {
1882                 if (!tqual)
1883                     tqual = new AST.TypeIdentifier(loc, id);
1884                 else
1885                     tqual.addIdent(id);
1886             }
1887 
1888             if (token.value != TOK.dot)
1889                 break;
1890 
1891             nextToken();
1892             if (token.value != TOK.identifier)
1893             {
1894                 error("identifier expected following `.` instead of `%s`", token.toChars());
1895                 break;
1896             }
1897             loc = token.loc;
1898             id = token.ident;
1899             nextToken();
1900         }
1901 
1902         id = null;
1903         if (token.value == TOK.identifier)
1904         {
1905             id = token.ident;
1906             nextToken();
1907         }
1908 
1909         tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1910         if (token.value != TOK.semicolon)
1911             error("`;` expected after `mixin`");
1912         nextToken();
1913 
1914         return tm;
1915     }
1916 
1917     /******************************************
1918      * Parse template arguments.
1919      * Input:
1920      *      current token is opening '!'
1921      * Output:
1922      *      current token is one after closing '$(RPAREN)'
1923      */
1924     private AST.Objects* parseTemplateArguments()
1925     {
1926         AST.Objects* tiargs;
1927 
1928         nextToken();
1929         if (token.value == TOK.leftParenthesis)
1930         {
1931             // ident!(template_arguments)
1932             tiargs = parseTemplateArgumentList();
1933         }
1934         else
1935         {
1936             // ident!template_argument
1937             tiargs = parseTemplateSingleArgument();
1938         }
1939         if (token.value == TOK.not)
1940         {
1941             TOK tok = peekNext();
1942             if (tok != TOK.is_ && tok != TOK.in_)
1943             {
1944                 error("multiple ! arguments are not allowed");
1945             Lagain:
1946                 nextToken();
1947                 if (token.value == TOK.leftParenthesis)
1948                     parseTemplateArgumentList();
1949                 else
1950                     parseTemplateSingleArgument();
1951                 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1952                     goto Lagain;
1953             }
1954         }
1955         return tiargs;
1956     }
1957 
1958     /******************************************
1959      * Parse template argument list.
1960      * Input:
1961      *      current token is opening '$(LPAREN)',
1962      *          or ',' for __traits
1963      * Output:
1964      *      current token is one after closing '$(RPAREN)'
1965      */
1966     private AST.Objects* parseTemplateArgumentList()
1967     {
1968         //printf("Parser::parseTemplateArgumentList()\n");
1969         auto tiargs = new AST.Objects();
1970         TOK endtok = TOK.rightParenthesis;
1971         assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1972         nextToken();
1973 
1974         // Get TemplateArgumentList
1975         while (token.value != endtok)
1976         {
1977             tiargs.push(parseTypeOrAssignExp());
1978             if (token.value != TOK.comma)
1979                 break;
1980             nextToken();
1981         }
1982         check(endtok, "template argument list");
1983         return tiargs;
1984     }
1985 
1986     /***************************************
1987      * Parse a Type or an Expression
1988      * Returns:
1989      *  RootObject representing the AST
1990      */
1991     RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1992     {
1993         return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1994             ? parseType()           // argument is a type
1995             : parseAssignExp();     // argument is an expression
1996     }
1997 
1998     /*****************************
1999      * Parse single template argument, to support the syntax:
2000      *      foo!arg
2001      * Input:
2002      *      current token is the arg
2003      */
2004     private AST.Objects* parseTemplateSingleArgument()
2005     {
2006         //printf("parseTemplateSingleArgument()\n");
2007         auto tiargs = new AST.Objects();
2008         AST.Type ta;
2009         switch (token.value)
2010         {
2011         case TOK.identifier:
2012             ta = new AST.TypeIdentifier(token.loc, token.ident);
2013             goto LabelX;
2014 
2015         case TOK.vector:
2016             ta = parseVector();
2017             goto LabelX;
2018 
2019         case TOK.void_:
2020             ta = AST.Type.tvoid;
2021             goto LabelX;
2022 
2023         case TOK.int8:
2024             ta = AST.Type.tint8;
2025             goto LabelX;
2026 
2027         case TOK.uns8:
2028             ta = AST.Type.tuns8;
2029             goto LabelX;
2030 
2031         case TOK.int16:
2032             ta = AST.Type.tint16;
2033             goto LabelX;
2034 
2035         case TOK.uns16:
2036             ta = AST.Type.tuns16;
2037             goto LabelX;
2038 
2039         case TOK.int32:
2040             ta = AST.Type.tint32;
2041             goto LabelX;
2042 
2043         case TOK.uns32:
2044             ta = AST.Type.tuns32;
2045             goto LabelX;
2046 
2047         case TOK.int64:
2048             ta = AST.Type.tint64;
2049             goto LabelX;
2050 
2051         case TOK.uns64:
2052             ta = AST.Type.tuns64;
2053             goto LabelX;
2054 
2055         case TOK.int128:
2056             ta = AST.Type.tint128;
2057             goto LabelX;
2058 
2059         case TOK.uns128:
2060             ta = AST.Type.tuns128;
2061             goto LabelX;
2062 
2063         case TOK.float32:
2064             ta = AST.Type.tfloat32;
2065             goto LabelX;
2066 
2067         case TOK.float64:
2068             ta = AST.Type.tfloat64;
2069             goto LabelX;
2070 
2071         case TOK.float80:
2072             ta = AST.Type.tfloat80;
2073             goto LabelX;
2074 
2075         case TOK.imaginary32:
2076             ta = AST.Type.timaginary32;
2077             goto LabelX;
2078 
2079         case TOK.imaginary64:
2080             ta = AST.Type.timaginary64;
2081             goto LabelX;
2082 
2083         case TOK.imaginary80:
2084             ta = AST.Type.timaginary80;
2085             goto LabelX;
2086 
2087         case TOK.complex32:
2088             ta = AST.Type.tcomplex32;
2089             goto LabelX;
2090 
2091         case TOK.complex64:
2092             ta = AST.Type.tcomplex64;
2093             goto LabelX;
2094 
2095         case TOK.complex80:
2096             ta = AST.Type.tcomplex80;
2097             goto LabelX;
2098 
2099         case TOK.bool_:
2100             ta = AST.Type.tbool;
2101             goto LabelX;
2102 
2103         case TOK.char_:
2104             ta = AST.Type.tchar;
2105             goto LabelX;
2106 
2107         case TOK.wchar_:
2108             ta = AST.Type.twchar;
2109             goto LabelX;
2110 
2111         case TOK.dchar_:
2112             ta = AST.Type.tdchar;
2113             goto LabelX;
2114         LabelX:
2115             tiargs.push(ta);
2116             nextToken();
2117             break;
2118 
2119         case TOK.int32Literal:
2120         case TOK.uns32Literal:
2121         case TOK.int64Literal:
2122         case TOK.uns64Literal:
2123         case TOK.int128Literal:
2124         case TOK.uns128Literal:
2125         case TOK.float32Literal:
2126         case TOK.float64Literal:
2127         case TOK.float80Literal:
2128         case TOK.imaginary32Literal:
2129         case TOK.imaginary64Literal:
2130         case TOK.imaginary80Literal:
2131         case TOK.null_:
2132         case TOK.true_:
2133         case TOK.false_:
2134         case TOK.charLiteral:
2135         case TOK.wcharLiteral:
2136         case TOK.dcharLiteral:
2137         case TOK.string_:
2138         case TOK.hexadecimalString:
2139         case TOK.file:
2140         case TOK.fileFullPath:
2141         case TOK.line:
2142         case TOK.moduleString:
2143         case TOK.functionString:
2144         case TOK.prettyFunction:
2145         case TOK.this_:
2146             {
2147                 // Template argument is an expression
2148                 AST.Expression ea = parsePrimaryExp();
2149                 tiargs.push(ea);
2150                 break;
2151             }
2152         default:
2153             error("template argument expected following `!`");
2154             break;
2155         }
2156         return tiargs;
2157     }
2158 
2159     /**********************************
2160      * Parse a static assertion.
2161      * Current token is 'static'.
2162      */
2163     private AST.StaticAssert parseStaticAssert()
2164     {
2165         const loc = token.loc;
2166         AST.Expression exp;
2167         AST.Expression msg = null;
2168 
2169         //printf("parseStaticAssert()\n");
2170         nextToken();
2171         nextToken();
2172         check(TOK.leftParenthesis);
2173         exp = parseAssignExp();
2174         if (token.value == TOK.comma)
2175         {
2176             nextToken();
2177             if (token.value != TOK.rightParenthesis)
2178             {
2179                 msg = parseAssignExp();
2180                 if (token.value == TOK.comma)
2181                     nextToken();
2182             }
2183         }
2184         check(TOK.rightParenthesis);
2185         check(TOK.semicolon);
2186         return new AST.StaticAssert(loc, exp, msg);
2187     }
2188 
2189     /***********************************
2190      * Parse typeof(expression).
2191      * Current token is on the 'typeof'.
2192      */
2193     private AST.TypeQualified parseTypeof()
2194     {
2195         AST.TypeQualified t;
2196         const loc = token.loc;
2197 
2198         nextToken();
2199         check(TOK.leftParenthesis);
2200         if (token.value == TOK.return_) // typeof(return)
2201         {
2202             nextToken();
2203             t = new AST.TypeReturn(loc);
2204         }
2205         else
2206         {
2207             AST.Expression exp = parseExpression(); // typeof(expression)
2208             t = new AST.TypeTypeof(loc, exp);
2209         }
2210         check(TOK.rightParenthesis);
2211         return t;
2212     }
2213 
2214     /***********************************
2215      * Parse __vector(type).
2216      * Current token is on the '__vector'.
2217      */
2218     private AST.Type parseVector()
2219     {
2220         nextToken();
2221         check(TOK.leftParenthesis);
2222         AST.Type tb = parseType();
2223         check(TOK.rightParenthesis);
2224         return new AST.TypeVector(tb);
2225     }
2226 
2227     /***********************************
2228      * Parse:
2229      *      extern (linkage)
2230      *      extern (C++, namespaces)
2231      *      extern (C++, "namespace", "namespaces", ...)
2232      *      extern (C++, (StringExp))
2233      * The parser is on the 'extern' token.
2234      */
2235     private ParsedLinkage!(AST) parseLinkage()
2236     {
2237         ParsedLinkage!(AST) result;
2238         nextToken();
2239         assert(token.value == TOK.leftParenthesis);
2240         nextToken();
2241         ParsedLinkage!(AST) returnLinkage(LINK link)
2242         {
2243             check(TOK.rightParenthesis);
2244             result.link = link;
2245             return result;
2246         }
2247         ParsedLinkage!(AST) invalidLinkage()
2248         {
2249             error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2250             return returnLinkage(LINK.d);
2251         }
2252 
2253         if (token.value != TOK.identifier)
2254             return returnLinkage(LINK.d);
2255 
2256         Identifier id = token.ident;
2257         nextToken();
2258         if (id == Id.Windows)
2259             return returnLinkage(LINK.windows);
2260         else if (id == Id.D)
2261             return returnLinkage(LINK.d);
2262         else if (id == Id.System)
2263             return returnLinkage(LINK.system);
2264         else if (id == Id.Objective) // Looking for tokens "Objective-C"
2265         {
2266             if (token.value != TOK.min)
2267                 return invalidLinkage();
2268 
2269             nextToken();
2270             if (token.ident != Id.C)
2271                 return invalidLinkage();
2272 
2273             nextToken();
2274             return returnLinkage(LINK.objc);
2275         }
2276         else if (id != Id.C)
2277             return invalidLinkage();
2278 
2279         if (token.value != TOK.plusPlus)
2280             return returnLinkage(LINK.c);
2281 
2282         nextToken();
2283         if (token.value != TOK.comma) // , namespaces or class or struct
2284             return returnLinkage(LINK.cpp);
2285 
2286         nextToken();
2287 
2288         if (token.value == TOK.rightParenthesis)
2289             return returnLinkage(LINK.cpp); // extern(C++,)
2290 
2291         if (token.value == TOK.class_ || token.value == TOK.struct_)
2292         {
2293             result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2294             nextToken();
2295         }
2296         else if (token.value == TOK.identifier) // named scope namespace
2297         {
2298             result.idents = new AST.Identifiers();
2299             while (1)
2300             {
2301                 Identifier idn = token.ident;
2302                 result.idents.push(idn);
2303                 nextToken();
2304                 if (token.value == TOK.dot)
2305                 {
2306                     nextToken();
2307                     if (token.value == TOK.identifier)
2308                         continue;
2309                     error("identifier expected for C++ namespace");
2310                     result.idents = null;  // error occurred, invalidate list of elements.
2311                 }
2312                 break;
2313             }
2314         }
2315         else // non-scoped StringExp namespace
2316         {
2317             result.identExps = new AST.Expressions();
2318             while (1)
2319             {
2320                 result.identExps.push(parseCondExp());
2321                 if (token.value != TOK.comma)
2322                     break;
2323                 nextToken();
2324                 // Allow trailing commas as done for argument lists, arrays, ...
2325                 if (token.value == TOK.rightParenthesis)
2326                     break;
2327             }
2328         }
2329         return returnLinkage(LINK.cpp);
2330     }
2331 
2332     /***********************************
2333      * Parse ident1.ident2.ident3
2334      *
2335      * Params:
2336      *  entity = what qualified identifier is expected to resolve into.
2337      *     Used only for better error message
2338      *
2339      * Returns:
2340      *     array of identifiers with actual qualified one stored last
2341      */
2342     private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2343     {
2344         Identifier[] qualified;
2345 
2346         do
2347         {
2348             nextToken();
2349             if (token.value != TOK.identifier)
2350             {
2351                 error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2352                 return qualified;
2353             }
2354 
2355             Identifier id = token.ident;
2356             qualified ~= id;
2357 
2358             nextToken();
2359         }
2360         while (token.value == TOK.dot);
2361 
2362         return qualified;
2363     }
2364 
2365     /**************************************
2366      * Parse a debug conditional
2367      */
2368     private AST.Condition parseDebugCondition()
2369     {
2370         uint level = 1;
2371         Identifier id = null;
2372         Loc loc = token.loc;
2373 
2374         if (token.value == TOK.leftParenthesis)
2375         {
2376             nextToken();
2377 
2378             if (token.value == TOK.identifier)
2379                 id = token.ident;
2380             else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2381                 level = cast(uint)token.unsvalue;
2382             else
2383                 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2384             loc = token.loc;
2385             nextToken();
2386             check(TOK.rightParenthesis);
2387         }
2388         return new AST.DebugCondition(loc, mod, level, id);
2389     }
2390 
2391     /**************************************
2392      * Parse a version conditional
2393      */
2394     private AST.Condition parseVersionCondition()
2395     {
2396         uint level = 1;
2397         Identifier id = null;
2398         Loc loc;
2399 
2400         if (token.value == TOK.leftParenthesis)
2401         {
2402             nextToken();
2403             /* Allow:
2404              *    version (unittest)
2405              *    version (assert)
2406              * even though they are keywords
2407              */
2408             loc = token.loc;
2409             if (token.value == TOK.identifier)
2410                 id = token.ident;
2411             else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2412                 level = cast(uint)token.unsvalue;
2413             else if (token.value == TOK.unittest_)
2414                 id = Identifier.idPool(Token.toString(TOK.unittest_));
2415             else if (token.value == TOK.assert_)
2416                 id = Identifier.idPool(Token.toString(TOK.assert_));
2417             else
2418                 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2419             nextToken();
2420             check(TOK.rightParenthesis);
2421         }
2422         else
2423             error("(condition) expected following `version`");
2424         return new AST.VersionCondition(loc, mod, level, id);
2425     }
2426 
2427     /***********************************************
2428      *      static if (expression)
2429      *          body
2430      *      else
2431      *          body
2432      * Current token is 'static'.
2433      */
2434     private AST.Condition parseStaticIfCondition()
2435     {
2436         AST.Expression exp;
2437         AST.Condition condition;
2438         const loc = token.loc;
2439 
2440         nextToken();
2441         nextToken();
2442         if (token.value == TOK.leftParenthesis)
2443         {
2444             nextToken();
2445             exp = parseAssignExp();
2446             check(TOK.rightParenthesis);
2447         }
2448         else
2449         {
2450             error("(expression) expected following `static if`");
2451             exp = null;
2452         }
2453         condition = new AST.StaticIfCondition(loc, exp);
2454         return condition;
2455     }
2456 
2457     /*****************************************
2458      * Parse a constructor definition:
2459      *      this(parameters) { body }
2460      * or postblit:
2461      *      this(this) { body }
2462      * or constructor template:
2463      *      this(templateparameters)(parameters) { body }
2464      * Current token is 'this'.
2465      */
2466     private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2467     {
2468         AST.Expressions* udas = null;
2469         const loc = token.loc;
2470         StorageClass stc = getStorageClass!AST(pAttrs);
2471 
2472         nextToken();
2473         if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2474         {
2475             // this(this) { ... }
2476             nextToken();
2477             nextToken();
2478             check(TOK.rightParenthesis);
2479 
2480             stc = parsePostfix(stc, &udas);
2481             if (stc & STC.immutable_)
2482                 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2483             if (stc & STC.shared_)
2484                 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2485             if (stc & STC.const_)
2486                 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2487             if (stc & STC.static_)
2488                 error(loc, "postblit cannot be `static`");
2489 
2490             auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2491             AST.Dsymbol s = parseContracts(f);
2492             if (udas)
2493             {
2494                 auto a = new AST.Dsymbols();
2495                 a.push(f);
2496                 s = new AST.UserAttributeDeclaration(udas, a);
2497             }
2498             return s;
2499         }
2500 
2501         /* Look ahead to see if:
2502          *   this(...)(...)
2503          * which is a constructor template
2504          */
2505         AST.TemplateParameters* tpl = null;
2506         if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2507         {
2508             tpl = parseTemplateParameterList();
2509         }
2510 
2511         /* Just a regular constructor
2512          */
2513         auto parameterList = parseParameterList(null);
2514         stc = parsePostfix(stc, &udas);
2515 
2516         if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2517         {
2518             if (stc & STC.static_)
2519                 error(loc, "constructor cannot be static");
2520         }
2521         else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2522         {
2523             if (ss == STC.static_)
2524                 error(loc, "use `static this()` to declare a static constructor");
2525             else if (ss == (STC.shared_ | STC.static_))
2526                 error(loc, "use `shared static this()` to declare a shared static constructor");
2527         }
2528 
2529         AST.Expression constraint = tpl ? parseConstraint() : null;
2530 
2531         AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2532         tf = tf.addSTC(stc);
2533 
2534         auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2535         AST.Dsymbol s = parseContracts(f);
2536         if (udas)
2537         {
2538             auto a = new AST.Dsymbols();
2539             a.push(f);
2540             s = new AST.UserAttributeDeclaration(udas, a);
2541         }
2542 
2543         if (tpl)
2544         {
2545             // Wrap a template around it
2546             auto decldefs = new AST.Dsymbols();
2547             decldefs.push(s);
2548             s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2549         }
2550 
2551         return s;
2552     }
2553 
2554     /*****************************************
2555      * Parse a destructor definition:
2556      *      ~this() { body }
2557      * Current token is '~'.
2558      */
2559     private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2560     {
2561         AST.Expressions* udas = null;
2562         const loc = token.loc;
2563         StorageClass stc = getStorageClass!AST(pAttrs);
2564 
2565         nextToken();
2566         check(TOK.this_);
2567         check(TOK.leftParenthesis);
2568         check(TOK.rightParenthesis);
2569 
2570         stc = parsePostfix(stc, &udas);
2571         if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2572         {
2573             if (ss == STC.static_)
2574                 error(loc, "use `static ~this()` to declare a static destructor");
2575             else if (ss == (STC.shared_ | STC.static_))
2576                 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2577         }
2578 
2579         auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2580         AST.Dsymbol s = parseContracts(f);
2581         if (udas)
2582         {
2583             auto a = new AST.Dsymbols();
2584             a.push(f);
2585             s = new AST.UserAttributeDeclaration(udas, a);
2586         }
2587         return s;
2588     }
2589 
2590     /*****************************************
2591      * Parse a static constructor definition:
2592      *      static this() { body }
2593      * Current token is 'static'.
2594      */
2595     private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2596     {
2597         //Expressions *udas = NULL;
2598         const loc = token.loc;
2599         StorageClass stc = getStorageClass!AST(pAttrs);
2600 
2601         nextToken();
2602         nextToken();
2603         check(TOK.leftParenthesis);
2604         check(TOK.rightParenthesis);
2605 
2606         stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2607         if (stc & STC.shared_)
2608             error(loc, "use `shared static this()` to declare a shared static constructor");
2609         else if (stc & STC.static_)
2610             appendStorageClass(stc, STC.static_); // complaint for the redundancy
2611         else if (StorageClass modStc = stc & STC.TYPECTOR)
2612         {
2613             OutBuffer buf;
2614             AST.stcToBuffer(&buf, modStc);
2615             error(loc, "static constructor cannot be `%s`", buf.peekChars());
2616         }
2617         stc &= ~(STC.static_ | STC.TYPECTOR);
2618 
2619         auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2620         AST.Dsymbol s = parseContracts(f);
2621         return s;
2622     }
2623 
2624     /*****************************************
2625      * Parse a static destructor definition:
2626      *      static ~this() { body }
2627      * Current token is 'static'.
2628      */
2629     private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2630     {
2631         AST.Expressions* udas = null;
2632         const loc = token.loc;
2633         StorageClass stc = getStorageClass!AST(pAttrs);
2634 
2635         nextToken();
2636         nextToken();
2637         check(TOK.this_);
2638         check(TOK.leftParenthesis);
2639         check(TOK.rightParenthesis);
2640 
2641         stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2642         if (stc & STC.shared_)
2643             error(loc, "use `shared static ~this()` to declare a shared static destructor");
2644         else if (stc & STC.static_)
2645             appendStorageClass(stc, STC.static_); // complaint for the redundancy
2646         else if (StorageClass modStc = stc & STC.TYPECTOR)
2647         {
2648             OutBuffer buf;
2649             AST.stcToBuffer(&buf, modStc);
2650             error(loc, "static destructor cannot be `%s`", buf.peekChars());
2651         }
2652         stc &= ~(STC.static_ | STC.TYPECTOR);
2653 
2654         auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2655         AST.Dsymbol s = parseContracts(f);
2656         if (udas)
2657         {
2658             auto a = new AST.Dsymbols();
2659             a.push(f);
2660             s = new AST.UserAttributeDeclaration(udas, a);
2661         }
2662         return s;
2663     }
2664 
2665     /*****************************************
2666      * Parse a shared static constructor definition:
2667      *      shared static this() { body }
2668      * Current token is 'shared'.
2669      */
2670     private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2671     {
2672         //Expressions *udas = NULL;
2673         const loc = token.loc;
2674         StorageClass stc = getStorageClass!AST(pAttrs);
2675 
2676         nextToken();
2677         nextToken();
2678         nextToken();
2679         check(TOK.leftParenthesis);
2680         check(TOK.rightParenthesis);
2681 
2682         stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2683         if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2684             appendStorageClass(stc, ss); // complaint for the redundancy
2685         else if (StorageClass modStc = stc & STC.TYPECTOR)
2686         {
2687             OutBuffer buf;
2688             AST.stcToBuffer(&buf, modStc);
2689             error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2690         }
2691         stc &= ~(STC.static_ | STC.TYPECTOR);
2692 
2693         auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2694         AST.Dsymbol s = parseContracts(f);
2695         return s;
2696     }
2697 
2698     /*****************************************
2699      * Parse a shared static destructor definition:
2700      *      shared static ~this() { body }
2701      * Current token is 'shared'.
2702      */
2703     private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2704     {
2705         AST.Expressions* udas = null;
2706         const loc = token.loc;
2707         StorageClass stc = getStorageClass!AST(pAttrs);
2708 
2709         nextToken();
2710         nextToken();
2711         nextToken();
2712         check(TOK.this_);
2713         check(TOK.leftParenthesis);
2714         check(TOK.rightParenthesis);
2715 
2716         stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2717         if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2718             appendStorageClass(stc, ss); // complaint for the redundancy
2719         else if (StorageClass modStc = stc & STC.TYPECTOR)
2720         {
2721             OutBuffer buf;
2722             AST.stcToBuffer(&buf, modStc);
2723             error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2724         }
2725         stc &= ~(STC.static_ | STC.TYPECTOR);
2726 
2727         auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2728         AST.Dsymbol s = parseContracts(f);
2729         if (udas)
2730         {
2731             auto a = new AST.Dsymbols();
2732             a.push(f);
2733             s = new AST.UserAttributeDeclaration(udas, a);
2734         }
2735         return s;
2736     }
2737 
2738     /*****************************************
2739      * Parse an invariant definition:
2740      *      invariant { statements... }
2741      *      invariant() { statements... }
2742      *      invariant (expression);
2743      * Current token is 'invariant'.
2744      */
2745     private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2746     {
2747         const loc = token.loc;
2748         StorageClass stc = getStorageClass!AST(pAttrs);
2749 
2750         nextToken();
2751         if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2752         {
2753             nextToken();
2754             if (token.value != TOK.rightParenthesis) // invariant (expression);
2755             {
2756                 AST.Expression e = parseAssignExp(), msg = null;
2757                 if (token.value == TOK.comma)
2758                 {
2759                     nextToken();
2760                     if (token.value != TOK.rightParenthesis)
2761                     {
2762                         msg = parseAssignExp();
2763                         if (token.value == TOK.comma)
2764                             nextToken();
2765                     }
2766                 }
2767                 check(TOK.rightParenthesis);
2768                 check(TOK.semicolon);
2769                 e = new AST.AssertExp(loc, e, msg);
2770                 auto fbody = new AST.ExpStatement(loc, e);
2771                 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2772                 return f;
2773             }
2774             nextToken();
2775         }
2776 
2777         auto fbody = parseStatement(ParseStatementFlags.curly);
2778         auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2779         return f;
2780     }
2781 
2782     /*****************************************
2783      * Parse a unittest definition:
2784      *      unittest { body }
2785      * Current token is 'unittest'.
2786      */
2787     private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2788     {
2789         const loc = token.loc;
2790         StorageClass stc = getStorageClass!AST(pAttrs);
2791 
2792         nextToken();
2793 
2794         const(char)* begPtr = token.ptr + 1; // skip '{'
2795         const(char)* endPtr = null;
2796         AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2797 
2798         /** Extract unittest body as a string. Must be done eagerly since memory
2799          will be released by the lexer before doc gen. */
2800         char* docline = null;
2801         if (global.params.doDocComments && endPtr > begPtr)
2802         {
2803             /* Remove trailing whitespaces */
2804             for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2805             {
2806                 endPtr = p;
2807             }
2808 
2809             size_t len = endPtr - begPtr;
2810             if (len > 0)
2811             {
2812                 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2813                 memcpy(docline, begPtr, len);
2814                 docline[len] = '\n'; // Terminate all lines by LF
2815                 docline[len + 1] = '\0';
2816             }
2817         }
2818 
2819         auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2820         f.fbody = sbody;
2821         return f;
2822     }
2823 
2824     /*****************************************
2825      * Parse a new definition:
2826      *      @disable new();
2827      * Current token is 'new'.
2828      */
2829     private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2830     {
2831         const loc = token.loc;
2832         StorageClass stc = getStorageClass!AST(pAttrs);
2833         if (!(stc & STC.disable))
2834         {
2835             error("`new` allocator must be annotated with `@disabled`");
2836         }
2837         nextToken();
2838 
2839         /* @@@DEPRECATED_2.098@@@
2840          * After deprecation period (2.108), remove all code in the version(all) block.
2841          */
2842         version (all)
2843         {
2844             auto parameterList = parseParameterList(null);  // parameterList ignored
2845             if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2846                 deprecation("`new` allocator with non-empty parameter list is deprecated");
2847             auto f = new AST.NewDeclaration(loc, stc);
2848             if (token.value != TOK.semicolon)
2849             {
2850                 deprecation("`new` allocator with function definition is deprecated");
2851                 parseContracts(f);  // body ignored
2852                 f.fbody = null;
2853                 f.fensures = null;
2854                 f.frequires = null;
2855             }
2856             else
2857                 nextToken();
2858             return f;
2859         }
2860         else
2861         {
2862             check(TOK.leftParenthesis);
2863             check(TOK.rightParenthesis);
2864             check(TOK.semicolon);
2865             return new AST.NewDeclaration(loc, stc);
2866         }
2867     }
2868 
2869     /**********************************************
2870      * Parse parameter list.
2871      */
2872     private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2873     {
2874         auto parameters = new AST.Parameters();
2875         VarArg varargs = VarArg.none;
2876         int hasdefault = 0;
2877         StorageClass varargsStc;
2878 
2879         // Attributes allowed for ...
2880         enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_;
2881 
2882         check(TOK.leftParenthesis);
2883         while (1)
2884         {
2885             Identifier ai = null;
2886             AST.Type at;
2887             StorageClass storageClass = 0;
2888             StorageClass stc;
2889             AST.Expression ae;
2890             AST.Expressions* udas = null;
2891             for (; 1; nextToken())
2892             {
2893             L3:
2894                 switch (token.value)
2895                 {
2896                 case TOK.rightParenthesis:
2897                     if (storageClass != 0 || udas !is null)
2898                         error("basic type expected, not `)`");
2899                     break;
2900 
2901                 case TOK.dotDotDot:
2902                     varargs = VarArg.variadic;
2903                     varargsStc = storageClass;
2904                     if (varargsStc & ~VarArgsStc)
2905                     {
2906                         OutBuffer buf;
2907                         AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc);
2908                         error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2909                         varargsStc &= VarArgsStc;
2910                     }
2911                     nextToken();
2912                     break;
2913 
2914                 case TOK.const_:
2915                     if (peekNext() == TOK.leftParenthesis)
2916                         goto default;
2917                     stc = STC.const_;
2918                     goto L2;
2919 
2920                 case TOK.immutable_:
2921                     if (peekNext() == TOK.leftParenthesis)
2922                         goto default;
2923                     stc = STC.immutable_;
2924                     goto L2;
2925 
2926                 case TOK.shared_:
2927                     if (peekNext() == TOK.leftParenthesis)
2928                         goto default;
2929                     stc = STC.shared_;
2930                     goto L2;
2931 
2932                 case TOK.inout_:
2933                     if (peekNext() == TOK.leftParenthesis)
2934                         goto default;
2935                     stc = STC.wild;
2936                     goto L2;
2937                 case TOK.at:
2938                     {
2939                         AST.Expressions* exps = null;
2940                         StorageClass stc2 = parseAttribute(exps);
2941                         if (stc2 & atAttrGroup)
2942                         {
2943                             error("`@%s` attribute for function parameter is not supported", token.toChars());
2944                         }
2945                         else
2946                         {
2947                             udas = AST.UserAttributeDeclaration.concat(udas, exps);
2948                         }
2949                         if (token.value == TOK.dotDotDot)
2950                             error("variadic parameter cannot have user-defined attributes");
2951                         if (stc2)
2952                             nextToken();
2953                         goto L3;
2954                         // Don't call nextToken again.
2955                     }
2956                 case TOK.in_:
2957                     stc = STC.in_;
2958                     goto L2;
2959 
2960                 case TOK.out_:
2961                     stc = STC.out_;
2962                     goto L2;
2963 
2964                 case TOK.ref_:
2965                     stc = STC.ref_;
2966                     goto L2;
2967 
2968                 case TOK.lazy_:
2969                     stc = STC.lazy_;
2970                     goto L2;
2971 
2972                 case TOK.scope_:
2973                     stc = STC.scope_;
2974                     goto L2;
2975 
2976                 case TOK.final_:
2977                     stc = STC.final_;
2978                     goto L2;
2979 
2980                 case TOK.auto_:
2981                     stc = STC.auto_;
2982                     goto L2;
2983 
2984                 case TOK.return_:
2985                     stc = STC.return_;
2986                     goto L2;
2987                 L2:
2988                     storageClass = appendStorageClass(storageClass, stc);
2989                     continue;
2990 
2991                     version (none)
2992                     {
2993                     case TOK.static_:
2994                         stc = STC.static_;
2995                         goto L2;
2996 
2997                     case TOK.auto_:
2998                         storageClass = STC.auto_;
2999                         goto L4;
3000 
3001                     case TOK.alias_:
3002                         storageClass = STC.alias_;
3003                         goto L4;
3004                     L4:
3005                         nextToken();
3006                         ai = null;
3007                         if (token.value == TOK.identifier)
3008                         {
3009                             ai = token.ident;
3010                             nextToken();
3011                         }
3012 
3013                         at = null; // no type
3014                         ae = null; // no default argument
3015                         if (token.value == TOK.assign) // = defaultArg
3016                         {
3017                             nextToken();
3018                             ae = parseDefaultInitExp();
3019                             hasdefault = 1;
3020                         }
3021                         else
3022                         {
3023                             if (hasdefault)
3024                                 error("default argument expected for `alias %s`", ai ? ai.toChars() : "");
3025                         }
3026                         goto L3;
3027                     }
3028                 default:
3029                     {
3030                         stc = storageClass & (STC.IOR | STC.lazy_);
3031                         // if stc is not a power of 2
3032                         if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
3033                             error("incompatible parameter storage classes");
3034                         //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
3035                             //error("scope cannot be ref or out");
3036 
3037                         if (tpl && token.value == TOK.identifier)
3038                         {
3039                             const tv = peekNext();
3040                             if (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)
3041                             {
3042                                 Identifier id = Identifier.generateId("__T");
3043                                 const loc = token.loc;
3044                                 at = new AST.TypeIdentifier(loc, id);
3045                                 if (!*tpl)
3046                                     *tpl = new AST.TemplateParameters();
3047                                 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
3048                                 (*tpl).push(tp);
3049 
3050                                 ai = token.ident;
3051                                 nextToken();
3052                             }
3053                             else goto _else;
3054                         }
3055                         else
3056                         {
3057                         _else:
3058                             at = parseType(&ai);
3059                         }
3060                         ae = null;
3061                         if (token.value == TOK.assign) // = defaultArg
3062                         {
3063                             nextToken();
3064                             ae = parseDefaultInitExp();
3065                             hasdefault = 1;
3066                         }
3067                         else
3068                         {
3069                             if (hasdefault)
3070                                 error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars());
3071                         }
3072                         auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
3073                         if (udas)
3074                         {
3075                             auto a = new AST.Dsymbols();
3076                             auto udad = new AST.UserAttributeDeclaration(udas, a);
3077                             param.userAttribDecl = udad;
3078                         }
3079                         if (token.value == TOK.at)
3080                         {
3081                             AST.Expressions* exps = null;
3082                             StorageClass stc2 = parseAttribute(exps);
3083                             if (stc2 & atAttrGroup)
3084                             {
3085                                 error("`@%s` attribute for function parameter is not supported", token.toChars());
3086                             }
3087                             else
3088                             {
3089                                 error("user-defined attributes cannot appear as postfixes", token.toChars());
3090                             }
3091                             if (stc2)
3092                                 nextToken();
3093                         }
3094                         if (token.value == TOK.dotDotDot)
3095                         {
3096                             /* This is:
3097                              *      at ai ...
3098                              */
3099                             if (storageClass & (STC.out_ | STC.ref_))
3100                                 error("variadic argument cannot be `out` or `ref`");
3101                             varargs = VarArg.typesafe;
3102                             parameters.push(param);
3103                             nextToken();
3104                             break;
3105                         }
3106                         parameters.push(param);
3107                         if (token.value == TOK.comma)
3108                         {
3109                             nextToken();
3110                             goto L1;
3111                         }
3112                         break;
3113                     }
3114                 }
3115                 break;
3116             }
3117             break;
3118 
3119         L1:
3120         }
3121         check(TOK.rightParenthesis);
3122         return AST.ParameterList(parameters, varargs, varargsStc);
3123     }
3124 
3125     /*************************************
3126      */
3127     private AST.EnumDeclaration parseEnum()
3128     {
3129         AST.EnumDeclaration e;
3130         Identifier id;
3131         AST.Type memtype;
3132         auto loc = token.loc;
3133 
3134         // printf("Parser::parseEnum()\n");
3135         nextToken();
3136         id = null;
3137         if (token.value == TOK.identifier)
3138         {
3139             id = token.ident;
3140             nextToken();
3141         }
3142 
3143         memtype = null;
3144         if (token.value == TOK.colon)
3145         {
3146             nextToken();
3147             int alt = 0;
3148             const typeLoc = token.loc;
3149             memtype = parseBasicType();
3150             memtype = parseDeclarator(memtype, alt, null);
3151             checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3152         }
3153 
3154         e = new AST.EnumDeclaration(loc, id, memtype);
3155         if (token.value == TOK.semicolon && id)
3156             nextToken();
3157         else if (token.value == TOK.leftCurly)
3158         {
3159             bool isAnonymousEnum = !id;
3160             TOK prevTOK;
3161 
3162             //printf("enum definition\n");
3163             e.members = new AST.Dsymbols();
3164             nextToken();
3165             const(char)[] comment = token.blockComment;
3166             while (token.value != TOK.rightCurly)
3167             {
3168                 /* Can take the following forms...
3169                  *  1. ident
3170                  *  2. ident = value
3171                  *  3. type ident = value
3172                  *  ... prefixed by valid attributes
3173                  */
3174                 loc = token.loc;
3175 
3176                 AST.Type type = null;
3177                 Identifier ident = null;
3178 
3179                 AST.Expressions* udas;
3180                 StorageClass stc;
3181                 AST.Expression deprecationMessage;
3182                 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3183                 while(token.value != TOK.rightCurly
3184                     && token.value != TOK.comma
3185                     && token.value != TOK.assign)
3186                 {
3187                     switch(token.value)
3188                     {
3189                         case TOK.at:
3190                             if (StorageClass _stc = parseAttribute(udas))
3191                             {
3192                                 if (_stc == STC.disable)
3193                                     stc |= _stc;
3194                                 else
3195                                 {
3196                                     OutBuffer buf;
3197                                     AST.stcToBuffer(&buf, _stc);
3198                                     error(attributeErrorMessage, buf.peekChars());
3199                                 }
3200                                 prevTOK = token.value;
3201                                 nextToken();
3202                             }
3203                             break;
3204                         case TOK.deprecated_:
3205                             stc |= STC.deprecated_;
3206                             if (!parseDeprecatedAttribute(deprecationMessage))
3207                             {
3208                                 prevTOK = token.value;
3209                                 nextToken();
3210                             }
3211                             break;
3212                         case TOK.identifier:
3213                             const tv = peekNext();
3214                             if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3215                             {
3216                                 ident = token.ident;
3217                                 type = null;
3218                                 prevTOK = token.value;
3219                                 nextToken();
3220                             }
3221                             else
3222                             {
3223                                 goto default;
3224                             }
3225                             break;
3226                         default:
3227                             if (isAnonymousEnum)
3228                             {
3229                                 type = parseType(&ident, null);
3230                                 if (type == AST.Type.terror)
3231                                 {
3232                                     type = null;
3233                                     prevTOK = token.value;
3234                                     nextToken();
3235                                 }
3236                                 else
3237                                 {
3238                                     prevTOK = TOK.identifier;
3239                                 }
3240                             }
3241                             else
3242                             {
3243                                 error(attributeErrorMessage, token.toChars());
3244                                 prevTOK = token.value;
3245                                 nextToken();
3246                             }
3247                             break;
3248                     }
3249                     if (token.value == TOK.comma)
3250                     {
3251                         prevTOK = token.value;
3252                     }
3253                 }
3254 
3255                 if (type && type != AST.Type.terror)
3256                 {
3257                     if (!ident)
3258                         error("no identifier for declarator `%s`", type.toChars());
3259                     if (!isAnonymousEnum)
3260                         error("type only allowed if anonymous enum and no enum type");
3261                 }
3262                 AST.Expression value;
3263                 if (token.value == TOK.assign)
3264                 {
3265                     if (prevTOK == TOK.identifier)
3266                     {
3267                         nextToken();
3268                         value = parseAssignExp();
3269                     }
3270                     else
3271                     {
3272                         error("assignment must be preceded by an identifier");
3273                         nextToken();
3274                     }
3275                 }
3276                 else
3277                 {
3278                     value = null;
3279                     if (type && type != AST.Type.terror && isAnonymousEnum)
3280                         error("if type, there must be an initializer");
3281                 }
3282 
3283                 AST.DeprecatedDeclaration dd;
3284                 if (deprecationMessage)
3285                 {
3286                     dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3287                     stc |= STC.deprecated_;
3288                 }
3289 
3290                 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3291                 e.members.push(em);
3292 
3293                 if (udas)
3294                 {
3295                     auto s = new AST.Dsymbols();
3296                     s.push(em);
3297                     auto uad = new AST.UserAttributeDeclaration(udas, s);
3298                     em.userAttribDecl = uad;
3299                 }
3300 
3301                 if (token.value == TOK.rightCurly)
3302                 {
3303                 }
3304                 else
3305                 {
3306                     addComment(em, comment);
3307                     comment = null;
3308                     check(TOK.comma);
3309                 }
3310                 addComment(em, comment);
3311                 comment = token.blockComment;
3312 
3313                 if (token.value == TOK.endOfFile)
3314                 {
3315                     error("premature end of file");
3316                     break;
3317                 }
3318             }
3319             nextToken();
3320         }
3321         else
3322             error("enum declaration is invalid");
3323 
3324         //printf("-parseEnum() %s\n", e.toChars());
3325         return e;
3326     }
3327 
3328     /********************************
3329      * Parse struct, union, interface, class.
3330      */
3331     private AST.Dsymbol parseAggregate()
3332     {
3333         AST.TemplateParameters* tpl = null;
3334         AST.Expression constraint;
3335         const loc = token.loc;
3336         TOK tok = token.value;
3337 
3338         //printf("Parser::parseAggregate()\n");
3339         nextToken();
3340         Identifier id;
3341         if (token.value != TOK.identifier)
3342         {
3343             id = null;
3344         }
3345         else
3346         {
3347             id = token.ident;
3348             nextToken();
3349 
3350             if (token.value == TOK.leftParenthesis)
3351             {
3352                 // struct/class template declaration.
3353                 tpl = parseTemplateParameterList();
3354                 constraint = parseConstraint();
3355             }
3356         }
3357 
3358         // Collect base class(es)
3359         AST.BaseClasses* baseclasses = null;
3360         if (token.value == TOK.colon)
3361         {
3362             if (tok != TOK.interface_ && tok != TOK.class_)
3363                 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3364             nextToken();
3365             baseclasses = parseBaseClasses();
3366         }
3367 
3368         if (token.value == TOK.if_)
3369         {
3370             if (constraint)
3371                 error("template constraints appear both before and after BaseClassList, put them before");
3372             constraint = parseConstraint();
3373         }
3374         if (constraint)
3375         {
3376             if (!id)
3377                 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3378             if (!tpl)
3379                 error("template constraints only allowed for templates");
3380         }
3381 
3382         AST.Dsymbols* members = null;
3383         if (token.value == TOK.leftCurly)
3384         {
3385             //printf("aggregate definition\n");
3386             const lookingForElseSave = lookingForElse;
3387             lookingForElse = Loc();
3388             nextToken();
3389             members = parseDeclDefs(0);
3390             lookingForElse = lookingForElseSave;
3391             if (token.value != TOK.rightCurly)
3392             {
3393                 /* { */
3394                 error("`}` expected following members in `%s` declaration at %s",
3395                     Token.toChars(tok), loc.toChars());
3396             }
3397             nextToken();
3398         }
3399         else if (token.value == TOK.semicolon && id)
3400         {
3401             if (baseclasses || constraint)
3402                 error("members expected");
3403             nextToken();
3404         }
3405         else
3406         {
3407             error("{ } expected following `%s` declaration", Token.toChars(tok));
3408         }
3409 
3410         AST.AggregateDeclaration a;
3411         switch (tok)
3412         {
3413         case TOK.interface_:
3414             if (!id)
3415                 error(loc, "anonymous interfaces not allowed");
3416             a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3417             a.members = members;
3418             break;
3419 
3420         case TOK.class_:
3421             if (!id)
3422                 error(loc, "anonymous classes not allowed");
3423             bool inObject = md && !md.packages && md.id == Id.object;
3424             a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3425             break;
3426 
3427         case TOK.struct_:
3428             if (id)
3429             {
3430                 bool inObject = md && !md.packages && md.id == Id.object;
3431                 a = new AST.StructDeclaration(loc, id, inObject);
3432                 a.members = members;
3433             }
3434             else
3435             {
3436                 /* Anonymous structs/unions are more like attributes.
3437                  */
3438                 assert(!tpl);
3439                 return new AST.AnonDeclaration(loc, false, members);
3440             }
3441             break;
3442 
3443         case TOK.union_:
3444             if (id)
3445             {
3446                 a = new AST.UnionDeclaration(loc, id);
3447                 a.members = members;
3448             }
3449             else
3450             {
3451                 /* Anonymous structs/unions are more like attributes.
3452                  */
3453                 assert(!tpl);
3454                 return new AST.AnonDeclaration(loc, true, members);
3455             }
3456             break;
3457 
3458         default:
3459             assert(0);
3460         }
3461 
3462         if (tpl)
3463         {
3464             // Wrap a template around the aggregate declaration
3465             auto decldefs = new AST.Dsymbols();
3466             decldefs.push(a);
3467             auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3468             return tempdecl;
3469         }
3470         return a;
3471     }
3472 
3473     /*******************************************
3474      */
3475     private AST.BaseClasses* parseBaseClasses()
3476     {
3477         auto baseclasses = new AST.BaseClasses();
3478 
3479         for (; 1; nextToken())
3480         {
3481             auto b = new AST.BaseClass(parseBasicType());
3482             baseclasses.push(b);
3483             if (token.value != TOK.comma)
3484                 break;
3485         }
3486         return baseclasses;
3487     }
3488 
3489     private AST.Dsymbols* parseImport()
3490     {
3491         auto decldefs = new AST.Dsymbols();
3492         Identifier aliasid = null;
3493 
3494         int isstatic = token.value == TOK.static_;
3495         if (isstatic)
3496             nextToken();
3497 
3498         //printf("Parser::parseImport()\n");
3499         do
3500         {
3501         L1:
3502             nextToken();
3503             if (token.value != TOK.identifier)
3504             {
3505                 error("identifier expected following `import`");
3506                 break;
3507             }
3508 
3509             const loc = token.loc;
3510             Identifier id = token.ident;
3511             Identifier[] a;
3512             nextToken();
3513             if (!aliasid && token.value == TOK.assign)
3514             {
3515                 aliasid = id;
3516                 goto L1;
3517             }
3518             while (token.value == TOK.dot)
3519             {
3520                 a ~= id;
3521                 nextToken();
3522                 if (token.value != TOK.identifier)
3523                 {
3524                     error("identifier expected following `package`");
3525                     break;
3526                 }
3527                 id = token.ident;
3528                 nextToken();
3529             }
3530 
3531             auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3532             decldefs.push(s);
3533 
3534             /* Look for
3535              *      : alias=name, alias=name;
3536              * syntax.
3537              */
3538             if (token.value == TOK.colon)
3539             {
3540                 do
3541                 {
3542                     nextToken();
3543                     if (token.value != TOK.identifier)
3544                     {
3545                         error("identifier expected following `:`");
3546                         break;
3547                     }
3548                     Identifier _alias = token.ident;
3549                     Identifier name;
3550                     nextToken();
3551                     if (token.value == TOK.assign)
3552                     {
3553                         nextToken();
3554                         if (token.value != TOK.identifier)
3555                         {
3556                             error("identifier expected following `%s=`", _alias.toChars());
3557                             break;
3558                         }
3559                         name = token.ident;
3560                         nextToken();
3561                     }
3562                     else
3563                     {
3564                         name = _alias;
3565                         _alias = null;
3566                     }
3567                     s.addAlias(name, _alias);
3568                 }
3569                 while (token.value == TOK.comma);
3570                 break; // no comma-separated imports of this form
3571             }
3572             aliasid = null;
3573         }
3574         while (token.value == TOK.comma);
3575 
3576         if (token.value == TOK.semicolon)
3577             nextToken();
3578         else
3579         {
3580             error("`;` expected");
3581             nextToken();
3582         }
3583 
3584         return decldefs;
3585     }
3586 
3587     AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null)
3588     {
3589         /* Take care of the storage class prefixes that
3590          * serve as type attributes:
3591          *               const type
3592          *           immutable type
3593          *              shared type
3594          *               inout type
3595          *         inout const type
3596          *        shared const type
3597          *        shared inout type
3598          *  shared inout const type
3599          */
3600         StorageClass stc = 0;
3601         while (1)
3602         {
3603             switch (token.value)
3604             {
3605             case TOK.const_:
3606                 if (peekNext() == TOK.leftParenthesis)
3607                     break; // const as type constructor
3608                 stc |= STC.const_; // const as storage class
3609                 nextToken();
3610                 continue;
3611 
3612             case TOK.immutable_:
3613                 if (peekNext() == TOK.leftParenthesis)
3614                     break;
3615                 stc |= STC.immutable_;
3616                 nextToken();
3617                 continue;
3618 
3619             case TOK.shared_:
3620                 if (peekNext() == TOK.leftParenthesis)
3621                     break;
3622                 stc |= STC.shared_;
3623                 nextToken();
3624                 continue;
3625 
3626             case TOK.inout_:
3627                 if (peekNext() == TOK.leftParenthesis)
3628                     break;
3629                 stc |= STC.wild;
3630                 nextToken();
3631                 continue;
3632 
3633             default:
3634                 break;
3635             }
3636             break;
3637         }
3638 
3639         const typeLoc = token.loc;
3640 
3641         AST.Type t;
3642         t = parseBasicType();
3643 
3644         int alt = 0;
3645         t = parseDeclarator(t, alt, pident, ptpl);
3646         checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3647 
3648         t = t.addSTC(stc);
3649         return t;
3650     }
3651 
3652     private AST.Type parseBasicType(bool dontLookDotIdents = false)
3653     {
3654         AST.Type t;
3655         Loc loc;
3656         Identifier id;
3657         //printf("parseBasicType()\n");
3658         switch (token.value)
3659         {
3660         case TOK.void_:
3661             t = AST.Type.tvoid;
3662             goto LabelX;
3663 
3664         case TOK.int8:
3665             t = AST.Type.tint8;
3666             goto LabelX;
3667 
3668         case TOK.uns8:
3669             t = AST.Type.tuns8;
3670             goto LabelX;
3671 
3672         case TOK.int16:
3673             t = AST.Type.tint16;
3674             goto LabelX;
3675 
3676         case TOK.uns16:
3677             t = AST.Type.tuns16;
3678             goto LabelX;
3679 
3680         case TOK.int32:
3681             t = AST.Type.tint32;
3682             goto LabelX;
3683 
3684         case TOK.uns32:
3685             t = AST.Type.tuns32;
3686             goto LabelX;
3687 
3688         case TOK.int64:
3689             t = AST.Type.tint64;
3690             nextToken();
3691             if (token.value == TOK.int64)   // if `long long`
3692             {
3693                 error("use `long` for a 64 bit integer instead of `long long`");
3694                 nextToken();
3695             }
3696             else if (token.value == TOK.float64)   // if `long double`
3697             {
3698                 error("use `real` instead of `long double`");
3699                 t = AST.Type.tfloat80;
3700                 nextToken();
3701             }
3702             break;
3703 
3704         case TOK.uns64:
3705             t = AST.Type.tuns64;
3706             goto LabelX;
3707 
3708         case TOK.int128:
3709             t = AST.Type.tint128;
3710             goto LabelX;
3711 
3712         case TOK.uns128:
3713             t = AST.Type.tuns128;
3714             goto LabelX;
3715 
3716         case TOK.float32:
3717             t = AST.Type.tfloat32;
3718             goto LabelX;
3719 
3720         case TOK.float64:
3721             t = AST.Type.tfloat64;
3722             goto LabelX;
3723 
3724         case TOK.float80:
3725             t = AST.Type.tfloat80;
3726             goto LabelX;
3727 
3728         case TOK.imaginary32:
3729             t = AST.Type.timaginary32;
3730             goto LabelX;
3731 
3732         case TOK.imaginary64:
3733             t = AST.Type.timaginary64;
3734             goto LabelX;
3735 
3736         case TOK.imaginary80:
3737             t = AST.Type.timaginary80;
3738             goto LabelX;
3739 
3740         case TOK.complex32:
3741             t = AST.Type.tcomplex32;
3742             goto LabelX;
3743 
3744         case TOK.complex64:
3745             t = AST.Type.tcomplex64;
3746             goto LabelX;
3747 
3748         case TOK.complex80:
3749             t = AST.Type.tcomplex80;
3750             goto LabelX;
3751 
3752         case TOK.bool_:
3753             t = AST.Type.tbool;
3754             goto LabelX;
3755 
3756         case TOK.char_:
3757             t = AST.Type.tchar;
3758             goto LabelX;
3759 
3760         case TOK.wchar_:
3761             t = AST.Type.twchar;
3762             goto LabelX;
3763 
3764         case TOK.dchar_:
3765             t = AST.Type.tdchar;
3766             goto LabelX;
3767         LabelX:
3768             nextToken();
3769             break;
3770 
3771         case TOK.this_:
3772         case TOK.super_:
3773         case TOK.identifier:
3774             loc = token.loc;
3775             id = token.ident;
3776             nextToken();
3777             if (token.value == TOK.not)
3778             {
3779                 // ident!(template_arguments)
3780                 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3781                 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3782             }
3783             else
3784             {
3785                 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3786             }
3787             break;
3788 
3789         case TOK.mixin_:
3790             // https://dlang.org/spec/expression.html#mixin_types
3791             loc = token.loc;
3792             nextToken();
3793             if (token.value != TOK.leftParenthesis)
3794                 error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3795             auto exps = parseArguments();
3796             t = new AST.TypeMixin(loc, exps);
3797             break;
3798 
3799         case TOK.dot:
3800             // Leading . as in .foo
3801             t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3802             break;
3803 
3804         case TOK.typeof_:
3805             // typeof(expression)
3806             t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3807             break;
3808 
3809         case TOK.vector:
3810             t = parseVector();
3811             break;
3812 
3813         case TOK.traits:
3814             if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3815                 if (te.ident && te.args)
3816                 {
3817                     t = new AST.TypeTraits(token.loc, te);
3818                     break;
3819                 }
3820             t = new AST.TypeError;
3821             break;
3822 
3823         case TOK.const_:
3824             // const(type)
3825             nextToken();
3826             check(TOK.leftParenthesis);
3827             t = parseType().addSTC(STC.const_);
3828             check(TOK.rightParenthesis);
3829             break;
3830 
3831         case TOK.immutable_:
3832             // immutable(type)
3833             nextToken();
3834             check(TOK.leftParenthesis);
3835             t = parseType().addSTC(STC.immutable_);
3836             check(TOK.rightParenthesis);
3837             break;
3838 
3839         case TOK.shared_:
3840             // shared(type)
3841             nextToken();
3842             check(TOK.leftParenthesis);
3843             t = parseType().addSTC(STC.shared_);
3844             check(TOK.rightParenthesis);
3845             break;
3846 
3847         case TOK.inout_:
3848             // wild(type)
3849             nextToken();
3850             check(TOK.leftParenthesis);
3851             t = parseType().addSTC(STC.wild);
3852             check(TOK.rightParenthesis);
3853             break;
3854 
3855         default:
3856             error("basic type expected, not `%s`", token.toChars());
3857             if (token.value == TOK.else_)
3858                 errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3859             t = AST.Type.terror;
3860             break;
3861         }
3862         return t;
3863     }
3864 
3865     private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3866     {
3867         AST.Type maybeArray = null;
3868         // See https://issues.dlang.org/show_bug.cgi?id=1215
3869         // A basic type can look like MyType (typical case), but also:
3870         //  MyType.T -> A type
3871         //  MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3872         //  MyType[expr].T -> A type.
3873         //  MyType[expr].T[expr] ->  Either a static array of MyType[expr].T or a type
3874         //                           (iif MyType[expr].T is a Ttuple)
3875         while (1)
3876         {
3877             switch (token.value)
3878             {
3879             case TOK.dot:
3880                 {
3881                     nextToken();
3882                     if (token.value != TOK.identifier)
3883                     {
3884                         error("identifier expected following `.` instead of `%s`", token.toChars());
3885                         break;
3886                     }
3887                     if (maybeArray)
3888                     {
3889                         // This is actually a TypeTuple index, not an {a/s}array.
3890                         // We need to have a while loop to unwind all index taking:
3891                         // T[e1][e2].U   ->  T, addIndex(e1), addIndex(e2)
3892                         AST.Objects dimStack;
3893                         AST.Type t = maybeArray;
3894                         while (true)
3895                         {
3896                             if (t.ty == Tsarray)
3897                             {
3898                                 // The index expression is an Expression.
3899                                 AST.TypeSArray a = cast(AST.TypeSArray)t;
3900                                 dimStack.push(a.dim.syntaxCopy());
3901                                 t = a.next.syntaxCopy();
3902                             }
3903                             else if (t.ty == Taarray)
3904                             {
3905                                 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3906                                 AST.TypeAArray a = cast(AST.TypeAArray)t;
3907                                 dimStack.push(a.index.syntaxCopy());
3908                                 t = a.next.syntaxCopy();
3909                             }
3910                             else
3911                             {
3912                                 break;
3913                             }
3914                         }
3915                         assert(dimStack.dim > 0);
3916                         // We're good. Replay indices in the reverse order.
3917                         tid = cast(AST.TypeQualified)t;
3918                         while (dimStack.dim)
3919                         {
3920                             tid.addIndex(dimStack.pop());
3921                         }
3922                         maybeArray = null;
3923                     }
3924                     const loc = token.loc;
3925                     Identifier id = token.ident;
3926                     nextToken();
3927                     if (token.value == TOK.not)
3928                     {
3929                         auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3930                         tid.addInst(tempinst);
3931                     }
3932                     else
3933                         tid.addIdent(id);
3934                     continue;
3935                 }
3936             case TOK.leftBracket:
3937                 {
3938                     if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3939                         goto Lend;
3940 
3941                     nextToken();
3942                     AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3943                     if (token.value == TOK.rightBracket)
3944                     {
3945                         // It's a dynamic array, and we're done:
3946                         // T[].U does not make sense.
3947                         t = new AST.TypeDArray(t);
3948                         nextToken();
3949                         return t;
3950                     }
3951                     else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3952                     {
3953                         // This can be one of two things:
3954                         //  1 - an associative array declaration, T[type]
3955                         //  2 - an associative array declaration, T[expr]
3956                         // These  can only be disambiguated later.
3957                         AST.Type index = parseType(); // [ type ]
3958                         maybeArray = new AST.TypeAArray(t, index);
3959                         check(TOK.rightBracket);
3960                     }
3961                     else
3962                     {
3963                         // This can be one of three things:
3964                         //  1 - an static array declaration, T[expr]
3965                         //  2 - a slice, T[expr .. expr]
3966                         //  3 - a template parameter pack index expression, T[expr].U
3967                         // 1 and 3 can only be disambiguated later.
3968                         //printf("it's type[expression]\n");
3969                         inBrackets++;
3970                         AST.Expression e = parseAssignExp(); // [ expression ]
3971                         if (token.value == TOK.slice)
3972                         {
3973                             // It's a slice, and we're done.
3974                             nextToken();
3975                             AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3976                             t = new AST.TypeSlice(t, e, e2);
3977                             inBrackets--;
3978                             check(TOK.rightBracket);
3979                             return t;
3980                         }
3981                         else
3982                         {
3983                             maybeArray = new AST.TypeSArray(t, e);
3984                             inBrackets--;
3985                             check(TOK.rightBracket);
3986                             continue;
3987                         }
3988                     }
3989                     break;
3990                 }
3991             default:
3992                 goto Lend;
3993             }
3994         }
3995     Lend:
3996         return maybeArray ? maybeArray : cast(AST.Type)tid;
3997     }
3998 
3999     /******************************************
4000      * Parse suffixes to type t.
4001      *      *
4002      *      []
4003      *      [AssignExpression]
4004      *      [AssignExpression .. AssignExpression]
4005      *      [Type]
4006      *      delegate Parameters MemberFunctionAttributes(opt)
4007      *      function Parameters FunctionAttributes(opt)
4008      * Params:
4009      *      t = the already parsed type
4010      * Returns:
4011      *      t with the suffixes added
4012      * See_Also:
4013      *      https://dlang.org/spec/declaration.html#TypeSuffixes
4014      */
4015     private AST.Type parseTypeSuffixes(AST.Type t)
4016     {
4017         //printf("parseTypeSuffixes()\n");
4018         while (1)
4019         {
4020             switch (token.value)
4021             {
4022             case TOK.mul:
4023                 t = new AST.TypePointer(t);
4024                 nextToken();
4025                 continue;
4026 
4027             case TOK.leftBracket:
4028                 // Handle []. Make sure things like
4029                 //     int[3][1] a;
4030                 // is (array[1] of array[3] of int)
4031                 nextToken();
4032                 if (token.value == TOK.rightBracket)
4033                 {
4034                     t = new AST.TypeDArray(t); // []
4035                     nextToken();
4036                 }
4037                 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4038                 {
4039                     // It's an associative array declaration
4040                     //printf("it's an associative array\n");
4041                     AST.Type index = parseType(); // [ type ]
4042                     t = new AST.TypeAArray(t, index);
4043                     check(TOK.rightBracket);
4044                 }
4045                 else
4046                 {
4047                     //printf("it's type[expression]\n");
4048                     inBrackets++;
4049                     AST.Expression e = parseAssignExp(); // [ expression ]
4050                     if (!e)
4051                     {
4052                         inBrackets--;
4053                         check(TOK.rightBracket);
4054                         continue;
4055                     }
4056                     if (token.value == TOK.slice)
4057                     {
4058                         nextToken();
4059                         AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
4060                         t = new AST.TypeSlice(t, e, e2);
4061                     }
4062                     else
4063                     {
4064                         t = new AST.TypeSArray(t, e);
4065                     }
4066                     inBrackets--;
4067                     check(TOK.rightBracket);
4068                 }
4069                 continue;
4070 
4071             case TOK.delegate_:
4072             case TOK.function_:
4073                 {
4074                     // Handle delegate declaration:
4075                     //      t delegate(parameter list) nothrow pure
4076                     //      t function(parameter list) nothrow pure
4077                     TOK save = token.value;
4078                     nextToken();
4079 
4080                     auto parameterList = parseParameterList(null);
4081 
4082                     StorageClass stc = parsePostfix(STC.undefined_, null);
4083                     auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4084                     if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
4085                     {
4086                         if (save == TOK.function_)
4087                             error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
4088                         else
4089                             tf = cast(AST.TypeFunction)tf.addSTC(stc);
4090                     }
4091                     t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
4092                     continue;
4093                 }
4094             default:
4095                 return t;
4096             }
4097             assert(0);
4098         }
4099         assert(0);
4100     }
4101 
4102     /**********************
4103      * Parse Declarator
4104      * Params:
4105      *  t            = base type to start with
4106      *  palt         = OR in 1 for C-style function pointer declaration syntax,
4107      *                 2 for C-style array declaration syntax, otherwise don't modify
4108      *  pident       = set to Identifier if there is one, null if not
4109      *  tpl          = if !null, then set to TemplateParameterList
4110      *  storageClass = any storage classes seen so far
4111      *  pdisable     = set to true if @disable seen
4112      *  pudas        = any user defined attributes seen so far. Merged with any more found
4113      * Returns:
4114      *  type declared
4115      * Reference: https://dlang.org/spec/declaration.html#Declarator
4116      */
4117     private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
4118         AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
4119         bool* pdisable = null, AST.Expressions** pudas = null)
4120     {
4121         //printf("parseDeclarator(tpl = %p)\n", tpl);
4122         t = parseTypeSuffixes(t);
4123         AST.Type ts;
4124         switch (token.value)
4125         {
4126         case TOK.identifier:
4127             if (pident)
4128                 *pident = token.ident;
4129             else
4130                 error("unexpected identifier `%s` in declarator", token.ident.toChars());
4131             ts = t;
4132             nextToken();
4133             break;
4134 
4135         case TOK.leftParenthesis:
4136             {
4137                 // like: T (*fp)();
4138                 // like: T ((*fp))();
4139                 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
4140                 {
4141                     /* Parse things with parentheses around the identifier, like:
4142                      *  int (*ident[3])[]
4143                      * although the D style would be:
4144                      *  int[]*[3] ident
4145                      */
4146                     palt |= 1;
4147                     nextToken();
4148                     ts = parseDeclarator(t, palt, pident);
4149                     check(TOK.rightParenthesis);
4150                     break;
4151                 }
4152                 ts = t;
4153 
4154                 Token* peekt = &token;
4155                 /* Completely disallow C-style things like:
4156                  *   T (a);
4157                  * Improve error messages for the common bug of a missing return type
4158                  * by looking to see if (a) looks like a parameter list.
4159                  */
4160                 if (isParameters(&peekt))
4161                 {
4162                     error("function declaration without return type. (Note that constructors are always named `this`)");
4163                 }
4164                 else
4165                     error("unexpected `(` in declarator");
4166                 break;
4167             }
4168         default:
4169             ts = t;
4170             break;
4171         }
4172 
4173         // parse DeclaratorSuffixes
4174         while (1)
4175         {
4176             switch (token.value)
4177             {
4178                 static if (CARRAYDECL)
4179                 {
4180                     /* Support C style array syntax:
4181                      *   int ident[]
4182                      * as opposed to D-style:
4183                      *   int[] ident
4184                      */
4185                 case TOK.leftBracket:
4186                     {
4187                         // This is the old C-style post [] syntax.
4188                         AST.TypeNext ta;
4189                         nextToken();
4190                         if (token.value == TOK.rightBracket)
4191                         {
4192                             // It's a dynamic array
4193                             ta = new AST.TypeDArray(t); // []
4194                             nextToken();
4195                             palt |= 2;
4196                         }
4197                         else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4198                         {
4199                             // It's an associative array
4200                             //printf("it's an associative array\n");
4201                             AST.Type index = parseType(); // [ type ]
4202                             check(TOK.rightBracket);
4203                             ta = new AST.TypeAArray(t, index);
4204                             palt |= 2;
4205                         }
4206                         else
4207                         {
4208                             //printf("It's a static array\n");
4209                             AST.Expression e = parseAssignExp(); // [ expression ]
4210                             ta = new AST.TypeSArray(t, e);
4211                             check(TOK.rightBracket);
4212                             palt |= 2;
4213                         }
4214 
4215                         /* Insert ta into
4216                          *   ts -> ... -> t
4217                          * so that
4218                          *   ts -> ... -> ta -> t
4219                          */
4220                         AST.Type* pt;
4221                         for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4222                         {
4223                         }
4224                         *pt = ta;
4225                         continue;
4226                     }
4227                 }
4228             case TOK.leftParenthesis:
4229                 {
4230                     if (tpl)
4231                     {
4232                         Token* tk = peekPastParen(&token);
4233                         if (tk.value == TOK.leftParenthesis)
4234                         {
4235                             /* Look ahead to see if this is (...)(...),
4236                              * i.e. a function template declaration
4237                              */
4238                             //printf("function template declaration\n");
4239 
4240                             // Gather template parameter list
4241                             *tpl = parseTemplateParameterList();
4242                         }
4243                         else if (tk.value == TOK.assign)
4244                         {
4245                             /* or (...) =,
4246                              * i.e. a variable template declaration
4247                              */
4248                             //printf("variable template declaration\n");
4249                             *tpl = parseTemplateParameterList();
4250                             break;
4251                         }
4252                     }
4253 
4254                     auto parameterList = parseParameterList(null);
4255 
4256                     /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4257                      */
4258                     // merge prefix storage classes
4259                     StorageClass stc = parsePostfix(storageClass, pudas);
4260 
4261                     AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4262                     tf = tf.addSTC(stc);
4263                     if (pdisable)
4264                         *pdisable = stc & STC.disable ? true : false;
4265 
4266                     /* Insert tf into
4267                      *   ts -> ... -> t
4268                      * so that
4269                      *   ts -> ... -> tf -> t
4270                      */
4271                     AST.Type* pt;
4272                     for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4273                     {
4274                     }
4275                     *pt = tf;
4276                     break;
4277                 }
4278             default:
4279                 break;
4280             }
4281             break;
4282         }
4283         return ts;
4284     }
4285 
4286     private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4287         ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4288         out Loc linkloc)
4289     {
4290         StorageClass stc;
4291         bool sawLinkage = false; // seen a linkage declaration
4292 
4293         linkloc = Loc.initial;
4294 
4295         while (1)
4296         {
4297             switch (token.value)
4298             {
4299             case TOK.const_:
4300                 if (peekNext() == TOK.leftParenthesis)
4301                     break; // const as type constructor
4302                 stc = STC.const_; // const as storage class
4303                 goto L1;
4304 
4305             case TOK.immutable_:
4306                 if (peekNext() == TOK.leftParenthesis)
4307                     break;
4308                 stc = STC.immutable_;
4309                 goto L1;
4310 
4311             case TOK.shared_:
4312                 if (peekNext() == TOK.leftParenthesis)
4313                     break;
4314                 stc = STC.shared_;
4315                 goto L1;
4316 
4317             case TOK.inout_:
4318                 if (peekNext() == TOK.leftParenthesis)
4319                     break;
4320                 stc = STC.wild;
4321                 goto L1;
4322 
4323             case TOK.static_:
4324                 stc = STC.static_;
4325                 goto L1;
4326 
4327             case TOK.final_:
4328                 stc = STC.final_;
4329                 goto L1;
4330 
4331             case TOK.auto_:
4332                 stc = STC.auto_;
4333                 goto L1;
4334 
4335             case TOK.scope_:
4336                 stc = STC.scope_;
4337                 goto L1;
4338 
4339             case TOK.override_:
4340                 stc = STC.override_;
4341                 goto L1;
4342 
4343             case TOK.abstract_:
4344                 stc = STC.abstract_;
4345                 goto L1;
4346 
4347             case TOK.synchronized_:
4348                 stc = STC.synchronized_;
4349                 goto L1;
4350 
4351             case TOK.deprecated_:
4352                 stc = STC.deprecated_;
4353                 goto L1;
4354 
4355             case TOK.nothrow_:
4356                 stc = STC.nothrow_;
4357                 goto L1;
4358 
4359             case TOK.pure_:
4360                 stc = STC.pure_;
4361                 goto L1;
4362 
4363             case TOK.ref_:
4364                 stc = STC.ref_;
4365                 goto L1;
4366 
4367             case TOK.gshared:
4368                 stc = STC.gshared;
4369                 goto L1;
4370 
4371             case TOK.enum_:
4372                 {
4373                     const tv = peekNext();
4374                     if (tv == TOK.leftCurly || tv == TOK.colon)
4375                         break;
4376                     if (tv == TOK.identifier)
4377                     {
4378                         const nextv = peekNext2();
4379                         if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4380                             break;
4381                     }
4382                     stc = STC.manifest;
4383                     goto L1;
4384                 }
4385 
4386             case TOK.at:
4387                 {
4388                     stc = parseAttribute(udas);
4389                     if (stc)
4390                         goto L1;
4391                     continue;
4392                 }
4393             L1:
4394                 storage_class = appendStorageClass(storage_class, stc);
4395                 nextToken();
4396                 continue;
4397 
4398             case TOK.extern_:
4399                 {
4400                     if (peekNext() != TOK.leftParenthesis)
4401                     {
4402                         stc = STC.extern_;
4403                         goto L1;
4404                     }
4405 
4406                     if (sawLinkage)
4407                         error("redundant linkage declaration");
4408                     sawLinkage = true;
4409                     linkloc = token.loc;
4410                     auto res = parseLinkage();
4411                     link = res.link;
4412                     if (res.idents || res.identExps)
4413                     {
4414                         error("C++ name spaces not allowed here");
4415                     }
4416                     if (res.cppmangle != CPPMANGLE.def)
4417                     {
4418                         error("C++ mangle declaration not allowed here");
4419                     }
4420                     continue;
4421                 }
4422             case TOK.align_:
4423                 {
4424                     nextToken();
4425                     setAlignment = true;
4426                     if (token.value == TOK.leftParenthesis)
4427                     {
4428                         nextToken();
4429                         ealign = parseExpression();
4430                         check(TOK.rightParenthesis);
4431                     }
4432                     continue;
4433                 }
4434             default:
4435                 break;
4436             }
4437             break;
4438         }
4439     }
4440 
4441     /**********************************
4442      * Parse Declarations.
4443      * These can be:
4444      *      1. declarations at global/class level
4445      *      2. declarations at statement level
4446      * Return array of Declaration *'s.
4447      */
4448     private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4449     {
4450         StorageClass storage_class = STC.undefined_;
4451         TOK tok = TOK.reserved;
4452         LINK link = linkage;
4453         Loc linkloc = this.linkLoc;
4454         bool setAlignment = false;
4455         AST.Expression ealign;
4456         AST.Expressions* udas = null;
4457 
4458         //printf("parseDeclarations() %s\n", token.toChars());
4459         if (!comment)
4460             comment = token.blockComment.ptr;
4461 
4462         /* Look for AliasAssignment:
4463          *   identifier = type;
4464          */
4465         if (token.value == TOK.identifier && peekNext() == TOK.assign)
4466         {
4467             const loc = token.loc;
4468             auto ident = token.ident;
4469             nextToken();
4470             nextToken();        // advance past =
4471             auto t = parseType();
4472             AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4473             check(TOK.semicolon);
4474             addComment(s, comment);
4475             auto a = new AST.Dsymbols();
4476             a.push(s);
4477             return a;
4478         }
4479 
4480         if (token.value == TOK.alias_)
4481         {
4482             const loc = token.loc;
4483             tok = token.value;
4484             nextToken();
4485 
4486             /* Look for:
4487              *   alias identifier this;
4488              */
4489             if (token.value == TOK.identifier && peekNext() == TOK.this_)
4490             {
4491                 auto s = new AST.AliasThis(loc, token.ident);
4492                 nextToken();
4493                 check(TOK.this_);
4494                 check(TOK.semicolon);
4495                 auto a = new AST.Dsymbols();
4496                 a.push(s);
4497                 addComment(s, comment);
4498                 return a;
4499             }
4500             version (none)
4501             {
4502                 /* Look for:
4503                  *  alias this = identifier;
4504                  */
4505                 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4506                 {
4507                     check(TOK.this_);
4508                     check(TOK.assign);
4509                     auto s = new AliasThis(loc, token.ident);
4510                     nextToken();
4511                     check(TOK.semicolon);
4512                     auto a = new Dsymbols();
4513                     a.push(s);
4514                     addComment(s, comment);
4515                     return a;
4516                 }
4517             }
4518             /* Look for:
4519              *  alias identifier = type;
4520              *  alias identifier(...) = type;
4521              */
4522             if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4523             {
4524                 auto a = new AST.Dsymbols();
4525                 while (1)
4526                 {
4527                     auto ident = token.ident;
4528                     nextToken();
4529                     AST.TemplateParameters* tpl = null;
4530                     if (token.value == TOK.leftParenthesis)
4531                         tpl = parseTemplateParameterList();
4532                     check(TOK.assign);
4533 
4534                     bool hasParsedAttributes;
4535                     void parseAttributes()
4536                     {
4537                         if (hasParsedAttributes) // only parse once
4538                             return;
4539                         hasParsedAttributes = true;
4540                         udas = null;
4541                         storage_class = STC.undefined_;
4542                         link = linkage;
4543                         linkloc = this.linkLoc;
4544                         setAlignment = false;
4545                         ealign = null;
4546                         parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4547                     }
4548 
4549                     if (token.value == TOK.at)
4550                         parseAttributes;
4551 
4552                     AST.Declaration v;
4553                     AST.Dsymbol s;
4554 
4555                     // try to parse function type:
4556                     // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
4557                     bool attributesAppended;
4558                     const StorageClass funcStc = parseTypeCtor();
4559                     Token* tlu = &token;
4560                     Token* tk;
4561                     if (token.value != TOK.function_ &&
4562                         token.value != TOK.delegate_ &&
4563                         isBasicType(&tlu) && tlu &&
4564                         tlu.value == TOK.leftParenthesis)
4565                     {
4566                         AST.Type tret = parseBasicType();
4567                         auto parameterList = parseParameterList(null);
4568 
4569                         parseAttributes();
4570                         if (udas)
4571                             error("user-defined attributes not allowed for `alias` declarations");
4572 
4573                         attributesAppended = true;
4574                         storage_class = appendStorageClass(storage_class, funcStc);
4575                         AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
4576                         v = new AST.AliasDeclaration(loc, ident, tf);
4577                     }
4578                     else if (token.value == TOK.function_ ||
4579                         token.value == TOK.delegate_ ||
4580                         token.value == TOK.leftParenthesis &&
4581                             skipAttributes(peekPastParen(&token), &tk) &&
4582                             (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4583                         token.value == TOK.leftCurly ||
4584                         token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4585                         token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4586                             skipAttributes(peekPastParen(peek(&token)), &tk) &&
4587                             (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
4588                        )
4589                     {
4590                         // function (parameters) { statements... }
4591                         // delegate (parameters) { statements... }
4592                         // (parameters) { statements... }
4593                         // (parameters) => expression
4594                         // { statements... }
4595                         // identifier => expression
4596                         // ref (parameters) { statements... }
4597                         // ref (parameters) => expression
4598 
4599                         s = parseFunctionLiteral();
4600 
4601                         if (udas !is null)
4602                         {
4603                             if (storage_class != 0)
4604                                 error("Cannot put a storage-class in an alias declaration.");
4605                             // parseAttributes shouldn't have set these variables
4606                             assert(link == linkage && !setAlignment && ealign is null);
4607                             auto tpl_ = cast(AST.TemplateDeclaration) s;
4608                             assert(tpl_ !is null && tpl_.members.dim == 1);
4609                             auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4610                             auto tf = cast(AST.TypeFunction) fd.type;
4611                             assert(tf.parameterList.parameters.dim > 0);
4612                             auto as = new AST.Dsymbols();
4613                             (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4614                         }
4615 
4616                         v = new AST.AliasDeclaration(loc, ident, s);
4617                     }
4618                     else
4619                     {
4620                         parseAttributes();
4621                         // type
4622                         if (udas)
4623                             error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4624 
4625                         auto t = parseType();
4626 
4627                         // Disallow meaningless storage classes on type aliases
4628                         if (storage_class)
4629                         {
4630                             // Don't raise errors for STC that are part of a function/delegate type, e.g.
4631                             // `alias F = ref pure nothrow @nogc @safe int function();`
4632                             auto tp = t.isTypePointer;
4633                             const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4634                             const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4635 
4636                             if (remStc)
4637                             {
4638                                 OutBuffer buf;
4639                                 AST.stcToBuffer(&buf, remStc);
4640                                 // @@@DEPRECATED_2.093@@@
4641                                 // Deprecated in 2020-07, can be made an error in 2.103
4642                                 deprecation("storage class `%s` has no effect in type aliases", buf.peekChars());
4643                             }
4644                         }
4645 
4646                         v = new AST.AliasDeclaration(loc, ident, t);
4647                     }
4648                     if (!attributesAppended)
4649                         storage_class = appendStorageClass(storage_class, funcStc);
4650                     v.storage_class = storage_class;
4651 
4652                     s = v;
4653                     if (tpl)
4654                     {
4655                         auto a2 = new AST.Dsymbols();
4656                         a2.push(s);
4657                         auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
4658                         s = tempdecl;
4659                     }
4660                     if (link != linkage)
4661                     {
4662                         auto a2 = new AST.Dsymbols();
4663                         a2.push(s);
4664                         s = new AST.LinkDeclaration(linkloc, link, a2);
4665                     }
4666                     a.push(s);
4667 
4668                     switch (token.value)
4669                     {
4670                     case TOK.semicolon:
4671                         nextToken();
4672                         addComment(s, comment);
4673                         break;
4674 
4675                     case TOK.comma:
4676                         nextToken();
4677                         addComment(s, comment);
4678                         if (token.value != TOK.identifier)
4679                         {
4680                             error("identifier expected following comma, not `%s`", token.toChars());
4681                             break;
4682                         }
4683                         if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
4684                         {
4685                             error("`=` expected following identifier");
4686                             nextToken();
4687                             break;
4688                         }
4689                         continue;
4690 
4691                     default:
4692                         error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4693                         break;
4694                     }
4695                     break;
4696                 }
4697                 return a;
4698             }
4699 
4700             // alias StorageClasses type ident;
4701         }
4702 
4703         AST.Type ts;
4704 
4705         if (!autodecl)
4706         {
4707             parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4708 
4709             if (token.value == TOK.enum_)
4710             {
4711                 AST.Dsymbol d = parseEnum();
4712                 auto a = new AST.Dsymbols();
4713                 a.push(d);
4714 
4715                 if (udas)
4716                 {
4717                     d = new AST.UserAttributeDeclaration(udas, a);
4718                     a = new AST.Dsymbols();
4719                     a.push(d);
4720                 }
4721 
4722                 addComment(d, comment);
4723                 return a;
4724             }
4725             if (token.value == TOK.struct_ ||
4726                      token.value == TOK.union_ ||
4727                      token.value == TOK.class_ ||
4728                      token.value == TOK.interface_)
4729             {
4730                 AST.Dsymbol s = parseAggregate();
4731                 auto a = new AST.Dsymbols();
4732                 a.push(s);
4733 
4734                 if (storage_class)
4735                 {
4736                     s = new AST.StorageClassDeclaration(storage_class, a);
4737                     a = new AST.Dsymbols();
4738                     a.push(s);
4739                 }
4740                 if (setAlignment)
4741                 {
4742                     s = new AST.AlignDeclaration(s.loc, ealign, a);
4743                     a = new AST.Dsymbols();
4744                     a.push(s);
4745                 }
4746                 if (link != linkage)
4747                 {
4748                     s = new AST.LinkDeclaration(linkloc, link, a);
4749                     a = new AST.Dsymbols();
4750                     a.push(s);
4751                 }
4752                 if (udas)
4753                 {
4754                     s = new AST.UserAttributeDeclaration(udas, a);
4755                     a = new AST.Dsymbols();
4756                     a.push(s);
4757                 }
4758 
4759                 addComment(s, comment);
4760                 return a;
4761             }
4762 
4763             /* Look for auto initializers:
4764              *  storage_class identifier = initializer;
4765              *  storage_class identifier(...) = initializer;
4766              */
4767             if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4768             {
4769                 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4770                 if (udas)
4771                 {
4772                     AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4773                     a = new AST.Dsymbols();
4774                     a.push(s);
4775                 }
4776                 return a;
4777             }
4778 
4779             /* Look for return type inference for template functions.
4780              */
4781             {
4782                 Token* tk;
4783                 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4784                     skipAttributes(tk, &tk) &&
4785                     (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4786                      tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4787                 {
4788                     // @@@DEPRECATED@@@
4789                     // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
4790                     // Deprecated in 2.097 - Can be removed from 2.117
4791                     // The deprecation period is longer than usual as `body`
4792                     // was quite widely used.
4793                     if (tk.value == TOK.identifier && tk.ident == Id._body)
4794                         deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
4795 
4796                     ts = null;
4797                 }
4798                 else
4799                 {
4800                     ts = parseBasicType();
4801                     ts = parseTypeSuffixes(ts);
4802                 }
4803             }
4804         }
4805 
4806         if (pAttrs)
4807         {
4808             storage_class |= pAttrs.storageClass;
4809             //pAttrs.storageClass = STC.undefined_;
4810         }
4811 
4812         AST.Type tfirst = null;
4813         auto a = new AST.Dsymbols();
4814 
4815         while (1)
4816         {
4817             AST.TemplateParameters* tpl = null;
4818             bool disable;
4819             int alt = 0;
4820 
4821             const loc = token.loc;
4822             Identifier ident;
4823 
4824             auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4825             assert(t);
4826             if (!tfirst)
4827                 tfirst = t;
4828             else if (t != tfirst)
4829                 error("multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4830 
4831             bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4832             if (ident)
4833                 checkCstyleTypeSyntax(loc, t, alt, ident);
4834             else if (!isThis && (t != AST.Type.terror))
4835                 error("no identifier for declarator `%s`", t.toChars());
4836 
4837             if (tok == TOK.alias_)
4838             {
4839                 AST.Declaration v;
4840                 AST.Initializer _init = null;
4841 
4842                 /* Aliases can no longer have multiple declarators, storage classes,
4843                  * linkages, or auto declarations.
4844                  * These never made any sense, anyway.
4845                  * The code below needs to be fixed to reject them.
4846                  * The grammar has already been fixed to preclude them.
4847                  */
4848 
4849                 if (udas)
4850                     error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4851 
4852                 if (token.value == TOK.assign)
4853                 {
4854                     nextToken();
4855                     _init = parseInitializer();
4856                 }
4857                 if (_init)
4858                 {
4859                     if (isThis)
4860                         error("cannot use syntax `alias this = %s`, use `alias %s this` instead", _init.toChars(), _init.toChars());
4861                     else
4862                         error("alias cannot have initializer");
4863                 }
4864                 v = new AST.AliasDeclaration(loc, ident, t);
4865 
4866                 v.storage_class = storage_class;
4867                 if (pAttrs)
4868                 {
4869                     /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4870                      * on prefix and postfix.
4871                      *   @safe alias void function() FP1;
4872                      *   alias @safe void function() FP2;    // FP2 is not @safe
4873                      *   alias void function() @safe FP3;
4874                      */
4875                     pAttrs.storageClass &= STC.safeGroup;
4876                 }
4877                 AST.Dsymbol s = v;
4878 
4879                 if (link != linkage)
4880                 {
4881                     auto ax = new AST.Dsymbols();
4882                     ax.push(v);
4883                     s = new AST.LinkDeclaration(linkloc, link, ax);
4884                 }
4885                 a.push(s);
4886                 switch (token.value)
4887                 {
4888                 case TOK.semicolon:
4889                     nextToken();
4890                     addComment(s, comment);
4891                     break;
4892 
4893                 case TOK.comma:
4894                     nextToken();
4895                     addComment(s, comment);
4896                     continue;
4897 
4898                 default:
4899                     error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4900                     break;
4901                 }
4902             }
4903             else if (t.ty == Tfunction)
4904             {
4905                 AST.Expression constraint = null;
4906                 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4907                 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4908                 if (pAttrs)
4909                     pAttrs.storageClass = STC.undefined_;
4910                 if (tpl)
4911                     constraint = parseConstraint();
4912                 AST.Dsymbol s = parseContracts(f);
4913                 auto tplIdent = s.ident;
4914 
4915                 if (link != linkage)
4916                 {
4917                     auto ax = new AST.Dsymbols();
4918                     ax.push(s);
4919                     s = new AST.LinkDeclaration(linkloc, link, ax);
4920                 }
4921                 if (udas)
4922                 {
4923                     auto ax = new AST.Dsymbols();
4924                     ax.push(s);
4925                     s = new AST.UserAttributeDeclaration(udas, ax);
4926                 }
4927 
4928                 /* A template parameter list means it's a function template
4929                  */
4930                 if (tpl)
4931                 {
4932                     // Wrap a template around the function declaration
4933                     auto decldefs = new AST.Dsymbols();
4934                     decldefs.push(s);
4935                     auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4936                     s = tempdecl;
4937 
4938                     StorageClass stc2 = STC.undefined_;
4939                     if (storage_class & STC.static_)
4940                     {
4941                         assert(f.storage_class & STC.static_);
4942                         f.storage_class &= ~STC.static_;
4943                         stc2 |= STC.static_;
4944                     }
4945                     if (storage_class & STC.deprecated_)
4946                     {
4947                         assert(f.storage_class & STC.deprecated_);
4948                         f.storage_class &= ~STC.deprecated_;
4949                         stc2 |= STC.deprecated_;
4950                     }
4951                     if (stc2 != STC.undefined_)
4952                     {
4953                         auto ax = new AST.Dsymbols();
4954                         ax.push(s);
4955                         s = new AST.StorageClassDeclaration(stc2, ax);
4956                     }
4957                 }
4958                 a.push(s);
4959                 addComment(s, comment);
4960             }
4961             else if (ident)
4962             {
4963                 AST.Initializer _init = null;
4964                 if (token.value == TOK.assign)
4965                 {
4966                     nextToken();
4967                     _init = parseInitializer();
4968                 }
4969 
4970                 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4971                 v.storage_class = storage_class;
4972                 if (pAttrs)
4973                     pAttrs.storageClass = STC.undefined_;
4974 
4975                 AST.Dsymbol s = v;
4976 
4977                 if (tpl && _init)
4978                 {
4979                     auto a2 = new AST.Dsymbols();
4980                     a2.push(s);
4981                     auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4982                     s = tempdecl;
4983                 }
4984                 if (setAlignment)
4985                 {
4986                     auto ax = new AST.Dsymbols();
4987                     ax.push(s);
4988                     s = new AST.AlignDeclaration(v.loc, ealign, ax);
4989                 }
4990                 if (link != linkage)
4991                 {
4992                     auto ax = new AST.Dsymbols();
4993                     ax.push(s);
4994                     s = new AST.LinkDeclaration(linkloc, link, ax);
4995                 }
4996                 if (udas)
4997                 {
4998                     auto ax = new AST.Dsymbols();
4999                     ax.push(s);
5000                     s = new AST.UserAttributeDeclaration(udas, ax);
5001                 }
5002                 a.push(s);
5003                 switch (token.value)
5004                 {
5005                 case TOK.semicolon:
5006                     nextToken();
5007                     addComment(s, comment);
5008                     break;
5009 
5010                 case TOK.comma:
5011                     nextToken();
5012                     addComment(s, comment);
5013                     continue;
5014 
5015                 default:
5016                     error("semicolon expected, not `%s`", token.toChars());
5017                     break;
5018                 }
5019             }
5020             break;
5021         }
5022         return a;
5023     }
5024 
5025     private AST.Dsymbol parseFunctionLiteral()
5026     {
5027         const loc = token.loc;
5028         AST.TemplateParameters* tpl = null;
5029         AST.ParameterList parameterList;
5030         AST.Type tret = null;
5031         StorageClass stc = 0;
5032         TOK save = TOK.reserved;
5033 
5034         switch (token.value)
5035         {
5036         case TOK.function_:
5037         case TOK.delegate_:
5038             save = token.value;
5039             nextToken();
5040             if (token.value == TOK.ref_)
5041             {
5042                 // function ref (parameters) { statements... }
5043                 // delegate ref (parameters) { statements... }
5044                 stc = STC.ref_;
5045                 nextToken();
5046             }
5047             if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly)
5048             {
5049                 // function type (parameters) { statements... }
5050                 // delegate type (parameters) { statements... }
5051                 tret = parseBasicType();
5052                 tret = parseTypeSuffixes(tret); // function return type
5053             }
5054 
5055             if (token.value == TOK.leftParenthesis)
5056             {
5057                 // function (parameters) { statements... }
5058                 // delegate (parameters) { statements... }
5059             }
5060             else
5061             {
5062                 // function { statements... }
5063                 // delegate { statements... }
5064                 break;
5065             }
5066             goto case TOK.leftParenthesis;
5067 
5068         case TOK.ref_:
5069             {
5070                 // ref (parameters) => expression
5071                 // ref (parameters) { statements... }
5072                 stc = STC.ref_;
5073                 nextToken();
5074                 goto case TOK.leftParenthesis;
5075             }
5076         case TOK.leftParenthesis:
5077             {
5078                 // (parameters) => expression
5079                 // (parameters) { statements... }
5080                 parameterList = parseParameterList(&tpl);
5081                 stc = parsePostfix(stc, null);
5082                 if (StorageClass modStc = stc & STC.TYPECTOR)
5083                 {
5084                     if (save == TOK.function_)
5085                     {
5086                         OutBuffer buf;
5087                         AST.stcToBuffer(&buf, modStc);
5088                         error("function literal cannot be `%s`", buf.peekChars());
5089                     }
5090                     else
5091                         save = TOK.delegate_;
5092                 }
5093                 break;
5094             }
5095         case TOK.leftCurly:
5096             // { statements... }
5097             break;
5098 
5099         case TOK.identifier:
5100             {
5101                 // identifier => expression
5102                 parameterList.parameters = new AST.Parameters();
5103                 Identifier id = Identifier.generateId("__T");
5104                 AST.Type t = new AST.TypeIdentifier(loc, id);
5105                 parameterList.parameters.push(new AST.Parameter(STC.parameter, t, token.ident, null, null));
5106 
5107                 tpl = new AST.TemplateParameters();
5108                 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5109                 tpl.push(tp);
5110 
5111                 nextToken();
5112                 break;
5113             }
5114         default:
5115             assert(0);
5116         }
5117 
5118         auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5119         tf = cast(AST.TypeFunction)tf.addSTC(stc);
5120         auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null);
5121 
5122         if (token.value == TOK.goesTo)
5123         {
5124             check(TOK.goesTo);
5125             if (token.value == TOK.leftCurly)
5126             {
5127                 deprecation("Using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5128                 deprecationSupplemental(token.loc, "Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5129             }
5130             const returnloc = token.loc;
5131             AST.Expression ae = parseAssignExp();
5132             fd.fbody = new AST.ReturnStatement(returnloc, ae);
5133             fd.endloc = token.loc;
5134         }
5135         else
5136         {
5137             parseContracts(fd);
5138         }
5139 
5140         if (tpl)
5141         {
5142             // Wrap a template around function fd
5143             auto decldefs = new AST.Dsymbols();
5144             decldefs.push(fd);
5145             return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5146         }
5147         return fd;
5148     }
5149 
5150     /*****************************************
5151      * Parse contracts following function declaration.
5152      */
5153     private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f)
5154     {
5155         LINK linksave = linkage;
5156 
5157         bool literal = f.isFuncLiteralDeclaration() !is null;
5158 
5159         // The following is irrelevant, as it is overridden by sc.linkage in
5160         // TypeFunction::semantic
5161         linkage = LINK.d; // nested functions have D linkage
5162         bool requireDo = false;
5163     L1:
5164         switch (token.value)
5165         {
5166         case TOK.goesTo:
5167             if (requireDo)
5168                 error("missing `do { ... }` after `in` or `out`");
5169             if (!global.params.shortenedMethods)
5170                 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
5171             const returnloc = token.loc;
5172             nextToken();
5173             f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5174             f.endloc = token.loc;
5175             check(TOK.semicolon);
5176             break;
5177 
5178         case TOK.leftCurly:
5179             if (requireDo)
5180                 error("missing `do { ... }` after `in` or `out`");
5181             f.fbody = parseStatement(ParseStatementFlags.semi);
5182             f.endloc = endloc;
5183             break;
5184 
5185         case TOK.identifier:
5186             if (token.ident == Id._body)
5187             {
5188                 // @@@DEPRECATED@@@
5189                 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
5190                 // Deprecated in 2.097 - Can be removed from 2.117
5191                 // The deprecation period is longer than usual as `body`
5192                 // was quite widely used.
5193                 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
5194                 goto case TOK.do_;
5195             }
5196             goto default;
5197 
5198         case TOK.do_:
5199             nextToken();
5200             f.fbody = parseStatement(ParseStatementFlags.curly);
5201             f.endloc = endloc;
5202             break;
5203 
5204             version (none)
5205             {
5206                 // Do we want this for function declarations, so we can do:
5207                 // int x, y, foo(), z;
5208             case TOK.comma:
5209                 nextToken();
5210                 continue;
5211             }
5212 
5213         case TOK.in_:
5214             // in { statements... }
5215             // in (expression)
5216             auto loc = token.loc;
5217             nextToken();
5218             if (!f.frequires)
5219             {
5220                 f.frequires = new AST.Statements;
5221             }
5222             if (token.value == TOK.leftParenthesis)
5223             {
5224                 nextToken();
5225                 AST.Expression e = parseAssignExp(), msg = null;
5226                 if (token.value == TOK.comma)
5227                 {
5228                     nextToken();
5229                     if (token.value != TOK.rightParenthesis)
5230                     {
5231                         msg = parseAssignExp();
5232                         if (token.value == TOK.comma)
5233                             nextToken();
5234                     }
5235                 }
5236                 check(TOK.rightParenthesis);
5237                 e = new AST.AssertExp(loc, e, msg);
5238                 f.frequires.push(new AST.ExpStatement(loc, e));
5239                 requireDo = false;
5240             }
5241             else
5242             {
5243                 f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_));
5244                 requireDo = true;
5245             }
5246             goto L1;
5247 
5248         case TOK.out_:
5249             // out { statements... }
5250             // out (; expression)
5251             // out (identifier) { statements... }
5252             // out (identifier; expression)
5253             auto loc = token.loc;
5254             nextToken();
5255             if (!f.fensures)
5256             {
5257                 f.fensures = new AST.Ensures;
5258             }
5259             Identifier id = null;
5260             if (token.value != TOK.leftCurly)
5261             {
5262                 check(TOK.leftParenthesis);
5263                 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5264                     error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5265                 if (token.value != TOK.semicolon)
5266                 {
5267                     id = token.ident;
5268                     nextToken();
5269                 }
5270                 if (token.value == TOK.semicolon)
5271                 {
5272                     nextToken();
5273                     AST.Expression e = parseAssignExp(), msg = null;
5274                     if (token.value == TOK.comma)
5275                     {
5276                         nextToken();
5277                         if (token.value != TOK.rightParenthesis)
5278                         {
5279                             msg = parseAssignExp();
5280                             if (token.value == TOK.comma)
5281                                 nextToken();
5282                         }
5283                     }
5284                     check(TOK.rightParenthesis);
5285                     e = new AST.AssertExp(loc, e, msg);
5286                     f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5287                     requireDo = false;
5288                     goto L1;
5289                 }
5290                 check(TOK.rightParenthesis);
5291             }
5292             f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5293             requireDo = true;
5294             goto L1;
5295 
5296         case TOK.semicolon:
5297             if (!literal)
5298             {
5299                 // https://issues.dlang.org/show_bug.cgi?id=15799
5300                 // Semicolon becomes a part of function declaration
5301                 // only when 'do' is not required
5302                 if (!requireDo)
5303                     nextToken();
5304                 break;
5305             }
5306             goto default;
5307 
5308         default:
5309             if (literal)
5310             {
5311                 const(char)* sbody = requireDo ? "do " : "";
5312                 error("missing `%s{ ... }` for function literal", sbody);
5313             }
5314             else if (!requireDo) // allow contracts even with no body
5315             {
5316                 TOK t = token.value;
5317                 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5318                         t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5319                     error("'%s' cannot be placed after a template constraint", token.toChars);
5320                 else if (t == TOK.at)
5321                     error("attributes cannot be placed after a template constraint");
5322                 else if (t == TOK.if_)
5323                     error("cannot use function constraints for non-template functions. Use `static if` instead");
5324                 else
5325                     error("semicolon expected following function declaration");
5326             }
5327             break;
5328         }
5329         if (literal && !f.fbody)
5330         {
5331             // Set empty function body for error recovery
5332             f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5333         }
5334 
5335         linkage = linksave;
5336 
5337         return f;
5338     }
5339 
5340     /*****************************************
5341      */
5342     private void checkDanglingElse(Loc elseloc)
5343     {
5344         if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5345         {
5346             warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5347         }
5348     }
5349 
5350     /* *************************
5351      * Issue errors if C-style syntax
5352      * Params:
5353      *  alt = !=0 for C-style syntax
5354      */
5355     private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5356     {
5357         if (!alt)
5358             return;
5359 
5360         const(char)* sp = !ident ? "" : " ";
5361         const(char)* s = !ident ? "" : ident.toChars();
5362         error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5363     }
5364 
5365     /*****************************************
5366      * Parses `foreach` statements, `static foreach` statements and
5367      * `static foreach` declarations.
5368      * Params:
5369      *  Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5370      *  loc = location of foreach
5371      *  pLastDecl = non-null for StaticForeachDeclaration
5372      * Returns:
5373      *  the Foreach generated
5374      */
5375     private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5376     {
5377         static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5378         {
5379             nextToken();
5380         }
5381 
5382         TOK op = token.value;
5383 
5384         nextToken();
5385         check(TOK.leftParenthesis);
5386 
5387         auto parameters = new AST.Parameters();
5388         while (1)
5389         {
5390             Identifier ai = null;
5391             AST.Type at;
5392 
5393             StorageClass storageClass = 0;
5394             StorageClass stc = 0;
5395         Lagain:
5396             if (stc)
5397             {
5398                 storageClass = appendStorageClass(storageClass, stc);
5399                 nextToken();
5400             }
5401             switch (token.value)
5402             {
5403                 case TOK.ref_:
5404                     stc = STC.ref_;
5405                     goto Lagain;
5406 
5407                 case TOK.scope_:
5408                     stc = STC.scope_;
5409                     goto Lagain;
5410 
5411                 case TOK.enum_:
5412                     stc = STC.manifest;
5413                     goto Lagain;
5414 
5415                 case TOK.alias_:
5416                     storageClass = appendStorageClass(storageClass, STC.alias_);
5417                     nextToken();
5418                     break;
5419 
5420                 case TOK.const_:
5421                     if (peekNext() != TOK.leftParenthesis)
5422                     {
5423                         stc = STC.const_;
5424                         goto Lagain;
5425                     }
5426                     break;
5427 
5428                 case TOK.immutable_:
5429                     if (peekNext() != TOK.leftParenthesis)
5430                     {
5431                         stc = STC.immutable_;
5432                         goto Lagain;
5433                     }
5434                     break;
5435 
5436                 case TOK.shared_:
5437                     if (peekNext() != TOK.leftParenthesis)
5438                     {
5439                         stc = STC.shared_;
5440                         goto Lagain;
5441                     }
5442                     break;
5443 
5444                 case TOK.inout_:
5445                     if (peekNext() != TOK.leftParenthesis)
5446                     {
5447                         stc = STC.wild;
5448                         goto Lagain;
5449                     }
5450                     break;
5451 
5452                 default:
5453                     break;
5454             }
5455             if (token.value == TOK.identifier)
5456             {
5457                 const tv = peekNext();
5458                 if (tv == TOK.comma || tv == TOK.semicolon)
5459                 {
5460                     ai = token.ident;
5461                     at = null; // infer argument type
5462                     nextToken();
5463                     goto Larg;
5464                 }
5465             }
5466             at = parseType(&ai);
5467             if (!ai)
5468                 error("no identifier for declarator `%s`", at.toChars());
5469         Larg:
5470             auto p = new AST.Parameter(storageClass, at, ai, null, null);
5471             parameters.push(p);
5472             if (token.value == TOK.comma)
5473             {
5474                 nextToken();
5475                 continue;
5476             }
5477             break;
5478         }
5479         check(TOK.semicolon);
5480 
5481         AST.Expression aggr = parseExpression();
5482         if (token.value == TOK.slice && parameters.dim == 1)
5483         {
5484             AST.Parameter p = (*parameters)[0];
5485             nextToken();
5486             AST.Expression upr = parseExpression();
5487             check(TOK.rightParenthesis);
5488             Loc endloc;
5489             static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5490             {
5491                 AST.Statement _body = parseStatement(0, null, &endloc);
5492             }
5493             else
5494             {
5495                 AST.Statement _body = null;
5496             }
5497             auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5498             static if (is(Foreach == AST.Statement))
5499             {
5500                 return rangefe;
5501             }
5502             else static if(is(Foreach == AST.StaticForeachDeclaration))
5503             {
5504                 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5505             }
5506             else static if (is(Foreach == AST.StaticForeachStatement))
5507             {
5508                 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5509             }
5510         }
5511         else
5512         {
5513             check(TOK.rightParenthesis);
5514             Loc endloc;
5515             static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5516             {
5517                 AST.Statement _body = parseStatement(0, null, &endloc);
5518             }
5519             else
5520             {
5521                 AST.Statement _body = null;
5522             }
5523             auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5524             static if (is(Foreach == AST.Statement))
5525             {
5526                 return aggrfe;
5527             }
5528             else static if(is(Foreach == AST.StaticForeachDeclaration))
5529             {
5530                 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5531             }
5532             else static if (is(Foreach == AST.StaticForeachStatement))
5533             {
5534                 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5535             }
5536         }
5537 
5538     }
5539 
5540     /***
5541      * Parse an assignment condition for if or while statements.
5542      *
5543      * Returns:
5544      *      The variable that is declared inside the condition
5545      */
5546     AST.Parameter parseAssignCondition()
5547     {
5548         AST.Parameter param = null;
5549         StorageClass storageClass = 0;
5550         StorageClass stc = 0;
5551 LagainStc:
5552         if (stc)
5553         {
5554             storageClass = appendStorageClass(storageClass, stc);
5555             nextToken();
5556         }
5557         switch (token.value)
5558         {
5559         case TOK.ref_:
5560             stc = STC.ref_;
5561             goto LagainStc;
5562 
5563         case TOK.scope_:
5564             stc = STC.scope_;
5565             goto LagainStc;
5566 
5567         case TOK.auto_:
5568             stc = STC.auto_;
5569             goto LagainStc;
5570 
5571         case TOK.const_:
5572             if (peekNext() != TOK.leftParenthesis)
5573             {
5574                 stc = STC.const_;
5575                 goto LagainStc;
5576             }
5577             break;
5578 
5579         case TOK.immutable_:
5580             if (peekNext() != TOK.leftParenthesis)
5581             {
5582                 stc = STC.immutable_;
5583                 goto LagainStc;
5584             }
5585             break;
5586 
5587         case TOK.shared_:
5588             if (peekNext() != TOK.leftParenthesis)
5589             {
5590                 stc = STC.shared_;
5591                 goto LagainStc;
5592             }
5593             break;
5594 
5595         case TOK.inout_:
5596             if (peekNext() != TOK.leftParenthesis)
5597             {
5598                 stc = STC.wild;
5599                 goto LagainStc;
5600             }
5601             break;
5602 
5603         default:
5604             break;
5605         }
5606         auto n = peek(&token);
5607         if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5608         {
5609             Identifier ai = token.ident;
5610             AST.Type at = null; // infer parameter type
5611             nextToken();
5612             check(TOK.assign);
5613             param = new AST.Parameter(storageClass, at, ai, null, null);
5614         }
5615         else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5616         {
5617             Identifier ai;
5618             AST.Type at = parseType(&ai);
5619             check(TOK.assign);
5620             param = new AST.Parameter(storageClass, at, ai, null, null);
5621         }
5622         else if (storageClass != 0)
5623             error("found `%s` while expecting `=` or identifier", n.toChars());
5624 
5625         return param;
5626     }
5627 
5628     /*****************************************
5629      * Input:
5630      *      flags   PSxxxx
5631      * Output:
5632      *      pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5633      */
5634     AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5635     {
5636         AST.Statement s;
5637         AST.Condition cond;
5638         AST.Statement ifbody;
5639         AST.Statement elsebody;
5640         bool isfinal;
5641         const loc = token.loc;
5642 
5643         //printf("parseStatement()\n");
5644         if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5645             error("statement expected to be `{ }`, not `%s`", token.toChars());
5646 
5647         switch (token.value)
5648         {
5649         case TOK.identifier:
5650             {
5651                 /* A leading identifier can be a declaration, label, or expression.
5652                  * The easiest case to check first is label:
5653                  */
5654                 if (peekNext() == TOK.colonColon)
5655                 {
5656                     // skip ident::
5657                     nextToken();
5658                     nextToken();
5659                     error("use `.` for member lookup, not `::`");
5660                     break;
5661                 }
5662 
5663                 if (peekNext() == TOK.colon)
5664                 {
5665                     // It's a label
5666                     Identifier ident = token.ident;
5667                     nextToken();
5668                     nextToken();
5669                     if (token.value == TOK.rightCurly)
5670                         s = null;
5671                     else if (token.value == TOK.leftCurly)
5672                         s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5673                     else
5674                         s = parseStatement(ParseStatementFlags.semiOk);
5675                     s = new AST.LabelStatement(loc, ident, s);
5676                     break;
5677                 }
5678                 goto case TOK.dot;
5679             }
5680         case TOK.dot:
5681         case TOK.typeof_:
5682         case TOK.vector:
5683         case TOK.traits:
5684             /* https://issues.dlang.org/show_bug.cgi?id=15163
5685              * If tokens can be handled as
5686              * old C-style declaration or D expression, prefer the latter.
5687              */
5688             if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5689                 goto Ldeclaration;
5690             goto Lexp;
5691 
5692         case TOK.assert_:
5693         case TOK.this_:
5694         case TOK.super_:
5695         case TOK.int32Literal:
5696         case TOK.uns32Literal:
5697         case TOK.int64Literal:
5698         case TOK.uns64Literal:
5699         case TOK.int128Literal:
5700         case TOK.uns128Literal:
5701         case TOK.float32Literal:
5702         case TOK.float64Literal:
5703         case TOK.float80Literal:
5704         case TOK.imaginary32Literal:
5705         case TOK.imaginary64Literal:
5706         case TOK.imaginary80Literal:
5707         case TOK.charLiteral:
5708         case TOK.wcharLiteral:
5709         case TOK.dcharLiteral:
5710         case TOK.null_:
5711         case TOK.true_:
5712         case TOK.false_:
5713         case TOK.string_:
5714         case TOK.hexadecimalString:
5715         case TOK.leftParenthesis:
5716         case TOK.cast_:
5717         case TOK.mul:
5718         case TOK.min:
5719         case TOK.add:
5720         case TOK.tilde:
5721         case TOK.not:
5722         case TOK.plusPlus:
5723         case TOK.minusMinus:
5724         case TOK.new_:
5725         case TOK.delete_:
5726         case TOK.delegate_:
5727         case TOK.function_:
5728         case TOK.typeid_:
5729         case TOK.is_:
5730         case TOK.leftBracket:
5731         case TOK.file:
5732         case TOK.fileFullPath:
5733         case TOK.line:
5734         case TOK.moduleString:
5735         case TOK.functionString:
5736         case TOK.prettyFunction:
5737         Lexp:
5738             {
5739                 AST.Expression exp = parseExpression();
5740                 /* https://issues.dlang.org/show_bug.cgi?id=15103
5741                  * Improve declaration / initialization syntax error message
5742                  * Error: found 'foo' when expecting ';' following statement
5743                  * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5744                  */
5745                 if (token.value == TOK.identifier && exp.op == TOK.identifier)
5746                 {
5747                     error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
5748                     nextToken();
5749                 }
5750                 else
5751                     check(TOK.semicolon, "statement");
5752                 s = new AST.ExpStatement(loc, exp);
5753                 break;
5754             }
5755         case TOK.static_:
5756             {
5757                 // Look ahead to see if it's static assert() or static if()
5758                 const tv = peekNext();
5759                 if (tv == TOK.assert_)
5760                 {
5761                     s = new AST.StaticAssertStatement(parseStaticAssert());
5762                     break;
5763                 }
5764                 if (tv == TOK.if_)
5765                 {
5766                     cond = parseStaticIfCondition();
5767                     goto Lcondition;
5768                 }
5769                 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5770                 {
5771                     s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5772                     if (flags & ParseStatementFlags.scope_)
5773                         s = new AST.ScopeStatement(loc, s, token.loc);
5774                     break;
5775                 }
5776                 if (tv == TOK.import_)
5777                 {
5778                     AST.Dsymbols* imports = parseImport();
5779                     s = new AST.ImportStatement(loc, imports);
5780                     if (flags & ParseStatementFlags.scope_)
5781                         s = new AST.ScopeStatement(loc, s, token.loc);
5782                     break;
5783                 }
5784                 goto Ldeclaration;
5785             }
5786         case TOK.final_:
5787             if (peekNext() == TOK.switch_)
5788             {
5789                 nextToken();
5790                 isfinal = true;
5791                 goto Lswitch;
5792             }
5793             goto Ldeclaration;
5794 
5795         case TOK.wchar_:
5796         case TOK.dchar_:
5797         case TOK.bool_:
5798         case TOK.char_:
5799         case TOK.int8:
5800         case TOK.uns8:
5801         case TOK.int16:
5802         case TOK.uns16:
5803         case TOK.int32:
5804         case TOK.uns32:
5805         case TOK.int64:
5806         case TOK.uns64:
5807         case TOK.int128:
5808         case TOK.uns128:
5809         case TOK.float32:
5810         case TOK.float64:
5811         case TOK.float80:
5812         case TOK.imaginary32:
5813         case TOK.imaginary64:
5814         case TOK.imaginary80:
5815         case TOK.complex32:
5816         case TOK.complex64:
5817         case TOK.complex80:
5818         case TOK.void_:
5819             // bug 7773: int.max is always a part of expression
5820             if (peekNext() == TOK.dot)
5821                 goto Lexp;
5822             if (peekNext() == TOK.leftParenthesis)
5823                 goto Lexp;
5824             goto case;
5825 
5826         case TOK.alias_:
5827         case TOK.const_:
5828         case TOK.auto_:
5829         case TOK.abstract_:
5830         case TOK.extern_:
5831         case TOK.align_:
5832         case TOK.immutable_:
5833         case TOK.shared_:
5834         case TOK.inout_:
5835         case TOK.deprecated_:
5836         case TOK.nothrow_:
5837         case TOK.pure_:
5838         case TOK.ref_:
5839         case TOK.gshared:
5840         case TOK.at:
5841         case TOK.struct_:
5842         case TOK.union_:
5843         case TOK.class_:
5844         case TOK.interface_:
5845         Ldeclaration:
5846             {
5847                 AST.Dsymbols* a = parseDeclarations(false, null, null);
5848                 if (a.dim > 1)
5849                 {
5850                     auto as = new AST.Statements();
5851                     as.reserve(a.dim);
5852                     foreach (i; 0 .. a.dim)
5853                     {
5854                         AST.Dsymbol d = (*a)[i];
5855                         s = new AST.ExpStatement(loc, d);
5856                         as.push(s);
5857                     }
5858                     s = new AST.CompoundDeclarationStatement(loc, as);
5859                 }
5860                 else if (a.dim == 1)
5861                 {
5862                     AST.Dsymbol d = (*a)[0];
5863                     s = new AST.ExpStatement(loc, d);
5864                 }
5865                 else
5866                     s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5867                 if (flags & ParseStatementFlags.scope_)
5868                     s = new AST.ScopeStatement(loc, s, token.loc);
5869                 break;
5870             }
5871         case TOK.enum_:
5872             {
5873                 /* Determine if this is a manifest constant declaration,
5874                  * or a conventional enum.
5875                  */
5876                 AST.Dsymbol d;
5877                 const tv = peekNext();
5878                 if (tv == TOK.leftCurly || tv == TOK.colon)
5879                     d = parseEnum();
5880                 else if (tv != TOK.identifier)
5881                     goto Ldeclaration;
5882                 else
5883                 {
5884                     const nextv = peekNext2();
5885                     if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
5886                         d = parseEnum();
5887                     else
5888                         goto Ldeclaration;
5889                 }
5890                 s = new AST.ExpStatement(loc, d);
5891                 if (flags & ParseStatementFlags.scope_)
5892                     s = new AST.ScopeStatement(loc, s, token.loc);
5893                 break;
5894             }
5895         case TOK.mixin_:
5896             {
5897                 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5898                     goto Ldeclaration;
5899                 if (peekNext() == TOK.leftParenthesis)
5900                 {
5901                     // mixin(string)
5902                     AST.Expression e = parseAssignExp();
5903                     check(TOK.semicolon);
5904                     if (e.op == TOK.mixin_)
5905                     {
5906                         AST.MixinExp cpe = cast(AST.MixinExp)e;
5907                         s = new AST.CompileStatement(loc, cpe.exps);
5908                     }
5909                     else
5910                     {
5911                         s = new AST.ExpStatement(loc, e);
5912                     }
5913                     break;
5914                 }
5915                 AST.Dsymbol d = parseMixin();
5916                 s = new AST.ExpStatement(loc, d);
5917                 if (flags & ParseStatementFlags.scope_)
5918                     s = new AST.ScopeStatement(loc, s, token.loc);
5919                 break;
5920             }
5921         case TOK.leftCurly:
5922             {
5923                 const lookingForElseSave = lookingForElse;
5924                 lookingForElse = Loc.initial;
5925 
5926                 nextToken();
5927                 //if (token.value == TOK.semicolon)
5928                 //    error("use `{ }` for an empty statement, not `;`");
5929                 auto statements = new AST.Statements();
5930                 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
5931                 {
5932                     statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
5933                 }
5934                 if (endPtr)
5935                     *endPtr = token.ptr;
5936                 endloc = token.loc;
5937                 if (pEndloc)
5938                 {
5939                     *pEndloc = token.loc;
5940                     pEndloc = null; // don't set it again
5941                 }
5942                 s = new AST.CompoundStatement(loc, statements);
5943                 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
5944                     s = new AST.ScopeStatement(loc, s, token.loc);
5945                 check(TOK.rightCurly, "compound statement");
5946                 lookingForElse = lookingForElseSave;
5947                 break;
5948             }
5949         case TOK.while_:
5950             {
5951                 AST.Parameter param = null;
5952                 nextToken();
5953                 check(TOK.leftParenthesis);
5954                 param = parseAssignCondition();
5955                 AST.Expression condition = parseExpression();
5956                 check(TOK.rightParenthesis);
5957                 Loc endloc;
5958                 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
5959                 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
5960                 break;
5961             }
5962         case TOK.semicolon:
5963             if (!(flags & ParseStatementFlags.semiOk))
5964             {
5965                 if (flags & ParseStatementFlags.semi)
5966                     deprecation("use `{ }` for an empty statement, not `;`");
5967                 else
5968                     error("use `{ }` for an empty statement, not `;`");
5969             }
5970             nextToken();
5971             s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5972             break;
5973 
5974         case TOK.do_:
5975             {
5976                 AST.Statement _body;
5977                 AST.Expression condition;
5978 
5979                 nextToken();
5980                 const lookingForElseSave = lookingForElse;
5981                 lookingForElse = Loc.initial;
5982                 _body = parseStatement(ParseStatementFlags.scope_);
5983                 lookingForElse = lookingForElseSave;
5984                 check(TOK.while_);
5985                 check(TOK.leftParenthesis);
5986                 condition = parseExpression();
5987                 check(TOK.rightParenthesis);
5988                 if (token.value == TOK.semicolon)
5989                     nextToken();
5990                 else
5991                     error("terminating `;` required after do-while statement");
5992                 s = new AST.DoStatement(loc, _body, condition, token.loc);
5993                 break;
5994             }
5995         case TOK.for_:
5996             {
5997                 AST.Statement _init;
5998                 AST.Expression condition;
5999                 AST.Expression increment;
6000 
6001                 nextToken();
6002                 check(TOK.leftParenthesis);
6003                 if (token.value == TOK.semicolon)
6004                 {
6005                     _init = null;
6006                     nextToken();
6007                 }
6008                 else
6009                 {
6010                     const lookingForElseSave = lookingForElse;
6011                     lookingForElse = Loc.initial;
6012                     _init = parseStatement(0);
6013                     lookingForElse = lookingForElseSave;
6014                 }
6015                 if (token.value == TOK.semicolon)
6016                 {
6017                     condition = null;
6018                     nextToken();
6019                 }
6020                 else
6021                 {
6022                     condition = parseExpression();
6023                     check(TOK.semicolon, "`for` condition");
6024                 }
6025                 if (token.value == TOK.rightParenthesis)
6026                 {
6027                     increment = null;
6028                     nextToken();
6029                 }
6030                 else
6031                 {
6032                     increment = parseExpression();
6033                     check(TOK.rightParenthesis);
6034                 }
6035                 Loc endloc;
6036                 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6037                 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6038                 break;
6039             }
6040         case TOK.foreach_:
6041         case TOK.foreach_reverse_:
6042             {
6043                 s = parseForeach!(AST.Statement)(loc, null);
6044                 break;
6045             }
6046         case TOK.if_:
6047             {
6048                 AST.Parameter param = null;
6049                 AST.Expression condition;
6050 
6051                 nextToken();
6052                 check(TOK.leftParenthesis);
6053                 param = parseAssignCondition();
6054                 condition = parseExpression();
6055                 check(TOK.rightParenthesis);
6056                 {
6057                     const lookingForElseSave = lookingForElse;
6058                     lookingForElse = loc;
6059                     ifbody = parseStatement(ParseStatementFlags.scope_);
6060                     lookingForElse = lookingForElseSave;
6061                 }
6062                 if (token.value == TOK.else_)
6063                 {
6064                     const elseloc = token.loc;
6065                     nextToken();
6066                     elsebody = parseStatement(ParseStatementFlags.scope_);
6067                     checkDanglingElse(elseloc);
6068                 }
6069                 else
6070                     elsebody = null;
6071                 if (condition && ifbody)
6072                     s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6073                 else
6074                     s = null; // don't propagate parsing errors
6075                 break;
6076             }
6077 
6078         case TOK.else_:
6079             error("found `else` without a corresponding `if`, `version` or `debug` statement");
6080             goto Lerror;
6081 
6082         case TOK.scope_:
6083             if (peekNext() != TOK.leftParenthesis)
6084                 goto Ldeclaration; // scope used as storage class
6085             nextToken();
6086             check(TOK.leftParenthesis);
6087             if (token.value != TOK.identifier)
6088             {
6089                 error("scope identifier expected");
6090                 goto Lerror;
6091             }
6092             else
6093             {
6094                 TOK t = TOK.onScopeExit;
6095                 Identifier id = token.ident;
6096                 if (id == Id.exit)
6097                     t = TOK.onScopeExit;
6098                 else if (id == Id.failure)
6099                     t = TOK.onScopeFailure;
6100                 else if (id == Id.success)
6101                     t = TOK.onScopeSuccess;
6102                 else
6103                     error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6104                 nextToken();
6105                 check(TOK.rightParenthesis);
6106                 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6107                 s = new AST.ScopeGuardStatement(loc, t, st);
6108                 break;
6109             }
6110 
6111         case TOK.debug_:
6112             nextToken();
6113             if (token.value == TOK.assign)
6114             {
6115                 error("debug conditions can only be declared at module scope");
6116                 nextToken();
6117                 nextToken();
6118                 goto Lerror;
6119             }
6120             cond = parseDebugCondition();
6121             goto Lcondition;
6122 
6123         case TOK.version_:
6124             nextToken();
6125             if (token.value == TOK.assign)
6126             {
6127                 error("version conditions can only be declared at module scope");
6128                 nextToken();
6129                 nextToken();
6130                 goto Lerror;
6131             }
6132             cond = parseVersionCondition();
6133             goto Lcondition;
6134 
6135         Lcondition:
6136             {
6137                 const lookingForElseSave = lookingForElse;
6138                 lookingForElse = loc;
6139                 ifbody = parseStatement(0);
6140                 lookingForElse = lookingForElseSave;
6141             }
6142             elsebody = null;
6143             if (token.value == TOK.else_)
6144             {
6145                 const elseloc = token.loc;
6146                 nextToken();
6147                 elsebody = parseStatement(0);
6148                 checkDanglingElse(elseloc);
6149             }
6150             s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6151             if (flags & ParseStatementFlags.scope_)
6152                 s = new AST.ScopeStatement(loc, s, token.loc);
6153             break;
6154 
6155         case TOK.pragma_:
6156             {
6157                 Identifier ident;
6158                 AST.Expressions* args = null;
6159                 AST.Statement _body;
6160 
6161                 nextToken();
6162                 check(TOK.leftParenthesis);
6163                 if (token.value != TOK.identifier)
6164                 {
6165                     error("`pragma(identifier)` expected");
6166                     goto Lerror;
6167                 }
6168                 ident = token.ident;
6169                 nextToken();
6170                 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6171                     args = parseArguments(); // pragma(identifier, args...);
6172                 else
6173                     check(TOK.rightParenthesis); // pragma(identifier);
6174                 if (token.value == TOK.semicolon)
6175                 {
6176                     nextToken();
6177                     _body = null;
6178                 }
6179                 else
6180                     _body = parseStatement(ParseStatementFlags.semi);
6181                 s = new AST.PragmaStatement(loc, ident, args, _body);
6182                 break;
6183             }
6184         case TOK.switch_:
6185             isfinal = false;
6186             goto Lswitch;
6187 
6188         Lswitch:
6189             {
6190                 nextToken();
6191                 check(TOK.leftParenthesis);
6192                 AST.Expression condition = parseExpression();
6193                 check(TOK.rightParenthesis);
6194                 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6195                 s = new AST.SwitchStatement(loc, condition, _body, isfinal);
6196                 break;
6197             }
6198         case TOK.case_:
6199             {
6200                 AST.Expression exp;
6201                 AST.Expressions cases; // array of Expression's
6202                 AST.Expression last = null;
6203 
6204                 nextToken();
6205                 do
6206                 {
6207                     exp = parseAssignExp();
6208                     cases.push(exp);
6209                     if (token.value != TOK.comma)
6210                         break;
6211                     nextToken(); //comma
6212                 }
6213                 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6214                 check(TOK.colon);
6215 
6216                 /* case exp: .. case last:
6217                  */
6218                 if (token.value == TOK.slice)
6219                 {
6220                     if (cases.dim > 1)
6221                         error("only one `case` allowed for start of case range");
6222                     nextToken();
6223                     check(TOK.case_);
6224                     last = parseAssignExp();
6225                     check(TOK.colon);
6226                 }
6227 
6228                 if (flags & ParseStatementFlags.curlyScope)
6229                 {
6230                     auto statements = new AST.Statements();
6231                     while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6232                     {
6233                         auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
6234                         statements.push(cur);
6235 
6236                         // https://issues.dlang.org/show_bug.cgi?id=21739
6237                         // Stop at the last break s.t. the following non-case statements are
6238                         // not merged into the current case. This can happen for
6239                         // case 1: ... break;
6240                         // debug { case 2: ... }
6241                         if (cur && cur.isBreakStatement())
6242                             break;
6243                     }
6244                     s = new AST.CompoundStatement(loc, statements);
6245                 }
6246                 else
6247                 {
6248                     s = parseStatement(ParseStatementFlags.semi);
6249                 }
6250                 s = new AST.ScopeStatement(loc, s, token.loc);
6251 
6252                 if (last)
6253                 {
6254                     s = new AST.CaseRangeStatement(loc, exp, last, s);
6255                 }
6256                 else
6257                 {
6258                     // Keep cases in order by building the case statements backwards
6259                     for (size_t i = cases.dim; i; i--)
6260                     {
6261                         exp = cases[i - 1];
6262                         s = new AST.CaseStatement(loc, exp, s);
6263                     }
6264                 }
6265                 break;
6266             }
6267         case TOK.default_:
6268             {
6269                 nextToken();
6270                 check(TOK.colon);
6271 
6272                 if (flags & ParseStatementFlags.curlyScope)
6273                 {
6274                     auto statements = new AST.Statements();
6275                     while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6276                     {
6277                         statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
6278                     }
6279                     s = new AST.CompoundStatement(loc, statements);
6280                 }
6281                 else
6282                     s = parseStatement(ParseStatementFlags.semi);
6283                 s = new AST.ScopeStatement(loc, s, token.loc);
6284                 s = new AST.DefaultStatement(loc, s);
6285                 break;
6286             }
6287         case TOK.return_:
6288             {
6289                 AST.Expression exp;
6290                 nextToken();
6291                 exp = token.value == TOK.semicolon ? null : parseExpression();
6292                 check(TOK.semicolon, "`return` statement");
6293                 s = new AST.ReturnStatement(loc, exp);
6294                 break;
6295             }
6296         case TOK.break_:
6297             {
6298                 Identifier ident;
6299                 nextToken();
6300                 ident = null;
6301                 if (token.value == TOK.identifier)
6302                 {
6303                     ident = token.ident;
6304                     nextToken();
6305                 }
6306                 check(TOK.semicolon, "`break` statement");
6307                 s = new AST.BreakStatement(loc, ident);
6308                 break;
6309             }
6310         case TOK.continue_:
6311             {
6312                 Identifier ident;
6313                 nextToken();
6314                 ident = null;
6315                 if (token.value == TOK.identifier)
6316                 {
6317                     ident = token.ident;
6318                     nextToken();
6319                 }
6320                 check(TOK.semicolon, "`continue` statement");
6321                 s = new AST.ContinueStatement(loc, ident);
6322                 break;
6323             }
6324         case TOK.goto_:
6325             {
6326                 Identifier ident;
6327                 nextToken();
6328                 if (token.value == TOK.default_)
6329                 {
6330                     nextToken();
6331                     s = new AST.GotoDefaultStatement(loc);
6332                 }
6333                 else if (token.value == TOK.case_)
6334                 {
6335                     AST.Expression exp = null;
6336                     nextToken();
6337                     if (token.value != TOK.semicolon)
6338                         exp = parseExpression();
6339                     s = new AST.GotoCaseStatement(loc, exp);
6340                 }
6341                 else
6342                 {
6343                     if (token.value != TOK.identifier)
6344                     {
6345                         error("identifier expected following `goto`");
6346                         ident = null;
6347                     }
6348                     else
6349                     {
6350                         ident = token.ident;
6351                         nextToken();
6352                     }
6353                     s = new AST.GotoStatement(loc, ident);
6354                 }
6355                 check(TOK.semicolon, "`goto` statement");
6356                 break;
6357             }
6358         case TOK.synchronized_:
6359             {
6360                 AST.Expression exp;
6361                 AST.Statement _body;
6362 
6363                 Token* t = peek(&token);
6364                 if (skipAttributes(t, &t) && t.value == TOK.class_)
6365                     goto Ldeclaration;
6366 
6367                 nextToken();
6368                 if (token.value == TOK.leftParenthesis)
6369                 {
6370                     nextToken();
6371                     exp = parseExpression();
6372                     check(TOK.rightParenthesis);
6373                 }
6374                 else
6375                     exp = null;
6376                 _body = parseStatement(ParseStatementFlags.scope_);
6377                 s = new AST.SynchronizedStatement(loc, exp, _body);
6378                 break;
6379             }
6380         case TOK.with_:
6381             {
6382                 AST.Expression exp;
6383                 AST.Statement _body;
6384                 Loc endloc = loc;
6385 
6386                 nextToken();
6387                 check(TOK.leftParenthesis);
6388                 exp = parseExpression();
6389                 check(TOK.rightParenthesis);
6390                 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6391                 s = new AST.WithStatement(loc, exp, _body, endloc);
6392                 break;
6393             }
6394         case TOK.try_:
6395             {
6396                 AST.Statement _body;
6397                 AST.Catches* catches = null;
6398                 AST.Statement finalbody = null;
6399 
6400                 nextToken();
6401                 const lookingForElseSave = lookingForElse;
6402                 lookingForElse = Loc.initial;
6403                 _body = parseStatement(ParseStatementFlags.scope_);
6404                 lookingForElse = lookingForElseSave;
6405                 while (token.value == TOK.catch_)
6406                 {
6407                     AST.Statement handler;
6408                     AST.Catch c;
6409                     AST.Type t;
6410                     Identifier id;
6411                     const catchloc = token.loc;
6412 
6413                     nextToken();
6414                     if (token.value != TOK.leftParenthesis)
6415                     {
6416                         deprecation("`catch` statement without an exception specification is deprecated");
6417                         deprecationSupplemental(token.loc, "use `catch(Throwable)` for old behavior");
6418                         t = null;
6419                         id = null;
6420                     }
6421                     else
6422                     {
6423                         check(TOK.leftParenthesis);
6424                         id = null;
6425                         t = parseType(&id);
6426                         check(TOK.rightParenthesis);
6427                     }
6428                     handler = parseStatement(0);
6429                     c = new AST.Catch(catchloc, t, id, handler);
6430                     if (!catches)
6431                         catches = new AST.Catches();
6432                     catches.push(c);
6433                 }
6434 
6435                 if (token.value == TOK.finally_)
6436                 {
6437                     nextToken();
6438                     finalbody = parseStatement(ParseStatementFlags.scope_);
6439                 }
6440 
6441                 s = _body;
6442                 if (!catches && !finalbody)
6443                     error("`catch` or `finally` expected following `try`");
6444                 else
6445                 {
6446                     if (catches)
6447                         s = new AST.TryCatchStatement(loc, _body, catches);
6448                     if (finalbody)
6449                         s = new AST.TryFinallyStatement(loc, s, finalbody);
6450                 }
6451                 break;
6452             }
6453         case TOK.throw_:
6454             {
6455                 AST.Expression exp;
6456                 nextToken();
6457                 exp = parseExpression();
6458                 check(TOK.semicolon, "`throw` statement");
6459                 s = new AST.ThrowStatement(loc, exp);
6460                 break;
6461             }
6462 
6463         case TOK.asm_:
6464             s = parseAsm();
6465             break;
6466 
6467         case TOK.import_:
6468             {
6469                 /* https://issues.dlang.org/show_bug.cgi?id=16088
6470                  *
6471                  * At this point it can either be an
6472                  * https://dlang.org/spec/grammar.html#ImportExpression
6473                  * or an
6474                  * https://dlang.org/spec/grammar.html#ImportDeclaration.
6475                  * See if the next token after `import` is a `(`; if so,
6476                  * then it is an import expression.
6477                  */
6478                 if (peekNext() == TOK.leftParenthesis)
6479                 {
6480                     AST.Expression e = parseExpression();
6481                     check(TOK.semicolon);
6482                     s = new AST.ExpStatement(loc, e);
6483                 }
6484                 else
6485                 {
6486                     AST.Dsymbols* imports = parseImport();
6487                     s = new AST.ImportStatement(loc, imports);
6488                     if (flags & ParseStatementFlags.scope_)
6489                         s = new AST.ScopeStatement(loc, s, token.loc);
6490                 }
6491                 break;
6492             }
6493         case TOK.template_:
6494             {
6495                 AST.Dsymbol d = parseTemplateDeclaration();
6496                 s = new AST.ExpStatement(loc, d);
6497                 break;
6498             }
6499         default:
6500             error("found `%s` instead of statement", token.toChars());
6501             goto Lerror;
6502 
6503         Lerror:
6504             while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6505                 nextToken();
6506             if (token.value == TOK.semicolon)
6507                 nextToken();
6508             s = null;
6509             break;
6510         }
6511         if (pEndloc)
6512             *pEndloc = prevloc;
6513         return s;
6514     }
6515 
6516 
6517     private  AST.ExpInitializer parseExpInitializer(Loc loc)
6518     {
6519         auto ae = parseAssignExp();
6520         return new AST.ExpInitializer(loc, ae);
6521     }
6522 
6523     private AST.Initializer parseStructInitializer(Loc loc)
6524     {
6525         /* Scan ahead to discern between a struct initializer and
6526          * parameterless function literal.
6527          *
6528          * We'll scan the topmost curly bracket level for statement-related
6529          * tokens, thereby ruling out a struct initializer.  (A struct
6530          * initializer which itself contains function literals may have
6531          * statements at nested curly bracket levels.)
6532          *
6533          * It's important that this function literal check not be
6534          * pendantic, otherwise a function having the slightest syntax
6535          * error would emit confusing errors when we proceed to parse it
6536          * as a struct initializer.
6537          *
6538          * The following two ambiguous cases will be treated as a struct
6539          * initializer (best we can do without type info):
6540          *     {}
6541          *     {{statements...}}  - i.e. it could be struct initializer
6542          *        with one function literal, or function literal having an
6543          *        extra level of curly brackets
6544          * If a function literal is intended in these cases (unlikely),
6545          * source can use a more explicit function literal syntax
6546          * (e.g. prefix with "()" for empty parameter list).
6547          */
6548         int braces = 1;
6549         int parens = 0;
6550         for (auto t = peek(&token); 1; t = peek(t))
6551         {
6552             switch (t.value)
6553             {
6554                 case TOK.leftParenthesis:
6555                     parens++;
6556                     continue;
6557                 case TOK.rightParenthesis:
6558                     parens--;
6559                     continue;
6560                 // https://issues.dlang.org/show_bug.cgi?id=21163
6561                 // lambda params can have the `scope` storage class, e.g
6562                 // `S s = { (scope Type Id){} }`
6563                 case TOK.scope_:
6564                     if (!parens) goto case;
6565                     continue;
6566                 /* Look for a semicolon or keyword of statements which don't
6567                  * require a semicolon (typically containing BlockStatement).
6568                  * Tokens like "else", "catch", etc. are omitted where the
6569                  * leading token of the statement is sufficient.
6570                  */
6571                 case TOK.asm_:
6572                 case TOK.class_:
6573                 case TOK.debug_:
6574                 case TOK.enum_:
6575                 case TOK.if_:
6576                 case TOK.interface_:
6577                 case TOK.pragma_:
6578                 case TOK.semicolon:
6579                 case TOK.struct_:
6580                 case TOK.switch_:
6581                 case TOK.synchronized_:
6582                 case TOK.try_:
6583                 case TOK.union_:
6584                 case TOK.version_:
6585                 case TOK.while_:
6586                 case TOK.with_:
6587                     if (braces == 1)
6588                         return parseExpInitializer(loc);
6589                     continue;
6590 
6591                 case TOK.leftCurly:
6592                     braces++;
6593                     continue;
6594 
6595                 case TOK.rightCurly:
6596                     if (--braces == 0)
6597                         break;
6598                     continue;
6599 
6600                 case TOK.endOfFile:
6601                     break;
6602 
6603                 default:
6604                     continue;
6605             }
6606             break;
6607         }
6608 
6609         auto _is = new AST.StructInitializer(loc);
6610         bool commaExpected = false;
6611         nextToken();
6612         while (1)
6613         {
6614             switch (token.value)
6615             {
6616                 case TOK.identifier:
6617                 {
6618 
6619                     if (commaExpected)
6620                         error("comma expected separating field initializers");
6621                     const t = peek(&token);
6622                     Identifier id;
6623                     if (t.value == TOK.colon)
6624                     {
6625                         id = token.ident;
6626                         nextToken();
6627                         nextToken(); // skip over ':'
6628                     }
6629                     auto value = parseInitializer();
6630                     _is.addInit(id, value);
6631                     commaExpected = true;
6632                     continue;
6633                 }
6634                 case TOK.comma:
6635                     if (!commaExpected)
6636                         error("expression expected, not `,`");
6637                     nextToken();
6638                     commaExpected = false;
6639                     continue;
6640 
6641                 case TOK.rightCurly: // allow trailing comma's
6642                     nextToken();
6643                     break;
6644 
6645                 case TOK.endOfFile:
6646                     error("found end of file instead of initializer");
6647                     break;
6648 
6649                 default:
6650                     if (commaExpected)
6651                         error("comma expected separating field initializers");
6652                     auto value = parseInitializer();
6653                     _is.addInit(null, value);
6654                     commaExpected = true;
6655                     continue;
6656             }
6657             break;
6658         }
6659         return _is;
6660 
6661     }
6662 
6663     /*****************************************
6664      * Parse initializer for variable declaration.
6665      */
6666     private AST.Initializer parseInitializer()
6667     {
6668         const loc = token.loc;
6669 
6670         switch (token.value)
6671         {
6672         case TOK.leftCurly:
6673             return parseStructInitializer(loc);
6674 
6675         case TOK.leftBracket:
6676             /* Scan ahead to see if it is an array initializer or
6677              * an expression.
6678              * If it ends with a ';' ',' or '}', it is an array initializer.
6679              */
6680             int brackets = 1;
6681             for (auto t = peek(&token); 1; t = peek(t))
6682             {
6683                 switch (t.value)
6684                 {
6685                 case TOK.leftBracket:
6686                     brackets++;
6687                     continue;
6688 
6689                 case TOK.rightBracket:
6690                     if (--brackets == 0)
6691                     {
6692                         t = peek(t);
6693                         if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6694                             return parseExpInitializer(loc);
6695                         break;
6696                     }
6697                     continue;
6698 
6699                 case TOK.endOfFile:
6700                     break;
6701 
6702                 default:
6703                     continue;
6704                 }
6705                 break;
6706             }
6707 
6708             auto ia = new AST.ArrayInitializer(loc);
6709             bool commaExpected = false;
6710 
6711             nextToken();
6712             while (1)
6713             {
6714                 switch (token.value)
6715                 {
6716                 default:
6717                     if (commaExpected)
6718                     {
6719                         error("comma expected separating array initializers, not `%s`", token.toChars());
6720                         nextToken();
6721                         break;
6722                     }
6723                     auto e = parseAssignExp();
6724                     if (!e)
6725                         break;
6726 
6727                     AST.Initializer value;
6728                     if (token.value == TOK.colon)
6729                     {
6730                         nextToken();
6731                         value = parseInitializer();
6732                     }
6733                     else
6734                     {
6735                         value = new AST.ExpInitializer(e.loc, e);
6736                         e = null;
6737                     }
6738                     ia.addInit(e, value);
6739                     commaExpected = true;
6740                     continue;
6741 
6742                 case TOK.leftCurly:
6743                 case TOK.leftBracket:
6744                     if (commaExpected)
6745                         error("comma expected separating array initializers, not `%s`", token.toChars());
6746                     auto value = parseInitializer();
6747                     AST.Expression e;
6748 
6749                     if (token.value == TOK.colon)
6750                     {
6751                         nextToken();
6752                         if (auto ei = value.isExpInitializer())
6753                         {
6754                             e = ei.exp;
6755                             value = parseInitializer();
6756                         }
6757                         else
6758                             error("initializer expression expected following colon, not `%s`", token.toChars());
6759                     }
6760                     ia.addInit(e, value);
6761                     commaExpected = true;
6762                     continue;
6763 
6764                 case TOK.comma:
6765                     if (!commaExpected)
6766                         error("expression expected, not `,`");
6767                     nextToken();
6768                     commaExpected = false;
6769                     continue;
6770 
6771                 case TOK.rightBracket: // allow trailing comma's
6772                     nextToken();
6773                     break;
6774 
6775                 case TOK.endOfFile:
6776                     error("found `%s` instead of array initializer", token.toChars());
6777                     break;
6778                 }
6779                 break;
6780             }
6781             return ia;
6782 
6783         case TOK.void_:
6784             const tv = peekNext();
6785             if (tv == TOK.semicolon || tv == TOK.comma)
6786             {
6787                 nextToken();
6788                 return new AST.VoidInitializer(loc);
6789             }
6790             return parseExpInitializer(loc);
6791 
6792         default:
6793             return parseExpInitializer(loc);
6794         }
6795     }
6796 
6797     /*****************************************
6798      * Parses default argument initializer expression that is an assign expression,
6799      * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
6800      */
6801     private AST.Expression parseDefaultInitExp()
6802     {
6803         AST.Expression e = null;
6804         const tv = peekNext();
6805         if (tv == TOK.comma || tv == TOK.rightParenthesis)
6806         {
6807             switch (token.value)
6808             {
6809             case TOK.file:           e = new AST.FileInitExp(token.loc, TOK.file); break;
6810             case TOK.fileFullPath:   e = new AST.FileInitExp(token.loc, TOK.fileFullPath); break;
6811             case TOK.line:           e = new AST.LineInitExp(token.loc); break;
6812             case TOK.moduleString:   e = new AST.ModuleInitExp(token.loc); break;
6813             case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
6814             case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
6815             default: goto LExp;
6816             }
6817             nextToken();
6818             return e;
6819         }
6820         LExp:
6821         return parseAssignExp();
6822     }
6823 
6824     /********************
6825      * Parse inline assembler block.
6826      * Returns:
6827      *   inline assembler block as a Statement
6828      */
6829     AST.Statement parseAsm()
6830     {
6831         // Parse the asm block into a sequence of AsmStatements,
6832         // each AsmStatement is one instruction.
6833         // Separate out labels.
6834         // Defer parsing of AsmStatements until semantic processing.
6835 
6836         const loc = token.loc;
6837         Loc labelloc;
6838 
6839         nextToken();
6840         StorageClass stc = parsePostfix(STC.undefined_, null);
6841         if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6842             error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6843 
6844         check(TOK.leftCurly);
6845         Token* toklist = null;
6846         Token** ptoklist = &toklist;
6847         Identifier label = null;
6848         auto statements = new AST.Statements();
6849         size_t nestlevel = 0;
6850         while (1)
6851         {
6852             switch (token.value)
6853             {
6854             case TOK.identifier:
6855                 if (!toklist)
6856                 {
6857                     // Look ahead to see if it is a label
6858                     if (peekNext() == TOK.colon)
6859                     {
6860                         // It's a label
6861                         label = token.ident;
6862                         labelloc = token.loc;
6863                         nextToken();
6864                         nextToken();
6865                         continue;
6866                     }
6867                 }
6868                 goto default;
6869 
6870             case TOK.leftCurly:
6871                 ++nestlevel;
6872                 goto default;
6873 
6874             case TOK.rightCurly:
6875                 if (nestlevel > 0)
6876                 {
6877                     --nestlevel;
6878                     goto default;
6879                 }
6880                 if (toklist || label)
6881                 {
6882                     error("`asm` statements must end in `;`");
6883                 }
6884                 break;
6885 
6886             case TOK.semicolon:
6887                 if (nestlevel != 0)
6888                     error("mismatched number of curly brackets");
6889 
6890                 if (toklist || label)
6891                 {
6892                     // Create AsmStatement from list of tokens we've saved
6893                     AST.Statement s = new AST.AsmStatement(token.loc, toklist);
6894                     toklist = null;
6895                     ptoklist = &toklist;
6896                     if (label)
6897                     {
6898                         s = new AST.LabelStatement(labelloc, label, s);
6899                         label = null;
6900                     }
6901                     statements.push(s);
6902                 }
6903                 nextToken();
6904                 continue;
6905 
6906             case TOK.endOfFile:
6907                 /* { */
6908                 error("matching `}` expected, not end of file");
6909                 break;
6910 
6911             case TOK.colonColon:  // treat as two separate : tokens for iasmgcc
6912                 *ptoklist = allocateToken();
6913                 memcpy(*ptoklist, &token, Token.sizeof);
6914                 (*ptoklist).value = TOK.colon;
6915                 ptoklist = &(*ptoklist).next;
6916 
6917                 *ptoklist = allocateToken();
6918                 memcpy(*ptoklist, &token, Token.sizeof);
6919                 (*ptoklist).value = TOK.colon;
6920                 ptoklist = &(*ptoklist).next;
6921 
6922                 *ptoklist = null;
6923                 nextToken();
6924                 continue;
6925 
6926             default:
6927                 *ptoklist = allocateToken();
6928                 memcpy(*ptoklist, &token, Token.sizeof);
6929                 ptoklist = &(*ptoklist).next;
6930                 *ptoklist = null;
6931                 nextToken();
6932                 continue;
6933             }
6934             break;
6935         }
6936         nextToken();
6937         auto s = new AST.CompoundAsmStatement(loc, statements, stc);
6938         return s;
6939     }
6940 
6941     /**********************************
6942      * Issue error if the current token is not `value`,
6943      * advance to next token.
6944      * Params:
6945      *  loc = location for error message
6946      *  value = token value to compare with
6947      */
6948     void check(Loc loc, TOK value)
6949     {
6950         if (token.value != value)
6951             error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
6952         nextToken();
6953     }
6954 
6955     /**********************************
6956      * Issue error if the current token is not `value`,
6957      * advance to next token.
6958      * Params:
6959      *  value = token value to compare with
6960      */
6961     void check(TOK value)
6962     {
6963         check(token.loc, value);
6964     }
6965 
6966     /**********************************
6967      * Issue error if the current token is not `value`,
6968      * advance to next token.
6969      * Params:
6970      *  value = token value to compare with
6971      *  string = for error message
6972      */
6973     void check(TOK value, const(char)* string)
6974     {
6975         if (token.value != value)
6976             error("found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
6977         nextToken();
6978     }
6979 
6980     private void checkParens(TOK value, AST.Expression e)
6981     {
6982         if (precedence[e.op] == PREC.rel && !e.parens)
6983             error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
6984     }
6985 
6986     ///
6987     enum NeedDeclaratorId
6988     {
6989         no,             // Declarator part must have no identifier
6990         opt,            // Declarator part identifier is optional
6991         must,           // Declarator part must have identifier
6992         mustIfDstyle,   // Declarator part must have identifier, but don't recognize old C-style syntax
6993     }
6994 
6995     /************************************
6996      * Determine if the scanner is sitting on the start of a declaration.
6997      * Params:
6998      *      t       = current token of the scanner
6999      *      needId  = flag with additional requirements for a declaration
7000      *      endtok  = ending token
7001      *      pt      = will be set ending token (if not null)
7002      * Output:
7003      *      true if the token `t` is a declaration, false otherwise
7004      */
7005     private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7006     {
7007         //printf("isDeclaration(needId = %d)\n", needId);
7008         int haveId = 0;
7009         int haveTpl = 0;
7010 
7011         while (1)
7012         {
7013             if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7014             {
7015                 /* const type
7016                  * immutable type
7017                  * shared type
7018                  * wild type
7019                  */
7020                 t = peek(t);
7021                 continue;
7022             }
7023             break;
7024         }
7025 
7026         if (!isBasicType(&t))
7027         {
7028             goto Lisnot;
7029         }
7030         if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7031             goto Lisnot;
7032         if ((needId == NeedDeclaratorId.no && !haveId) ||
7033             (needId == NeedDeclaratorId.opt) ||
7034             (needId == NeedDeclaratorId.must && haveId) ||
7035             (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7036         {
7037             if (pt)
7038                 *pt = t;
7039             goto Lis;
7040         }
7041         goto Lisnot;
7042 
7043     Lis:
7044         //printf("\tis declaration, t = %s\n", t.toChars());
7045         return true;
7046 
7047     Lisnot:
7048         //printf("\tis not declaration\n");
7049         return false;
7050     }
7051 
7052     private bool isBasicType(Token** pt)
7053     {
7054         // This code parallels parseBasicType()
7055         Token* t = *pt;
7056         switch (t.value)
7057         {
7058         case TOK.wchar_:
7059         case TOK.dchar_:
7060         case TOK.bool_:
7061         case TOK.char_:
7062         case TOK.int8:
7063         case TOK.uns8:
7064         case TOK.int16:
7065         case TOK.uns16:
7066         case TOK.int32:
7067         case TOK.uns32:
7068         case TOK.int64:
7069         case TOK.uns64:
7070         case TOK.int128:
7071         case TOK.uns128:
7072         case TOK.float32:
7073         case TOK.float64:
7074         case TOK.float80:
7075         case TOK.imaginary32:
7076         case TOK.imaginary64:
7077         case TOK.imaginary80:
7078         case TOK.complex32:
7079         case TOK.complex64:
7080         case TOK.complex80:
7081         case TOK.void_:
7082             t = peek(t);
7083             break;
7084 
7085         case TOK.identifier:
7086         L5:
7087             t = peek(t);
7088             if (t.value == TOK.not)
7089             {
7090                 goto L4;
7091             }
7092             goto L3;
7093             while (1)
7094             {
7095             L2:
7096                 t = peek(t);
7097             L3:
7098                 if (t.value == TOK.dot)
7099                 {
7100                 Ldot:
7101                     t = peek(t);
7102                     if (t.value != TOK.identifier)
7103                         goto Lfalse;
7104                     t = peek(t);
7105                     if (t.value != TOK.not)
7106                         goto L3;
7107                 L4:
7108                     /* Seen a !
7109                      * Look for:
7110                      * !( args ), !identifier, etc.
7111                      */
7112                     t = peek(t);
7113                     switch (t.value)
7114                     {
7115                     case TOK.identifier:
7116                         goto L5;
7117 
7118                     case TOK.leftParenthesis:
7119                         if (!skipParens(t, &t))
7120                             goto Lfalse;
7121                         goto L3;
7122 
7123                     case TOK.wchar_:
7124                     case TOK.dchar_:
7125                     case TOK.bool_:
7126                     case TOK.char_:
7127                     case TOK.int8:
7128                     case TOK.uns8:
7129                     case TOK.int16:
7130                     case TOK.uns16:
7131                     case TOK.int32:
7132                     case TOK.uns32:
7133                     case TOK.int64:
7134                     case TOK.uns64:
7135                     case TOK.int128:
7136                     case TOK.uns128:
7137                     case TOK.float32:
7138                     case TOK.float64:
7139                     case TOK.float80:
7140                     case TOK.imaginary32:
7141                     case TOK.imaginary64:
7142                     case TOK.imaginary80:
7143                     case TOK.complex32:
7144                     case TOK.complex64:
7145                     case TOK.complex80:
7146                     case TOK.void_:
7147                     case TOK.int32Literal:
7148                     case TOK.uns32Literal:
7149                     case TOK.int64Literal:
7150                     case TOK.uns64Literal:
7151                     case TOK.int128Literal:
7152                     case TOK.uns128Literal:
7153                     case TOK.float32Literal:
7154                     case TOK.float64Literal:
7155                     case TOK.float80Literal:
7156                     case TOK.imaginary32Literal:
7157                     case TOK.imaginary64Literal:
7158                     case TOK.imaginary80Literal:
7159                     case TOK.null_:
7160                     case TOK.true_:
7161                     case TOK.false_:
7162                     case TOK.charLiteral:
7163                     case TOK.wcharLiteral:
7164                     case TOK.dcharLiteral:
7165                     case TOK.string_:
7166                     case TOK.hexadecimalString:
7167                     case TOK.file:
7168                     case TOK.fileFullPath:
7169                     case TOK.line:
7170                     case TOK.moduleString:
7171                     case TOK.functionString:
7172                     case TOK.prettyFunction:
7173                         goto L2;
7174 
7175                     default:
7176                         goto Lfalse;
7177                     }
7178                 }
7179                 break;
7180             }
7181             break;
7182 
7183         case TOK.dot:
7184             goto Ldot;
7185 
7186         case TOK.typeof_:
7187         case TOK.vector:
7188         case TOK.mixin_:
7189             /* typeof(exp).identifier...
7190              */
7191             t = peek(t);
7192             if (!skipParens(t, &t))
7193                 goto Lfalse;
7194             goto L3;
7195 
7196         case TOK.traits:
7197             // __traits(getMember
7198             t = peek(t);
7199             if (t.value != TOK.leftParenthesis)
7200                 goto Lfalse;
7201             auto lp = t;
7202             t = peek(t);
7203             if (t.value != TOK.identifier || t.ident != Id.getMember)
7204                 goto Lfalse;
7205             if (!skipParens(lp, &lp))
7206                 goto Lfalse;
7207             // we are in a lookup for decl VS statement
7208             // so we expect a declarator following __trait if it's a type.
7209             // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7210             if (lp.value != TOK.identifier)
7211                 goto Lfalse;
7212 
7213             break;
7214 
7215         case TOK.const_:
7216         case TOK.immutable_:
7217         case TOK.shared_:
7218         case TOK.inout_:
7219             // const(type)  or  immutable(type)  or  shared(type)  or  wild(type)
7220             t = peek(t);
7221             if (t.value != TOK.leftParenthesis)
7222                 goto Lfalse;
7223             t = peek(t);
7224             if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7225             {
7226                 goto Lfalse;
7227             }
7228             t = peek(t);
7229             break;
7230 
7231         default:
7232             goto Lfalse;
7233         }
7234         *pt = t;
7235         //printf("is\n");
7236         return true;
7237 
7238     Lfalse:
7239         //printf("is not\n");
7240         return false;
7241     }
7242 
7243     private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7244     {
7245         // This code parallels parseDeclarator()
7246         Token* t = *pt;
7247         int parens;
7248 
7249         //printf("Parser::isDeclarator() %s\n", t.toChars());
7250         if (t.value == TOK.assign)
7251             return false;
7252 
7253         while (1)
7254         {
7255             parens = false;
7256             switch (t.value)
7257             {
7258             case TOK.mul:
7259             //case TOK.and:
7260                 t = peek(t);
7261                 continue;
7262 
7263             case TOK.leftBracket:
7264                 t = peek(t);
7265                 if (t.value == TOK.rightBracket)
7266                 {
7267                     t = peek(t);
7268                 }
7269                 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7270                 {
7271                     // It's an associative array declaration
7272                     t = peek(t);
7273 
7274                     // ...[type].ident
7275                     if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7276                     {
7277                         t = peek(t);
7278                         t = peek(t);
7279                     }
7280                 }
7281                 else
7282                 {
7283                     // [ expression ]
7284                     // [ expression .. expression ]
7285                     if (!isExpression(&t))
7286                         return false;
7287                     if (t.value == TOK.slice)
7288                     {
7289                         t = peek(t);
7290                         if (!isExpression(&t))
7291                             return false;
7292                         if (t.value != TOK.rightBracket)
7293                             return false;
7294                         t = peek(t);
7295                     }
7296                     else
7297                     {
7298                         if (t.value != TOK.rightBracket)
7299                             return false;
7300                         t = peek(t);
7301                         // ...[index].ident
7302                         if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7303                         {
7304                             t = peek(t);
7305                             t = peek(t);
7306                         }
7307                     }
7308                 }
7309                 continue;
7310 
7311             case TOK.identifier:
7312                 if (*haveId)
7313                     return false;
7314                 *haveId = true;
7315                 t = peek(t);
7316                 break;
7317 
7318             case TOK.leftParenthesis:
7319                 if (!allowAltSyntax)
7320                     return false;   // Do not recognize C-style declarations.
7321 
7322                 t = peek(t);
7323                 if (t.value == TOK.rightParenthesis)
7324                     return false; // () is not a declarator
7325 
7326                 /* Regard ( identifier ) as not a declarator
7327                  * BUG: what about ( *identifier ) in
7328                  *      f(*p)(x);
7329                  * where f is a class instance with overloaded () ?
7330                  * Should we just disallow C-style function pointer declarations?
7331                  */
7332                 if (t.value == TOK.identifier)
7333                 {
7334                     Token* t2 = peek(t);
7335                     if (t2.value == TOK.rightParenthesis)
7336                         return false;
7337                 }
7338 
7339                 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7340                     return false;
7341                 t = peek(t);
7342                 parens = true;
7343                 break;
7344 
7345             case TOK.delegate_:
7346             case TOK.function_:
7347                 t = peek(t);
7348                 if (!isParameters(&t))
7349                     return false;
7350                 skipAttributes(t, &t);
7351                 continue;
7352 
7353             default:
7354                 break;
7355             }
7356             break;
7357         }
7358 
7359         while (1)
7360         {
7361             switch (t.value)
7362             {
7363                 static if (CARRAYDECL)
7364                 {
7365                 case TOK.leftBracket:
7366                     parens = false;
7367                     t = peek(t);
7368                     if (t.value == TOK.rightBracket)
7369                     {
7370                         t = peek(t);
7371                     }
7372                     else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7373                     {
7374                         // It's an associative array declaration
7375                         t = peek(t);
7376                     }
7377                     else
7378                     {
7379                         // [ expression ]
7380                         if (!isExpression(&t))
7381                             return false;
7382                         if (t.value != TOK.rightBracket)
7383                             return false;
7384                         t = peek(t);
7385                     }
7386                     continue;
7387                 }
7388 
7389             case TOK.leftParenthesis:
7390                 parens = false;
7391                 if (Token* tk = peekPastParen(t))
7392                 {
7393                     if (tk.value == TOK.leftParenthesis)
7394                     {
7395                         if (!haveTpl)
7396                             return false;
7397                         *haveTpl = 1;
7398                         t = tk;
7399                     }
7400                     else if (tk.value == TOK.assign)
7401                     {
7402                         if (!haveTpl)
7403                             return false;
7404                         *haveTpl = 1;
7405                         *pt = tk;
7406                         return true;
7407                     }
7408                 }
7409                 if (!isParameters(&t))
7410                     return false;
7411                 while (1)
7412                 {
7413                     switch (t.value)
7414                     {
7415                     case TOK.const_:
7416                     case TOK.immutable_:
7417                     case TOK.shared_:
7418                     case TOK.inout_:
7419                     case TOK.pure_:
7420                     case TOK.nothrow_:
7421                     case TOK.return_:
7422                     case TOK.scope_:
7423                         t = peek(t);
7424                         continue;
7425 
7426                     case TOK.at:
7427                         t = peek(t); // skip '@'
7428                         t = peek(t); // skip identifier
7429                         continue;
7430 
7431                     default:
7432                         break;
7433                     }
7434                     break;
7435                 }
7436                 continue;
7437 
7438             // Valid tokens that follow a declaration
7439             case TOK.rightParenthesis:
7440             case TOK.rightBracket:
7441             case TOK.assign:
7442             case TOK.comma:
7443             case TOK.dotDotDot:
7444             case TOK.semicolon:
7445             case TOK.leftCurly:
7446             case TOK.in_:
7447             case TOK.out_:
7448             case TOK.do_:
7449                 // The !parens is to disallow unnecessary parentheses
7450                 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7451                 {
7452                     *pt = t;
7453                     return true;
7454                 }
7455                 return false;
7456 
7457             case TOK.identifier:
7458                 if (t.ident == Id._body)
7459                 {
7460                     // @@@DEPRECATED@@@
7461                     // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
7462                     // Deprecated in 2.097 - Can be removed from 2.117
7463                     // The deprecation period is longer than usual as `body`
7464                     // was quite widely used.
7465                     deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
7466                     goto case TOK.do_;
7467                 }
7468                 goto default;
7469 
7470             case TOK.if_:
7471                 return haveTpl ? true : false;
7472 
7473             // Used for mixin type parsing
7474             case TOK.endOfFile:
7475                 if (endtok == TOK.endOfFile)
7476                     goto case TOK.do_;
7477                 return false;
7478 
7479             default:
7480                 return false;
7481             }
7482         }
7483         assert(0);
7484     }
7485 
7486     private bool isParameters(Token** pt)
7487     {
7488         // This code parallels parseParameterList()
7489         Token* t = *pt;
7490 
7491         //printf("isParameters()\n");
7492         if (t.value != TOK.leftParenthesis)
7493             return false;
7494 
7495         t = peek(t);
7496         for (; 1; t = peek(t))
7497         {
7498         L1:
7499             switch (t.value)
7500             {
7501             case TOK.rightParenthesis:
7502                 break;
7503 
7504             case TOK.at:
7505                 Token* pastAttr;
7506                 if (skipAttributes(t, &pastAttr))
7507                 {
7508                     t = pastAttr;
7509                     goto default;
7510                 }
7511                 break;
7512 
7513             case TOK.dotDotDot:
7514                 t = peek(t);
7515                 break;
7516 
7517             case TOK.in_:
7518             case TOK.out_:
7519             case TOK.ref_:
7520             case TOK.lazy_:
7521             case TOK.scope_:
7522             case TOK.final_:
7523             case TOK.auto_:
7524             case TOK.return_:
7525                 continue;
7526 
7527             case TOK.const_:
7528             case TOK.immutable_:
7529             case TOK.shared_:
7530             case TOK.inout_:
7531                 t = peek(t);
7532                 if (t.value == TOK.leftParenthesis)
7533                 {
7534                     t = peek(t);
7535                     if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7536                         return false;
7537                     t = peek(t); // skip past closing ')'
7538                     goto L2;
7539                 }
7540                 goto L1;
7541 
7542                 version (none)
7543                 {
7544                 case TOK.static_:
7545                     continue;
7546                 case TOK.auto_:
7547                 case TOK.alias_:
7548                     t = peek(t);
7549                     if (t.value == TOK.identifier)
7550                         t = peek(t);
7551                     if (t.value == TOK.assign)
7552                     {
7553                         t = peek(t);
7554                         if (!isExpression(&t))
7555                             return false;
7556                     }
7557                     goto L3;
7558                 }
7559 
7560             default:
7561                 {
7562                     if (!isBasicType(&t))
7563                         return false;
7564                 L2:
7565                     int tmp = false;
7566                     if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7567                         return false;
7568                     if (t.value == TOK.assign)
7569                     {
7570                         t = peek(t);
7571                         if (!isExpression(&t))
7572                             return false;
7573                     }
7574                     if (t.value == TOK.dotDotDot)
7575                     {
7576                         t = peek(t);
7577                         break;
7578                     }
7579                 }
7580                 if (t.value == TOK.comma)
7581                 {
7582                     continue;
7583                 }
7584                 break;
7585             }
7586             break;
7587         }
7588         if (t.value != TOK.rightParenthesis)
7589             return false;
7590         t = peek(t);
7591         *pt = t;
7592         return true;
7593     }
7594 
7595     private bool isExpression(Token** pt)
7596     {
7597         // This is supposed to determine if something is an expression.
7598         // What it actually does is scan until a closing right bracket
7599         // is found.
7600 
7601         Token* t = *pt;
7602         int brnest = 0;
7603         int panest = 0;
7604         int curlynest = 0;
7605 
7606         for (;; t = peek(t))
7607         {
7608             switch (t.value)
7609             {
7610             case TOK.leftBracket:
7611                 brnest++;
7612                 continue;
7613 
7614             case TOK.rightBracket:
7615                 if (--brnest >= 0)
7616                     continue;
7617                 break;
7618 
7619             case TOK.leftParenthesis:
7620                 panest++;
7621                 continue;
7622 
7623             case TOK.comma:
7624                 if (brnest || panest)
7625                     continue;
7626                 break;
7627 
7628             case TOK.rightParenthesis:
7629                 if (--panest >= 0)
7630                     continue;
7631                 break;
7632 
7633             case TOK.leftCurly:
7634                 curlynest++;
7635                 continue;
7636 
7637             case TOK.rightCurly:
7638                 if (--curlynest >= 0)
7639                     continue;
7640                 return false;
7641 
7642             case TOK.slice:
7643                 if (brnest)
7644                     continue;
7645                 break;
7646 
7647             case TOK.semicolon:
7648                 if (curlynest)
7649                     continue;
7650                 return false;
7651 
7652             case TOK.endOfFile:
7653                 return false;
7654 
7655             default:
7656                 continue;
7657             }
7658             break;
7659         }
7660 
7661         *pt = t;
7662         return true;
7663     }
7664 
7665     /*******************************************
7666      * Skip parentheses.
7667      * Params:
7668      *      t = on opening $(LPAREN)
7669      *      pt = *pt is set to token past '$(RPAREN)' on true
7670      * Returns:
7671      *      true    successful
7672      *      false   some parsing error
7673      */
7674     bool skipParens(Token* t, Token** pt)
7675     {
7676         if (t.value != TOK.leftParenthesis)
7677             return false;
7678 
7679         int parens = 0;
7680 
7681         while (1)
7682         {
7683             switch (t.value)
7684             {
7685             case TOK.leftParenthesis:
7686                 parens++;
7687                 break;
7688 
7689             case TOK.rightParenthesis:
7690                 parens--;
7691                 if (parens < 0)
7692                     goto Lfalse;
7693                 if (parens == 0)
7694                     goto Ldone;
7695                 break;
7696 
7697             case TOK.endOfFile:
7698                 goto Lfalse;
7699 
7700             default:
7701                 break;
7702             }
7703             t = peek(t);
7704         }
7705     Ldone:
7706         if (pt)
7707             *pt = peek(t); // skip found rparen
7708         return true;
7709 
7710     Lfalse:
7711         return false;
7712     }
7713 
7714     private bool skipParensIf(Token* t, Token** pt)
7715     {
7716         if (t.value != TOK.leftParenthesis)
7717         {
7718             if (pt)
7719                 *pt = t;
7720             return true;
7721         }
7722         return skipParens(t, pt);
7723     }
7724 
7725     //returns true if the next value (after optional matching parens) is expected
7726     private bool hasOptionalParensThen(Token* t, TOK expected)
7727     {
7728         Token* tk;
7729         if (!skipParensIf(t, &tk))
7730             return false;
7731         return tk.value == expected;
7732     }
7733 
7734     /*******************************************
7735      * Skip attributes.
7736      * Input:
7737      *      t is on a candidate attribute
7738      * Output:
7739      *      *pt is set to first non-attribute token on success
7740      * Returns:
7741      *      true    successful
7742      *      false   some parsing error
7743      */
7744     private bool skipAttributes(Token* t, Token** pt)
7745     {
7746         while (1)
7747         {
7748             switch (t.value)
7749             {
7750             case TOK.const_:
7751             case TOK.immutable_:
7752             case TOK.shared_:
7753             case TOK.inout_:
7754             case TOK.final_:
7755             case TOK.auto_:
7756             case TOK.scope_:
7757             case TOK.override_:
7758             case TOK.abstract_:
7759             case TOK.synchronized_:
7760                 break;
7761 
7762             case TOK.deprecated_:
7763                 if (peek(t).value == TOK.leftParenthesis)
7764                 {
7765                     t = peek(t);
7766                     if (!skipParens(t, &t))
7767                         goto Lerror;
7768                     // t is on the next of closing parenthesis
7769                     continue;
7770                 }
7771                 break;
7772 
7773             case TOK.nothrow_:
7774             case TOK.pure_:
7775             case TOK.ref_:
7776             case TOK.gshared:
7777             case TOK.return_:
7778                 break;
7779 
7780             case TOK.at:
7781                 t = peek(t);
7782                 if (t.value == TOK.identifier)
7783                 {
7784                     /* @identifier
7785                      * @identifier!arg
7786                      * @identifier!(arglist)
7787                      * any of the above followed by (arglist)
7788                      * @predefined_attribute
7789                      */
7790                     if (isBuiltinAtAttribute(t.ident))
7791                         break;
7792                     t = peek(t);
7793                     if (t.value == TOK.not)
7794                     {
7795                         t = peek(t);
7796                         if (t.value == TOK.leftParenthesis)
7797                         {
7798                             // @identifier!(arglist)
7799                             if (!skipParens(t, &t))
7800                                 goto Lerror;
7801                             // t is on the next of closing parenthesis
7802                         }
7803                         else
7804                         {
7805                             // @identifier!arg
7806                             // Do low rent skipTemplateArgument
7807                             if (t.value == TOK.vector)
7808                             {
7809                                 // identifier!__vector(type)
7810                                 t = peek(t);
7811                                 if (!skipParens(t, &t))
7812                                     goto Lerror;
7813                             }
7814                             else
7815                                 t = peek(t);
7816                         }
7817                     }
7818                     if (t.value == TOK.leftParenthesis)
7819                     {
7820                         if (!skipParens(t, &t))
7821                             goto Lerror;
7822                         // t is on the next of closing parenthesis
7823                         continue;
7824                     }
7825                     continue;
7826                 }
7827                 if (t.value == TOK.leftParenthesis)
7828                 {
7829                     // @( ArgumentList )
7830                     if (!skipParens(t, &t))
7831                         goto Lerror;
7832                     // t is on the next of closing parenthesis
7833                     continue;
7834                 }
7835                 goto Lerror;
7836 
7837             default:
7838                 goto Ldone;
7839             }
7840             t = peek(t);
7841         }
7842     Ldone:
7843         if (pt)
7844             *pt = t;
7845         return true;
7846 
7847     Lerror:
7848         return false;
7849     }
7850 
7851     AST.Expression parseExpression()
7852     {
7853         auto loc = token.loc;
7854 
7855         //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
7856         auto e = parseAssignExp();
7857         while (token.value == TOK.comma)
7858         {
7859             nextToken();
7860             auto e2 = parseAssignExp();
7861             e = new AST.CommaExp(loc, e, e2, false);
7862             loc = token.loc;
7863         }
7864         return e;
7865     }
7866 
7867     /********************************* Expression Parser ***************************/
7868 
7869     AST.Expression parsePrimaryExp()
7870     {
7871         AST.Expression e;
7872         AST.Type t;
7873         Identifier id;
7874         const loc = token.loc;
7875 
7876         //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
7877         switch (token.value)
7878         {
7879         case TOK.identifier:
7880             {
7881                 if (peekNext() == TOK.arrow)
7882                 {
7883                     // skip `identifier ->`
7884                     nextToken();
7885                     nextToken();
7886                     error("use `.` for member lookup, not `->`");
7887                     goto Lerr;
7888                 }
7889 
7890                 if (peekNext() == TOK.goesTo)
7891                     goto case_delegate;
7892 
7893                 id = token.ident;
7894                 nextToken();
7895                 TOK save;
7896                 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
7897                 {
7898                     // identifier!(template-argument-list)
7899                     auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
7900                     e = new AST.ScopeExp(loc, tempinst);
7901                 }
7902                 else
7903                     e = new AST.IdentifierExp(loc, id);
7904                 break;
7905             }
7906         case TOK.dollar:
7907             if (!inBrackets)
7908                 error("`$` is valid only inside [] of index or slice");
7909             e = new AST.DollarExp(loc);
7910             nextToken();
7911             break;
7912 
7913         case TOK.dot:
7914             // Signal global scope '.' operator with "" identifier
7915             e = new AST.IdentifierExp(loc, Id.empty);
7916             break;
7917 
7918         case TOK.this_:
7919             e = new AST.ThisExp(loc);
7920             nextToken();
7921             break;
7922 
7923         case TOK.super_:
7924             e = new AST.SuperExp(loc);
7925             nextToken();
7926             break;
7927 
7928         case TOK.int32Literal:
7929             e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
7930             nextToken();
7931             break;
7932 
7933         case TOK.uns32Literal:
7934             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
7935             nextToken();
7936             break;
7937 
7938         case TOK.int64Literal:
7939             e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
7940             nextToken();
7941             break;
7942 
7943         case TOK.uns64Literal:
7944             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
7945             nextToken();
7946             break;
7947 
7948         case TOK.float32Literal:
7949             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
7950             nextToken();
7951             break;
7952 
7953         case TOK.float64Literal:
7954             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
7955             nextToken();
7956             break;
7957 
7958         case TOK.float80Literal:
7959             e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
7960             nextToken();
7961             break;
7962 
7963         case TOK.imaginary32Literal:
7964             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
7965             nextToken();
7966             break;
7967 
7968         case TOK.imaginary64Literal:
7969             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
7970             nextToken();
7971             break;
7972 
7973         case TOK.imaginary80Literal:
7974             e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
7975             nextToken();
7976             break;
7977 
7978         case TOK.null_:
7979             e = new AST.NullExp(loc);
7980             nextToken();
7981             break;
7982 
7983         case TOK.file:
7984             {
7985                 const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
7986                 e = new AST.StringExp(loc, s.toDString());
7987                 nextToken();
7988                 break;
7989             }
7990         case TOK.fileFullPath:
7991             {
7992                 assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
7993                 const s = FileName.toAbsolute(loc.filename);
7994                 e = new AST.StringExp(loc, s.toDString());
7995                 nextToken();
7996                 break;
7997             }
7998 
7999         case TOK.line:
8000             e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
8001             nextToken();
8002             break;
8003 
8004         case TOK.moduleString:
8005             {
8006                 const(char)* s = md ? md.toChars() : mod.toChars();
8007                 e = new AST.StringExp(loc, s.toDString());
8008                 nextToken();
8009                 break;
8010             }
8011         case TOK.functionString:
8012             e = new AST.FuncInitExp(loc);
8013             nextToken();
8014             break;
8015 
8016         case TOK.prettyFunction:
8017             e = new AST.PrettyFuncInitExp(loc);
8018             nextToken();
8019             break;
8020 
8021         case TOK.true_:
8022             e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8023             nextToken();
8024             break;
8025 
8026         case TOK.false_:
8027             e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8028             nextToken();
8029             break;
8030 
8031         case TOK.charLiteral:
8032             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8033             nextToken();
8034             break;
8035 
8036         case TOK.wcharLiteral:
8037             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8038             nextToken();
8039             break;
8040 
8041         case TOK.dcharLiteral:
8042             e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8043             nextToken();
8044             break;
8045 
8046         case TOK.string_:
8047         case TOK.hexadecimalString:
8048             {
8049                 // cat adjacent strings
8050                 auto s = token.ustring;
8051                 auto len = token.len;
8052                 auto postfix = token.postfix;
8053                 while (1)
8054                 {
8055                     const prev = token;
8056                     nextToken();
8057                     if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
8058                     {
8059                         if (token.postfix)
8060                         {
8061                             if (token.postfix != postfix)
8062                                 error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8063                             postfix = token.postfix;
8064                         }
8065 
8066                         error("Implicit string concatenation is error-prone and disallowed in D");
8067                         errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8068                              "(concatenating literals is `@nogc`): %s ~ %s",
8069                              prev.toChars(), token.toChars());
8070 
8071                         const len1 = len;
8072                         const len2 = token.len;
8073                         len = len1 + len2;
8074                         auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8075                         memcpy(s2, s, len1 * char.sizeof);
8076                         memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8077                         s = s2;
8078                     }
8079                     else
8080                         break;
8081                 }
8082                 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8083                 break;
8084             }
8085         case TOK.void_:
8086             t = AST.Type.tvoid;
8087             goto LabelX;
8088 
8089         case TOK.int8:
8090             t = AST.Type.tint8;
8091             goto LabelX;
8092 
8093         case TOK.uns8:
8094             t = AST.Type.tuns8;
8095             goto LabelX;
8096 
8097         case TOK.int16:
8098             t = AST.Type.tint16;
8099             goto LabelX;
8100 
8101         case TOK.uns16:
8102             t = AST.Type.tuns16;
8103             goto LabelX;
8104 
8105         case TOK.int32:
8106             t = AST.Type.tint32;
8107             goto LabelX;
8108 
8109         case TOK.uns32:
8110             t = AST.Type.tuns32;
8111             goto LabelX;
8112 
8113         case TOK.int64:
8114             t = AST.Type.tint64;
8115             goto LabelX;
8116 
8117         case TOK.uns64:
8118             t = AST.Type.tuns64;
8119             goto LabelX;
8120 
8121         case TOK.int128:
8122             t = AST.Type.tint128;
8123             goto LabelX;
8124 
8125         case TOK.uns128:
8126             t = AST.Type.tuns128;
8127             goto LabelX;
8128 
8129         case TOK.float32:
8130             t = AST.Type.tfloat32;
8131             goto LabelX;
8132 
8133         case TOK.float64:
8134             t = AST.Type.tfloat64;
8135             goto LabelX;
8136 
8137         case TOK.float80:
8138             t = AST.Type.tfloat80;
8139             goto LabelX;
8140 
8141         case TOK.imaginary32:
8142             t = AST.Type.timaginary32;
8143             goto LabelX;
8144 
8145         case TOK.imaginary64:
8146             t = AST.Type.timaginary64;
8147             goto LabelX;
8148 
8149         case TOK.imaginary80:
8150             t = AST.Type.timaginary80;
8151             goto LabelX;
8152 
8153         case TOK.complex32:
8154             t = AST.Type.tcomplex32;
8155             goto LabelX;
8156 
8157         case TOK.complex64:
8158             t = AST.Type.tcomplex64;
8159             goto LabelX;
8160 
8161         case TOK.complex80:
8162             t = AST.Type.tcomplex80;
8163             goto LabelX;
8164 
8165         case TOK.bool_:
8166             t = AST.Type.tbool;
8167             goto LabelX;
8168 
8169         case TOK.char_:
8170             t = AST.Type.tchar;
8171             goto LabelX;
8172 
8173         case TOK.wchar_:
8174             t = AST.Type.twchar;
8175             goto LabelX;
8176 
8177         case TOK.dchar_:
8178             t = AST.Type.tdchar;
8179             goto LabelX;
8180         LabelX:
8181             nextToken();
8182             if (token.value == TOK.leftParenthesis)
8183             {
8184                 e = new AST.TypeExp(loc, t);
8185                 e = new AST.CallExp(loc, e, parseArguments());
8186                 break;
8187             }
8188             check(TOK.dot, t.toChars());
8189             if (token.value != TOK.identifier)
8190             {
8191                 error("found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8192                 goto Lerr;
8193             }
8194             e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8195             nextToken();
8196             break;
8197 
8198         case TOK.typeof_:
8199             {
8200                 t = parseTypeof();
8201                 e = new AST.TypeExp(loc, t);
8202                 break;
8203             }
8204         case TOK.vector:
8205             {
8206                 t = parseVector();
8207                 e = new AST.TypeExp(loc, t);
8208                 break;
8209             }
8210         case TOK.typeid_:
8211             {
8212                 nextToken();
8213                 check(TOK.leftParenthesis, "`typeid`");
8214                 RootObject o = parseTypeOrAssignExp();
8215                 check(TOK.rightParenthesis);
8216                 e = new AST.TypeidExp(loc, o);
8217                 break;
8218             }
8219         case TOK.traits:
8220             {
8221                 /* __traits(identifier, args...)
8222                  */
8223                 Identifier ident;
8224                 AST.Objects* args = null;
8225 
8226                 nextToken();
8227                 check(TOK.leftParenthesis);
8228                 if (token.value != TOK.identifier)
8229                 {
8230                     error("`__traits(identifier, args...)` expected");
8231                     goto Lerr;
8232                 }
8233                 ident = token.ident;
8234                 nextToken();
8235                 if (token.value == TOK.comma)
8236                     args = parseTemplateArgumentList(); // __traits(identifier, args...)
8237                 else
8238                     check(TOK.rightParenthesis); // __traits(identifier)
8239 
8240                 e = new AST.TraitsExp(loc, ident, args);
8241                 break;
8242             }
8243         case TOK.is_:
8244             {
8245                 AST.Type targ;
8246                 Identifier ident = null;
8247                 AST.Type tspec = null;
8248                 TOK tok = TOK.reserved;
8249                 TOK tok2 = TOK.reserved;
8250                 AST.TemplateParameters* tpl = null;
8251 
8252                 nextToken();
8253                 if (token.value == TOK.leftParenthesis)
8254                 {
8255                     nextToken();
8256                     if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8257                     {
8258                         error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8259                         nextToken();
8260                         Token* tempTok = peekPastParen(&token);
8261                         memcpy(&token, tempTok, Token.sizeof);
8262                         goto Lerr;
8263                     }
8264                     targ = parseType(&ident);
8265                     if (token.value == TOK.colon || token.value == TOK.equal)
8266                     {
8267                         tok = token.value;
8268                         nextToken();
8269                         if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8270                             || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8271                             || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8272                             || token.value == TOK.argumentTypes || token.value == TOK.parameters
8273                             || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8274                             || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8275                             || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8276                             || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8277                             || token.value == TOK.delegate_ || token.value == TOK.return_
8278                             || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8279                         {
8280                             tok2 = token.value;
8281                             nextToken();
8282                         }
8283                         else
8284                         {
8285                             tspec = parseType();
8286                         }
8287                     }
8288                     if (tspec)
8289                     {
8290                         if (token.value == TOK.comma)
8291                             tpl = parseTemplateParameterList(1);
8292                         else
8293                         {
8294                             tpl = new AST.TemplateParameters();
8295                             check(TOK.rightParenthesis);
8296                         }
8297                     }
8298                     else
8299                         check(TOK.rightParenthesis);
8300                 }
8301                 else
8302                 {
8303                     error("`type identifier : specialization` expected following `is`");
8304                     goto Lerr;
8305                 }
8306                 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8307                 break;
8308             }
8309         case TOK.assert_:
8310             {
8311                 // https://dlang.org/spec/expression.html#assert_expressions
8312                 AST.Expression msg = null;
8313 
8314                 nextToken();
8315                 check(TOK.leftParenthesis, "`assert`");
8316                 e = parseAssignExp();
8317                 if (token.value == TOK.comma)
8318                 {
8319                     nextToken();
8320                     if (token.value != TOK.rightParenthesis)
8321                     {
8322                         msg = parseAssignExp();
8323                         if (token.value == TOK.comma)
8324                             nextToken();
8325                     }
8326                 }
8327                 check(TOK.rightParenthesis);
8328                 e = new AST.AssertExp(loc, e, msg);
8329                 break;
8330             }
8331         case TOK.mixin_:
8332             {
8333                 // https://dlang.org/spec/expression.html#mixin_expressions
8334                 nextToken();
8335                 if (token.value != TOK.leftParenthesis)
8336                     error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8337                 auto exps = parseArguments();
8338                 e = new AST.MixinExp(loc, exps);
8339                 break;
8340             }
8341         case TOK.import_:
8342             {
8343                 nextToken();
8344                 check(TOK.leftParenthesis, "`import`");
8345                 e = parseAssignExp();
8346                 check(TOK.rightParenthesis);
8347                 e = new AST.ImportExp(loc, e);
8348                 break;
8349             }
8350         case TOK.new_:
8351             e = parseNewExp(null);
8352             break;
8353 
8354         case TOK.ref_:
8355             {
8356                 if (peekNext() == TOK.leftParenthesis)
8357                 {
8358                     Token* tk = peekPastParen(peek(&token));
8359                     if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8360                     {
8361                         // ref (arguments) => expression
8362                         // ref (arguments) { statements... }
8363                         goto case_delegate;
8364                     }
8365                 }
8366                 nextToken();
8367                 error("found `%s` when expecting function literal following `ref`", token.toChars());
8368                 goto Lerr;
8369             }
8370         case TOK.leftParenthesis:
8371             {
8372                 Token* tk = peekPastParen(&token);
8373                 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8374                 {
8375                     // (arguments) => expression
8376                     // (arguments) { statements... }
8377                     goto case_delegate;
8378                 }
8379 
8380                 // ( expression )
8381                 nextToken();
8382                 e = parseExpression();
8383                 e.parens = 1;
8384                 check(loc, TOK.rightParenthesis);
8385                 break;
8386             }
8387         case TOK.leftBracket:
8388             {
8389                 /* Parse array literals and associative array literals:
8390                  *  [ value, value, value ... ]
8391                  *  [ key:value, key:value, key:value ... ]
8392                  */
8393                 auto values = new AST.Expressions();
8394                 AST.Expressions* keys = null;
8395 
8396                 nextToken();
8397                 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8398                 {
8399                     e = parseAssignExp();
8400                     if (token.value == TOK.colon && (keys || values.dim == 0))
8401                     {
8402                         nextToken();
8403                         if (!keys)
8404                             keys = new AST.Expressions();
8405                         keys.push(e);
8406                         e = parseAssignExp();
8407                     }
8408                     else if (keys)
8409                     {
8410                         error("`key:value` expected for associative array literal");
8411                         keys = null;
8412                     }
8413                     values.push(e);
8414                     if (token.value == TOK.rightBracket)
8415                         break;
8416                     check(TOK.comma);
8417                 }
8418                 check(loc, TOK.rightBracket);
8419 
8420                 if (keys)
8421                     e = new AST.AssocArrayLiteralExp(loc, keys, values);
8422                 else
8423                     e = new AST.ArrayLiteralExp(loc, null, values);
8424                 break;
8425             }
8426         case TOK.leftCurly:
8427         case TOK.function_:
8428         case TOK.delegate_:
8429         case_delegate:
8430             {
8431                 AST.Dsymbol s = parseFunctionLiteral();
8432                 e = new AST.FuncExp(loc, s);
8433                 break;
8434             }
8435         default:
8436             error("expression expected, not `%s`", token.toChars());
8437         Lerr:
8438             // Anything for e, as long as it's not NULL
8439             e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8440             nextToken();
8441             break;
8442         }
8443         return e;
8444     }
8445 
8446     private AST.Expression parseUnaryExp()
8447     {
8448         AST.Expression e;
8449         const loc = token.loc;
8450 
8451         switch (token.value)
8452         {
8453         case TOK.and:
8454             nextToken();
8455             e = parseUnaryExp();
8456             e = new AST.AddrExp(loc, e);
8457             break;
8458 
8459         case TOK.plusPlus:
8460             nextToken();
8461             e = parseUnaryExp();
8462             //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8463             e = new AST.PreExp(TOK.prePlusPlus, loc, e);
8464             break;
8465 
8466         case TOK.minusMinus:
8467             nextToken();
8468             e = parseUnaryExp();
8469             //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8470             e = new AST.PreExp(TOK.preMinusMinus, loc, e);
8471             break;
8472 
8473         case TOK.mul:
8474             nextToken();
8475             e = parseUnaryExp();
8476             e = new AST.PtrExp(loc, e);
8477             break;
8478 
8479         case TOK.min:
8480             nextToken();
8481             e = parseUnaryExp();
8482             e = new AST.NegExp(loc, e);
8483             break;
8484 
8485         case TOK.add:
8486             nextToken();
8487             e = parseUnaryExp();
8488             e = new AST.UAddExp(loc, e);
8489             break;
8490 
8491         case TOK.not:
8492             nextToken();
8493             e = parseUnaryExp();
8494             e = new AST.NotExp(loc, e);
8495             break;
8496 
8497         case TOK.tilde:
8498             nextToken();
8499             e = parseUnaryExp();
8500             e = new AST.ComExp(loc, e);
8501             break;
8502 
8503         case TOK.delete_:
8504             nextToken();
8505             e = parseUnaryExp();
8506             e = new AST.DeleteExp(loc, e, false);
8507             break;
8508 
8509         case TOK.cast_: // cast(type) expression
8510             {
8511                 nextToken();
8512                 check(TOK.leftParenthesis);
8513                 /* Look for cast(), cast(const), cast(immutable),
8514                  * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8515                  */
8516                 ubyte m = 0;
8517                 while (1)
8518                 {
8519                     switch (token.value)
8520                     {
8521                     case TOK.const_:
8522                         if (peekNext() == TOK.leftParenthesis)
8523                             break; // const as type constructor
8524                         m |= MODFlags.const_; // const as storage class
8525                         nextToken();
8526                         continue;
8527 
8528                     case TOK.immutable_:
8529                         if (peekNext() == TOK.leftParenthesis)
8530                             break;
8531                         m |= MODFlags.immutable_;
8532                         nextToken();
8533                         continue;
8534 
8535                     case TOK.shared_:
8536                         if (peekNext() == TOK.leftParenthesis)
8537                             break;
8538                         m |= MODFlags.shared_;
8539                         nextToken();
8540                         continue;
8541 
8542                     case TOK.inout_:
8543                         if (peekNext() == TOK.leftParenthesis)
8544                             break;
8545                         m |= MODFlags.wild;
8546                         nextToken();
8547                         continue;
8548 
8549                     default:
8550                         break;
8551                     }
8552                     break;
8553                 }
8554                 if (token.value == TOK.rightParenthesis)
8555                 {
8556                     nextToken();
8557                     e = parseUnaryExp();
8558                     e = new AST.CastExp(loc, e, m);
8559                 }
8560                 else
8561                 {
8562                     AST.Type t = parseType(); // cast( type )
8563                     t = t.addMod(m); // cast( const type )
8564                     check(TOK.rightParenthesis);
8565                     e = parseUnaryExp();
8566                     e = new AST.CastExp(loc, e, t);
8567                 }
8568                 break;
8569             }
8570         case TOK.inout_:
8571         case TOK.shared_:
8572         case TOK.const_:
8573         case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8574             {
8575                 StorageClass stc = parseTypeCtor();
8576 
8577                 AST.Type t = parseBasicType();
8578                 t = t.addSTC(stc);
8579 
8580                 if (stc == 0 && token.value == TOK.dot)
8581                 {
8582                     nextToken();
8583                     if (token.value != TOK.identifier)
8584                     {
8585                         error("identifier expected following `(type)`.");
8586                         return null;
8587                     }
8588                     e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8589                     nextToken();
8590                     e = parsePostExp(e);
8591                 }
8592                 else
8593                 {
8594                     e = new AST.TypeExp(loc, t);
8595                     if (token.value != TOK.leftParenthesis)
8596                     {
8597                         error("`(arguments)` expected following `%s`", t.toChars());
8598                         return e;
8599                     }
8600                     e = new AST.CallExp(loc, e, parseArguments());
8601                 }
8602                 break;
8603             }
8604         case TOK.leftParenthesis:
8605             {
8606                 auto tk = peek(&token);
8607                 static if (CCASTSYNTAX)
8608                 {
8609                     // If cast
8610                     if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8611                     {
8612                         tk = peek(tk); // skip over right parenthesis
8613                         switch (tk.value)
8614                         {
8615                         case TOK.not:
8616                             tk = peek(tk);
8617                             if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8618                                 break;
8619                             goto case;
8620 
8621                         case TOK.dot:
8622                         case TOK.plusPlus:
8623                         case TOK.minusMinus:
8624                         case TOK.delete_:
8625                         case TOK.new_:
8626                         case TOK.leftParenthesis:
8627                         case TOK.identifier:
8628                         case TOK.this_:
8629                         case TOK.super_:
8630                         case TOK.int32Literal:
8631                         case TOK.uns32Literal:
8632                         case TOK.int64Literal:
8633                         case TOK.uns64Literal:
8634                         case TOK.int128Literal:
8635                         case TOK.uns128Literal:
8636                         case TOK.float32Literal:
8637                         case TOK.float64Literal:
8638                         case TOK.float80Literal:
8639                         case TOK.imaginary32Literal:
8640                         case TOK.imaginary64Literal:
8641                         case TOK.imaginary80Literal:
8642                         case TOK.null_:
8643                         case TOK.true_:
8644                         case TOK.false_:
8645                         case TOK.charLiteral:
8646                         case TOK.wcharLiteral:
8647                         case TOK.dcharLiteral:
8648                         case TOK.string_:
8649                             version (none)
8650                             {
8651                             case TOK.tilde:
8652                             case TOK.and:
8653                             case TOK.mul:
8654                             case TOK.min:
8655                             case TOK.add:
8656                             }
8657                         case TOK.function_:
8658                         case TOK.delegate_:
8659                         case TOK.typeof_:
8660                         case TOK.traits:
8661                         case TOK.vector:
8662                         case TOK.file:
8663                         case TOK.fileFullPath:
8664                         case TOK.line:
8665                         case TOK.moduleString:
8666                         case TOK.functionString:
8667                         case TOK.prettyFunction:
8668                         case TOK.wchar_:
8669                         case TOK.dchar_:
8670                         case TOK.bool_:
8671                         case TOK.char_:
8672                         case TOK.int8:
8673                         case TOK.uns8:
8674                         case TOK.int16:
8675                         case TOK.uns16:
8676                         case TOK.int32:
8677                         case TOK.uns32:
8678                         case TOK.int64:
8679                         case TOK.uns64:
8680                         case TOK.int128:
8681                         case TOK.uns128:
8682                         case TOK.float32:
8683                         case TOK.float64:
8684                         case TOK.float80:
8685                         case TOK.imaginary32:
8686                         case TOK.imaginary64:
8687                         case TOK.imaginary80:
8688                         case TOK.complex32:
8689                         case TOK.complex64:
8690                         case TOK.complex80:
8691                         case TOK.void_:
8692                             {
8693                                 // (type) una_exp
8694                                 nextToken();
8695                                 auto t = parseType();
8696                                 check(TOK.rightParenthesis);
8697 
8698                                 // if .identifier
8699                                 // or .identifier!( ... )
8700                                 if (token.value == TOK.dot)
8701                                 {
8702                                     if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8703                                     {
8704                                         error("identifier or new keyword expected following `(...)`.");
8705                                         return null;
8706                                     }
8707                                     e = new AST.TypeExp(loc, t);
8708                                     e.parens = true;
8709                                     e = parsePostExp(e);
8710                                 }
8711                                 else
8712                                 {
8713                                     e = parseUnaryExp();
8714                                     e = new AST.CastExp(loc, e, t);
8715                                     error("C style cast illegal, use `%s`", e.toChars());
8716                                 }
8717                                 return e;
8718                             }
8719                         default:
8720                             break;
8721                         }
8722                     }
8723                 }
8724                 e = parsePrimaryExp();
8725                 e = parsePostExp(e);
8726                 break;
8727             }
8728         default:
8729             e = parsePrimaryExp();
8730             e = parsePostExp(e);
8731             break;
8732         }
8733         assert(e);
8734 
8735         // ^^ is right associative and has higher precedence than the unary operators
8736         while (token.value == TOK.pow)
8737         {
8738             nextToken();
8739             AST.Expression e2 = parseUnaryExp();
8740             e = new AST.PowExp(loc, e, e2);
8741         }
8742 
8743         return e;
8744     }
8745 
8746     private AST.Expression parsePostExp(AST.Expression e)
8747     {
8748         while (1)
8749         {
8750             const loc = token.loc;
8751             switch (token.value)
8752             {
8753             case TOK.dot:
8754                 nextToken();
8755                 if (token.value == TOK.identifier)
8756                 {
8757                     Identifier id = token.ident;
8758 
8759                     nextToken();
8760                     if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8761                     {
8762                         AST.Objects* tiargs = parseTemplateArguments();
8763                         e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8764                     }
8765                     else
8766                         e = new AST.DotIdExp(loc, e, id);
8767                     continue;
8768                 }
8769                 if (token.value == TOK.new_)
8770                 {
8771                     e = parseNewExp(e);
8772                     continue;
8773                 }
8774                 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8775                 break;
8776 
8777             case TOK.plusPlus:
8778                 e = new AST.PostExp(TOK.plusPlus, loc, e);
8779                 break;
8780 
8781             case TOK.minusMinus:
8782                 e = new AST.PostExp(TOK.minusMinus, loc, e);
8783                 break;
8784 
8785             case TOK.leftParenthesis:
8786                 e = new AST.CallExp(loc, e, parseArguments());
8787                 continue;
8788 
8789             case TOK.leftBracket:
8790                 {
8791                     // array dereferences:
8792                     //      array[index]
8793                     //      array[]
8794                     //      array[lwr .. upr]
8795                     AST.Expression index;
8796                     AST.Expression upr;
8797                     auto arguments = new AST.Expressions();
8798 
8799                     inBrackets++;
8800                     nextToken();
8801                     while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8802                     {
8803                         index = parseAssignExp();
8804                         if (token.value == TOK.slice)
8805                         {
8806                             // array[..., lwr..upr, ...]
8807                             nextToken();
8808                             upr = parseAssignExp();
8809                             arguments.push(new AST.IntervalExp(loc, index, upr));
8810                         }
8811                         else
8812                             arguments.push(index);
8813                         if (token.value == TOK.rightBracket)
8814                             break;
8815                         check(TOK.comma);
8816                     }
8817                     check(TOK.rightBracket);
8818                     inBrackets--;
8819                     e = new AST.ArrayExp(loc, e, arguments);
8820                     continue;
8821                 }
8822             default:
8823                 return e;
8824             }
8825             nextToken();
8826         }
8827     }
8828 
8829     private AST.Expression parseMulExp()
8830     {
8831         const loc = token.loc;
8832         auto e = parseUnaryExp();
8833 
8834         while (1)
8835         {
8836             switch (token.value)
8837             {
8838             case TOK.mul:
8839                 nextToken();
8840                 auto e2 = parseUnaryExp();
8841                 e = new AST.MulExp(loc, e, e2);
8842                 continue;
8843 
8844             case TOK.div:
8845                 nextToken();
8846                 auto e2 = parseUnaryExp();
8847                 e = new AST.DivExp(loc, e, e2);
8848                 continue;
8849 
8850             case TOK.mod:
8851                 nextToken();
8852                 auto e2 = parseUnaryExp();
8853                 e = new AST.ModExp(loc, e, e2);
8854                 continue;
8855 
8856             default:
8857                 break;
8858             }
8859             break;
8860         }
8861         return e;
8862     }
8863 
8864     private AST.Expression parseAddExp()
8865     {
8866         const loc = token.loc;
8867         auto e = parseMulExp();
8868 
8869         while (1)
8870         {
8871             switch (token.value)
8872             {
8873             case TOK.add:
8874                 nextToken();
8875                 auto e2 = parseMulExp();
8876                 e = new AST.AddExp(loc, e, e2);
8877                 continue;
8878 
8879             case TOK.min:
8880                 nextToken();
8881                 auto e2 = parseMulExp();
8882                 e = new AST.MinExp(loc, e, e2);
8883                 continue;
8884 
8885             case TOK.tilde:
8886                 nextToken();
8887                 auto e2 = parseMulExp();
8888                 e = new AST.CatExp(loc, e, e2);
8889                 continue;
8890 
8891             default:
8892                 break;
8893             }
8894             break;
8895         }
8896         return e;
8897     }
8898 
8899     private AST.Expression parseShiftExp()
8900     {
8901         const loc = token.loc;
8902         auto e = parseAddExp();
8903 
8904         while (1)
8905         {
8906             switch (token.value)
8907             {
8908             case TOK.leftShift:
8909                 nextToken();
8910                 auto e2 = parseAddExp();
8911                 e = new AST.ShlExp(loc, e, e2);
8912                 continue;
8913 
8914             case TOK.rightShift:
8915                 nextToken();
8916                 auto e2 = parseAddExp();
8917                 e = new AST.ShrExp(loc, e, e2);
8918                 continue;
8919 
8920             case TOK.unsignedRightShift:
8921                 nextToken();
8922                 auto e2 = parseAddExp();
8923                 e = new AST.UshrExp(loc, e, e2);
8924                 continue;
8925 
8926             default:
8927                 break;
8928             }
8929             break;
8930         }
8931         return e;
8932     }
8933 
8934     private AST.Expression parseCmpExp()
8935     {
8936         const loc = token.loc;
8937 
8938         auto e = parseShiftExp();
8939         TOK op = token.value;
8940 
8941         switch (op)
8942         {
8943         case TOK.equal:
8944         case TOK.notEqual:
8945             nextToken();
8946             auto e2 = parseShiftExp();
8947             e = new AST.EqualExp(op, loc, e, e2);
8948             break;
8949 
8950         case TOK.is_:
8951             op = TOK.identity;
8952             goto L1;
8953 
8954         case TOK.not:
8955         {
8956             // Attempt to identify '!is'
8957             const tv = peekNext();
8958             if (tv == TOK.in_)
8959             {
8960                 nextToken();
8961                 nextToken();
8962                 auto e2 = parseShiftExp();
8963                 e = new AST.InExp(loc, e, e2);
8964                 e = new AST.NotExp(loc, e);
8965                 break;
8966             }
8967             if (tv != TOK.is_)
8968                 break;
8969             nextToken();
8970             op = TOK.notIdentity;
8971             goto L1;
8972         }
8973         L1:
8974             nextToken();
8975             auto e2 = parseShiftExp();
8976             e = new AST.IdentityExp(op, loc, e, e2);
8977             break;
8978 
8979         case TOK.lessThan:
8980         case TOK.lessOrEqual:
8981         case TOK.greaterThan:
8982         case TOK.greaterOrEqual:
8983             nextToken();
8984             auto e2 = parseShiftExp();
8985             e = new AST.CmpExp(op, loc, e, e2);
8986             break;
8987 
8988         case TOK.in_:
8989             nextToken();
8990             auto e2 = parseShiftExp();
8991             e = new AST.InExp(loc, e, e2);
8992             break;
8993 
8994         default:
8995             break;
8996         }
8997         return e;
8998     }
8999 
9000     private AST.Expression parseAndExp()
9001     {
9002         Loc loc = token.loc;
9003         auto e = parseCmpExp();
9004         while (token.value == TOK.and)
9005         {
9006             checkParens(TOK.and, e);
9007             nextToken();
9008             auto e2 = parseCmpExp();
9009             checkParens(TOK.and, e2);
9010             e = new AST.AndExp(loc, e, e2);
9011             loc = token.loc;
9012         }
9013         return e;
9014     }
9015 
9016     private AST.Expression parseXorExp()
9017     {
9018         const loc = token.loc;
9019 
9020         auto e = parseAndExp();
9021         while (token.value == TOK.xor)
9022         {
9023             checkParens(TOK.xor, e);
9024             nextToken();
9025             auto e2 = parseAndExp();
9026             checkParens(TOK.xor, e2);
9027             e = new AST.XorExp(loc, e, e2);
9028         }
9029         return e;
9030     }
9031 
9032     private AST.Expression parseOrExp()
9033     {
9034         const loc = token.loc;
9035 
9036         auto e = parseXorExp();
9037         while (token.value == TOK.or)
9038         {
9039             checkParens(TOK.or, e);
9040             nextToken();
9041             auto e2 = parseXorExp();
9042             checkParens(TOK.or, e2);
9043             e = new AST.OrExp(loc, e, e2);
9044         }
9045         return e;
9046     }
9047 
9048     private AST.Expression parseAndAndExp()
9049     {
9050         const loc = token.loc;
9051 
9052         auto e = parseOrExp();
9053         while (token.value == TOK.andAnd)
9054         {
9055             nextToken();
9056             auto e2 = parseOrExp();
9057             e = new AST.LogicalExp(loc, TOK.andAnd, e, e2);
9058         }
9059         return e;
9060     }
9061 
9062     private AST.Expression parseOrOrExp()
9063     {
9064         const loc = token.loc;
9065 
9066         auto e = parseAndAndExp();
9067         while (token.value == TOK.orOr)
9068         {
9069             nextToken();
9070             auto e2 = parseAndAndExp();
9071             e = new AST.LogicalExp(loc, TOK.orOr, e, e2);
9072         }
9073         return e;
9074     }
9075 
9076     private AST.Expression parseCondExp()
9077     {
9078         const loc = token.loc;
9079 
9080         auto e = parseOrOrExp();
9081         if (token.value == TOK.question)
9082         {
9083             nextToken();
9084             auto e1 = parseExpression();
9085             check(TOK.colon);
9086             auto e2 = parseCondExp();
9087             e = new AST.CondExp(loc, e, e1, e2);
9088         }
9089         return e;
9090     }
9091 
9092     AST.Expression parseAssignExp()
9093     {
9094         AST.Expression e;
9095         e = parseCondExp();
9096         if (e is null)
9097             return e;
9098 
9099         // require parens for e.g. `t ? a = 1 : b = 2`
9100         if (e.op == TOK.question && !e.parens && precedence[token.value] == PREC.assign)
9101             dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9102                 e.toChars(), Token.toChars(token.value));
9103 
9104         const loc = token.loc;
9105         switch (token.value)
9106         {
9107         case TOK.assign:
9108             nextToken();
9109             auto e2 = parseAssignExp();
9110             e = new AST.AssignExp(loc, e, e2);
9111             break;
9112 
9113         case TOK.addAssign:
9114             nextToken();
9115             auto e2 = parseAssignExp();
9116             e = new AST.AddAssignExp(loc, e, e2);
9117             break;
9118 
9119         case TOK.minAssign:
9120             nextToken();
9121             auto e2 = parseAssignExp();
9122             e = new AST.MinAssignExp(loc, e, e2);
9123             break;
9124 
9125         case TOK.mulAssign:
9126             nextToken();
9127             auto e2 = parseAssignExp();
9128             e = new AST.MulAssignExp(loc, e, e2);
9129             break;
9130 
9131         case TOK.divAssign:
9132             nextToken();
9133             auto e2 = parseAssignExp();
9134             e = new AST.DivAssignExp(loc, e, e2);
9135             break;
9136 
9137         case TOK.modAssign:
9138             nextToken();
9139             auto e2 = parseAssignExp();
9140             e = new AST.ModAssignExp(loc, e, e2);
9141             break;
9142 
9143         case TOK.powAssign:
9144             nextToken();
9145             auto e2 = parseAssignExp();
9146             e = new AST.PowAssignExp(loc, e, e2);
9147             break;
9148 
9149         case TOK.andAssign:
9150             nextToken();
9151             auto e2 = parseAssignExp();
9152             e = new AST.AndAssignExp(loc, e, e2);
9153             break;
9154 
9155         case TOK.orAssign:
9156             nextToken();
9157             auto e2 = parseAssignExp();
9158             e = new AST.OrAssignExp(loc, e, e2);
9159             break;
9160 
9161         case TOK.xorAssign:
9162             nextToken();
9163             auto e2 = parseAssignExp();
9164             e = new AST.XorAssignExp(loc, e, e2);
9165             break;
9166 
9167         case TOK.leftShiftAssign:
9168             nextToken();
9169             auto e2 = parseAssignExp();
9170             e = new AST.ShlAssignExp(loc, e, e2);
9171             break;
9172 
9173         case TOK.rightShiftAssign:
9174             nextToken();
9175             auto e2 = parseAssignExp();
9176             e = new AST.ShrAssignExp(loc, e, e2);
9177             break;
9178 
9179         case TOK.unsignedRightShiftAssign:
9180             nextToken();
9181             auto e2 = parseAssignExp();
9182             e = new AST.UshrAssignExp(loc, e, e2);
9183             break;
9184 
9185         case TOK.concatenateAssign:
9186             nextToken();
9187             auto e2 = parseAssignExp();
9188             e = new AST.CatAssignExp(loc, e, e2);
9189             break;
9190 
9191         default:
9192             break;
9193         }
9194 
9195         return e;
9196     }
9197 
9198     /*************************
9199      * Collect argument list.
9200      * Assume current token is ',', '$(LPAREN)' or '['.
9201      */
9202     private AST.Expressions* parseArguments()
9203     {
9204         // function call
9205         AST.Expressions* arguments;
9206 
9207         arguments = new AST.Expressions();
9208         const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9209 
9210         nextToken();
9211 
9212         while (token.value != endtok && token.value != TOK.endOfFile)
9213         {
9214             auto arg = parseAssignExp();
9215             arguments.push(arg);
9216             if (token.value != TOK.comma)
9217                 break;
9218 
9219             nextToken(); //comma
9220         }
9221 
9222         check(endtok);
9223 
9224         return arguments;
9225     }
9226 
9227     /*******************************************
9228      */
9229     private AST.Expression parseNewExp(AST.Expression thisexp)
9230     {
9231         const loc = token.loc;
9232 
9233         nextToken();
9234         AST.Expressions* newargs = null;
9235         AST.Expressions* arguments = null;
9236         if (token.value == TOK.leftParenthesis)
9237         {
9238             newargs = parseArguments();
9239         }
9240 
9241         // An anonymous nested class starts with "class"
9242         if (token.value == TOK.class_)
9243         {
9244             nextToken();
9245             if (token.value == TOK.leftParenthesis)
9246                 arguments = parseArguments();
9247 
9248             AST.BaseClasses* baseclasses = null;
9249             if (token.value != TOK.leftCurly)
9250                 baseclasses = parseBaseClasses();
9251 
9252             Identifier id = null;
9253             AST.Dsymbols* members = null;
9254 
9255             if (token.value != TOK.leftCurly)
9256             {
9257                 error("`{ members }` expected for anonymous class");
9258             }
9259             else
9260             {
9261                 nextToken();
9262                 members = parseDeclDefs(0);
9263                 if (token.value != TOK.rightCurly)
9264                     error("class member expected");
9265                 nextToken();
9266             }
9267 
9268             auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9269             auto e = new AST.NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
9270             return e;
9271         }
9272 
9273         const stc = parseTypeCtor();
9274         auto t = parseBasicType(true);
9275         t = parseTypeSuffixes(t);
9276         t = t.addSTC(stc);
9277         if (t.ty == Taarray)
9278         {
9279             AST.TypeAArray taa = cast(AST.TypeAArray)t;
9280             AST.Type index = taa.index;
9281             auto edim = AST.typeToExpression(index);
9282             if (!edim)
9283             {
9284                 error("cannot create a `%s` with `new`", t.toChars);
9285                 return new AST.NullExp(loc);
9286             }
9287             t = new AST.TypeSArray(taa.next, edim);
9288         }
9289         else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9290         {
9291             arguments = parseArguments();
9292         }
9293 
9294         auto e = new AST.NewExp(loc, thisexp, newargs, t, arguments);
9295         return e;
9296     }
9297 
9298     /**********************************************
9299      */
9300     private void addComment(AST.Dsymbol s, const(char)* blockComment)
9301     {
9302         if (s !is null)
9303             this.addComment(s, blockComment.toDString());
9304     }
9305 
9306     private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9307     {
9308         if (s !is null)
9309         {
9310             s.addComment(combineComments(blockComment, token.lineComment, true));
9311             token.lineComment = null;
9312         }
9313     }
9314 
9315     /**********************************************
9316      * Recognize builtin @ attributes
9317      * Params:
9318      *  ident = identifier
9319      * Returns:
9320      *  storage class for attribute, 0 if not
9321      */
9322     static StorageClass isBuiltinAtAttribute(Identifier ident)
9323     {
9324         return (ident == Id.property) ? STC.property :
9325                (ident == Id.nogc)     ? STC.nogc     :
9326                (ident == Id.safe)     ? STC.safe     :
9327                (ident == Id.trusted)  ? STC.trusted  :
9328                (ident == Id.system)   ? STC.system   :
9329                (ident == Id.live)     ? STC.live     :
9330                (ident == Id.future)   ? STC.future   :
9331                (ident == Id.disable)  ? STC.disable  :
9332                0;
9333     }
9334 
9335     enum StorageClass atAttrGroup =
9336                 STC.property |
9337                 STC.nogc     |
9338                 STC.safe     |
9339                 STC.trusted  |
9340                 STC.system   |
9341                 STC.live     |
9342                 /*STC.future   |*/ // probably should be included
9343                 STC.disable;
9344     }
9345 
9346 enum PREC : int
9347 {
9348     zero,
9349     expr,
9350     assign,
9351     cond,
9352     oror,
9353     andand,
9354     or,
9355     xor,
9356     and,
9357     equal,
9358     rel,
9359     shift,
9360     add,
9361     mul,
9362     pow,
9363     unary,
9364     primary,
9365 }
9366