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