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