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