1
/**
2
 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_parse.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d
12
 */
13

14
module dmd.parse;
15

16
import core.stdc.stdio;
17
import core.stdc.string;
18
import dmd.globals;
19
import dmd.id;
20
import dmd.identifier;
21
import dmd.lexer;
22
import dmd.errors;
23
import dmd.root.filename;
24
import dmd.root.outbuffer;
25
import dmd.root.rmem;
26
import dmd.root.rootobject;
27
import dmd.root.string;
28
import dmd.tokens;
29

30
// How multiple declarations are parsed.
31
// If 1, treat as C.
32
// If 0, treat:
33
//      int *p, i;
34
// as:
35
//      int* p;
36
//      int* i;
37
private enum CDECLSYNTAX = 0;
38

39
// Support C cast syntax:
40
//      (type)(expression)
41
private enum CCASTSYNTAX = 1;
42

43
// Support postfix C array declarations, such as
44
//      int a[3][4];
45
private enum CARRAYDECL = 1;
46

47
/**********************************
48
 * Set operator precedence for each operator.
49
 *
50
 * Used by hdrgen
51
 */
52
immutable PREC[TOK.max_] precedence =
53
[
54
    TOK.type : PREC.expr,
55
    TOK.error : PREC.expr,
56
    TOK.objcClassReference : PREC.expr, // Objective-C class reference, same as TOK.type
57

58
    TOK.typeof_ : PREC.primary,
59
    TOK.mixin_ : PREC.primary,
60

61
    TOK.import_ : PREC.primary,
62
    TOK.dotVariable : PREC.primary,
63
    TOK.scope_ : PREC.primary,
64
    TOK.identifier : PREC.primary,
65
    TOK.this_ : PREC.primary,
66
    TOK.super_ : PREC.primary,
67
    TOK.int64 : PREC.primary,
68
    TOK.float64 : PREC.primary,
69
    TOK.complex80 : PREC.primary,
70
    TOK.null_ : PREC.primary,
71
    TOK.string_ : PREC.primary,
72
    TOK.arrayLiteral : PREC.primary,
73
    TOK.assocArrayLiteral : PREC.primary,
74
    TOK.classReference : PREC.primary,
75
    TOK.file : PREC.primary,
76
    TOK.fileFullPath : PREC.primary,
77
    TOK.line : PREC.primary,
78
    TOK.moduleString : PREC.primary,
79
    TOK.functionString : PREC.primary,
80
    TOK.prettyFunction : PREC.primary,
81
    TOK.typeid_ : PREC.primary,
82
    TOK.is_ : PREC.primary,
83
    TOK.assert_ : PREC.primary,
84
    TOK.halt : PREC.primary,
85
    TOK.template_ : PREC.primary,
86
    TOK.dSymbol : PREC.primary,
87
    TOK.function_ : PREC.primary,
88
    TOK.variable : PREC.primary,
89
    TOK.symbolOffset : PREC.primary,
90
    TOK.structLiteral : PREC.primary,
91
    TOK.arrayLength : PREC.primary,
92
    TOK.delegatePointer : PREC.primary,
93
    TOK.delegateFunctionPointer : PREC.primary,
94
    TOK.remove : PREC.primary,
95
    TOK.tuple : PREC.primary,
96
    TOK.traits : PREC.primary,
97
    TOK.default_ : PREC.primary,
98
    TOK.overloadSet : PREC.primary,
99
    TOK.void_ : PREC.primary,
100
    TOK.vectorArray : PREC.primary,
101

102
    // post
103
    TOK.dotTemplateInstance : PREC.primary,
104
    TOK.dotIdentifier : PREC.primary,
105
    TOK.dotTemplateDeclaration : PREC.primary,
106
    TOK.dot : PREC.primary,
107
    TOK.dotType : PREC.primary,
108
    TOK.plusPlus : PREC.primary,
109
    TOK.minusMinus : PREC.primary,
110
    TOK.prePlusPlus : PREC.primary,
111
    TOK.preMinusMinus : PREC.primary,
112
    TOK.call : PREC.primary,
113
    TOK.slice : PREC.primary,
114
    TOK.array : PREC.primary,
115
    TOK.index : PREC.primary,
116

117
    TOK.delegate_ : PREC.unary,
118
    TOK.address : PREC.unary,
119
    TOK.star : PREC.unary,
120
    TOK.negate : PREC.unary,
121
    TOK.uadd : PREC.unary,
122
    TOK.not : PREC.unary,
123
    TOK.tilde : PREC.unary,
124
    TOK.delete_ : PREC.unary,
125
    TOK.new_ : PREC.unary,
126
    TOK.newAnonymousClass : PREC.unary,
127
    TOK.cast_ : PREC.unary,
128

129
    TOK.vector : PREC.unary,
130
    TOK.pow : PREC.pow,
131

132
    TOK.mul : PREC.mul,
133
    TOK.div : PREC.mul,
134
    TOK.mod : PREC.mul,
135

136
    TOK.add : PREC.add,
137
    TOK.min : PREC.add,
138
    TOK.concatenate : PREC.add,
139

140
    TOK.leftShift : PREC.shift,
141
    TOK.rightShift : PREC.shift,
142
    TOK.unsignedRightShift : PREC.shift,
143

144
    TOK.lessThan : PREC.rel,
145
    TOK.lessOrEqual : PREC.rel,
146
    TOK.greaterThan : PREC.rel,
147
    TOK.greaterOrEqual : PREC.rel,
148
    TOK.in_ : PREC.rel,
149

150
    /* Note that we changed precedence, so that < and != have the same
151
     * precedence. This change is in the parser, too.
152
     */
153
    TOK.equal : PREC.rel,
154
    TOK.notEqual : PREC.rel,
155
    TOK.identity : PREC.rel,
156
    TOK.notIdentity : PREC.rel,
157

158
    TOK.and : PREC.and,
159
    TOK.xor : PREC.xor,
160
    TOK.or : PREC.or,
161

162
    TOK.andAnd : PREC.andand,
163
    TOK.orOr : PREC.oror,
164

165
    TOK.question : PREC.cond,
166

167
    TOK.assign : PREC.assign,
168
    TOK.construct : PREC.assign,
169
    TOK.blit : PREC.assign,
170
    TOK.addAssign : PREC.assign,
171
    TOK.minAssign : PREC.assign,
172
    TOK.concatenateAssign : PREC.assign,
173
    TOK.concatenateElemAssign : PREC.assign,
174
    TOK.concatenateDcharAssign : PREC.assign,
175
    TOK.mulAssign : PREC.assign,
176
    TOK.divAssign : PREC.assign,
177
    TOK.modAssign : PREC.assign,
178
    TOK.powAssign : PREC.assign,
179
    TOK.leftShiftAssign : PREC.assign,
180
    TOK.rightShiftAssign : PREC.assign,
181
    TOK.unsignedRightShiftAssign : PREC.assign,
182
    TOK.andAssign : PREC.assign,
183
    TOK.orAssign : PREC.assign,
184
    TOK.xorAssign : PREC.assign,
185

186
    TOK.comma : PREC.expr,
187
    TOK.declaration : PREC.expr,
188

189
    TOK.interval : PREC.assign,
190
];
191

192
enum ParseStatementFlags : int
193
{
194
    semi          = 1,        // empty ';' statements are allowed, but deprecated
195
    scope_        = 2,        // start a new scope
196
    curly         = 4,        // { } statement is required
197
    curlyScope    = 8,        // { } starts a new scope
198
    semiOk        = 0x10,     // empty ';' are really ok
199
}
200

201
private struct PrefixAttributes(AST)
202
{
203
    StorageClass storageClass;
204
    AST.Expression depmsg;
205
    LINK link;
206
    AST.Prot protection;
207
    bool setAlignment;
208
    AST.Expression ealign;
209
    AST.Expressions* udas;
210
    const(char)* comment;
211
}
212

213
/*****************************
214
 * Destructively extract storage class from pAttrs.
215
 */
216
private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
217
{
218 1
    StorageClass stc = AST.STC.undefined_;
219 1
    if (pAttrs)
220
    {
221 1
        stc = pAttrs.storageClass;
222 1
        pAttrs.storageClass = AST.STC.undefined_;
223
    }
224 1
    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 1
    if (!global.params.mixinOut)
233 1
        return false;
234

235 1
    OutBuffer* ob = global.params.mixinOut;
236

237 1
    ob.writestring("// expansion at ");
238 1
    ob.writestring(loc.toChars());
239 1
    ob.writenl();
240

241 1
    global.params.mixinLines++;
242

243 1
    loc = Loc(global.params.mixinFile, global.params.mixinLines + 1, loc.charnum);
244

245
    // write by line to create consistent line endings
246 1
    size_t lastpos = 0;
247 1
    for (size_t i = 0; i < s.length; ++i)
248
    {
249
        // detect LF and CRLF
250 1
        const c = s[i];
251 1
        if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
252
        {
253 1
            ob.writestring(s[lastpos .. i]);
254 1
            ob.writenl();
255 1
            global.params.mixinLines++;
256 1
            if (c == '\r')
257 1
                ++i;
258 1
            lastpos = i + 1;
259
        }
260
    }
261

262 1
    if(lastpos < s.length)
263 1
        ob.writestring(s[lastpos .. $]);
264

265 1
    if (s.length == 0 || s[$-1] != '\n')
266
    {
267 1
        ob.writenl(); // ensure empty line after expansion
268 1
        global.params.mixinLines++;
269
    }
270 1
    ob.writenl();
271 1
    global.params.mixinLines++;
272

273 1
    return true;
274
}
275

276
/***********************************************************
277
 */
278
final class Parser(AST) : Lexer
279
{
280
    AST.ModuleDeclaration* md;
281
    alias STC = AST.STC;
282

283
    private
284
    {
285
        AST.Module mod;
286
        LINK linkage;
287
        CPPMANGLE cppmangle;
288
        Loc endloc; // set to location of last right curly
289
        int inBrackets; // inside [] of array index or slice
290
        Loc lookingForElse; // location of lonely if looking for an else
291
    }
292

293
    /*********************
294
     * Use this constructor for string mixins.
295
     * Input:
296
     *      loc     location in source file of mixin
297
     */
298 1
    extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment)
299
    {
300 1
        super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
301

302
        //printf("Parser::Parser()\n");
303 1
        scanloc = loc;
304

305 1
        if (!writeMixin(input, scanloc) && loc.filename)
306
        {
307
            /* Create a pseudo-filename for the mixin string, as it may not even exist
308
             * in the source file.
309
             */
310 1
            char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1);
311 1
            sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
312 1
            scanloc.filename = filename;
313
        }
314

315 1
        mod = _module;
316 1
        linkage = LINK.d;
317
        //nextToken();              // start up the scanner
318
    }
319

320 1
    extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment)
321
    {
322 1
        super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
323

324
        //printf("Parser::Parser()\n");
325 1
        mod = _module;
326 1
        linkage = LINK.d;
327
        //nextToken();              // start up the scanner
328
    }
329

330
    AST.Dsymbols* parseModule()
331
    {
332 1
        const comment = token.blockComment;
333 1
        bool isdeprecated = false;
334 1
        AST.Expression msg = null;
335 1
        AST.Expressions* udas = null;
336 1
        AST.Dsymbols* decldefs;
337 1
        AST.Dsymbol lastDecl = mod; // for attaching ddoc unittests to module decl
338

339 1
        Token* tk;
340 1
        if (skipAttributes(&token, &tk) && tk.value == TOK.module_)
341
        {
342 1
            while (token.value != TOK.module_)
343
            {
344 1
                switch (token.value)
345
                {
346 1
                case TOK.deprecated_:
347
                    {
348
                        // deprecated (...) module ...
349 1
                        if (isdeprecated)
350 0
                            error("there is only one deprecation attribute allowed for module declaration");
351 1
                        isdeprecated = true;
352 1
                        nextToken();
353 1
                        if (token.value == TOK.leftParentheses)
354
                        {
355 1
                            check(TOK.leftParentheses);
356 1
                            msg = parseAssignExp();
357 1
                            check(TOK.rightParentheses);
358
                        }
359 1
                        break;
360
                    }
361 1
                case TOK.at:
362
                    {
363 1
                        AST.Expressions* exps = null;
364 1
                        const stc = parseAttribute(&exps);
365 1
                        if (stc & atAttrGroup)
366
                        {
367 0
                            error("`@%s` attribute for module declaration is not supported", token.toChars());
368
                        }
369
                        else
370
                        {
371 1
                            udas = AST.UserAttributeDeclaration.concat(udas, exps);
372
                        }
373 1
                        if (stc)
374 0
                            nextToken();
375 1
                        break;
376
                    }
377 0
                default:
378
                    {
379 0
                        error("`module` expected instead of `%s`", token.toChars());
380 0
                        nextToken();
381 0
                        break;
382
                    }
383
                }
384
            }
385
        }
386

387 1
        if (udas)
388
        {
389 1
            auto a = new AST.Dsymbols();
390 1
            auto udad = new AST.UserAttributeDeclaration(udas, a);
391 1
            mod.userAttribDecl = udad;
392
        }
393

394
        // ModuleDeclation leads off
395 1
        if (token.value == TOK.module_)
396
        {
397 1
            const loc = token.loc;
398

399 1
            nextToken();
400 1
            if (token.value != TOK.identifier)
401
            {
402 0
                error("identifier expected following `module`");
403 0
                goto Lerr;
404
            }
405

406 1
            AST.Identifiers* a = null;
407 1
            Identifier id = token.ident;
408

409 1
            while (nextToken() == TOK.dot)
410
            {
411 1
                if (!a)
412 1
                    a = new AST.Identifiers();
413 1
                a.push(id);
414 1
                nextToken();
415 1
                if (token.value != TOK.identifier)
416
                {
417 0
                    error("identifier expected following `package`");
418 0
                    goto Lerr;
419
                }
420 1
                id = token.ident;
421
            }
422

423 1
            md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
424

425 1
            if (token.value != TOK.semicolon)
426 0
                error("`;` expected following module declaration instead of `%s`", token.toChars());
427 1
            nextToken();
428 1
            addComment(mod, comment);
429
        }
430

431 1
        decldefs = parseDeclDefs(0, &lastDecl);
432 1
        if (token.value != TOK.endOfFile)
433
        {
434 1
            error(token.loc, "unrecognized declaration");
435 1
            goto Lerr;
436
        }
437 1
        return decldefs;
438

439
    Lerr:
440 1
        while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
441 1
            nextToken();
442 1
        nextToken();
443 1
        return new AST.Dsymbols();
444
    }
445

446
    /**
447
     * Parses a `deprecated` declaration
448
     *
449
     * Params:
450
     *   msg = Deprecated message, if any.
451
     *         Used to support overriding a deprecated storage class with
452
     *         a deprecated declaration with a message, but to error
453
     *         if both declaration have a message.
454
     *
455
     * Returns:
456
     *   Whether the deprecated declaration has a message
457
     */
458
    private bool parseDeprecatedAttribute(ref AST.Expression msg)
459
    {
460 1
        if (peekNext() != TOK.leftParentheses)
461 1
            return false;
462

463 1
        nextToken();
464 1
        check(TOK.leftParentheses);
465 1
        AST.Expression e = parseAssignExp();
466 1
        check(TOK.rightParentheses);
467 1
        if (msg)
468
        {
469 0
            error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
470
        }
471 1
        msg = e;
472 1
        return true;
473
    }
474

475
    AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
476
    {
477 1
        AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
478 1
        if (!pLastDecl)
479 1
            pLastDecl = &lastDecl;
480

481 1
        const linksave = linkage; // save global state
482

483
        //printf("Parser::parseDeclDefs()\n");
484 1
        auto decldefs = new AST.Dsymbols();
485
        do
486
        {
487
            // parse result
488 1
            AST.Dsymbol s = null;
489 1
            AST.Dsymbols* a = null;
490

491 1
            PrefixAttributes!AST attrs;
492 1
            if (!once || !pAttrs)
493
            {
494 1
                pAttrs = &attrs;
495 1
                pAttrs.comment = token.blockComment.ptr;
496
            }
497 1
            AST.Prot.Kind prot;
498 1
            StorageClass stc;
499 1
            AST.Condition condition;
500

501 1
            linkage = linksave;
502

503 1
            switch (token.value)
504
            {
505 1
            case TOK.enum_:
506
                {
507
                    /* Determine if this is a manifest constant declaration,
508
                     * or a conventional enum.
509
                     */
510 1
                    const tv = peekNext();
511 1
                    if (tv == TOK.leftCurly || tv == TOK.colon)
512 1
                        s = parseEnum();
513 1
                    else if (tv != TOK.identifier)
514 1
                        goto Ldeclaration;
515
                    else
516
                    {
517 1
                        const nextv = peekNext2();
518 1
                        if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
519 1
                            s = parseEnum();
520
                        else
521 1
                            goto Ldeclaration;
522
                    }
523 1
                    break;
524
                }
525 1
            case TOK.import_:
526 1
                a = parseImport();
527
                // keep pLastDecl
528 1
                break;
529

530 1
            case TOK.template_:
531 1
                s = cast(AST.Dsymbol)parseTemplateDeclaration();
532 1
                break;
533

534 1
            case TOK.mixin_:
535
                {
536 1
                    const loc = token.loc;
537 1
                    switch (peekNext())
538
                    {
539 1
                    case TOK.leftParentheses:
540
                        {
541
                            // mixin(string)
542 1
                            nextToken();
543 1
                            auto exps = parseArguments();
544 1
                            check(TOK.semicolon);
545 1
                            s = new AST.CompileDeclaration(loc, exps);
546 1
                            break;
547
                        }
548 1
                    case TOK.template_:
549
                        // mixin template
550 1
                        nextToken();
551 1
                        s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
552 1
                        break;
553

554 1
                    default:
555 1
                        s = parseMixin();
556 1
                        break;
557
                    }
558 1
                    break;
559
                }
560 1
            case TOK.wchar_:
561 1
            case TOK.dchar_:
562 1
            case TOK.bool_:
563 1
            case TOK.char_:
564 1
            case TOK.int8:
565 1
            case TOK.uns8:
566 1
            case TOK.int16:
567 1
            case TOK.uns16:
568 1
            case TOK.int32:
569 1
            case TOK.uns32:
570 1
            case TOK.int64:
571 1
            case TOK.uns64:
572 1
            case TOK.int128:
573 1
            case TOK.uns128:
574 1
            case TOK.float32:
575 1
            case TOK.float64:
576 1
            case TOK.float80:
577 1
            case TOK.imaginary32:
578 1
            case TOK.imaginary64:
579 1
            case TOK.imaginary80:
580 1
            case TOK.complex32:
581 1
            case TOK.complex64:
582 1
            case TOK.complex80:
583 1
            case TOK.void_:
584 1
            case TOK.alias_:
585 1
            case TOK.identifier:
586 1
            case TOK.super_:
587 1
            case TOK.typeof_:
588 1
            case TOK.dot:
589 1
            case TOK.vector:
590 1
            case TOK.struct_:
591 1
            case TOK.union_:
592 1
            case TOK.class_:
593 1
            case TOK.interface_:
594 1
            case TOK.traits:
595
            Ldeclaration:
596 1
                a = parseDeclarations(false, pAttrs, pAttrs.comment);
597 1
                if (a && a.dim)
598 1
                    *pLastDecl = (*a)[a.dim - 1];
599 1
                break;
600

601 1
            case TOK.this_:
602 1
                if (peekNext() == TOK.dot)
603 0
                    goto Ldeclaration;
604 1
                s = parseCtor(pAttrs);
605 1
                break;
606

607 1
            case TOK.tilde:
608 1
                s = parseDtor(pAttrs);
609 1
                break;
610

611 1
            case TOK.invariant_:
612 1
                const tv = peekNext();
613 1
                if (tv == TOK.leftParentheses || tv == TOK.leftCurly)
614
                {
615
                    // invariant { statements... }
616
                    // invariant() { statements... }
617
                    // invariant (expression);
618 1
                    s = parseInvariant(pAttrs);
619 1
                    break;
620
                }
621 0
                error("invariant body expected, not `%s`", token.toChars());
622 0
                goto Lerror;
623

624 1
            case TOK.unittest_:
625 1
                if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
626
                {
627 1
                    s = parseUnitTest(pAttrs);
628 1
                    if (*pLastDecl)
629 1
                        (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
630
                }
631
                else
632
                {
633
                    // Skip over unittest block by counting { }
634 1
                    Loc loc = token.loc;
635 1
                    int braces = 0;
636 1
                    while (1)
637
                    {
638 1
                        nextToken();
639 1
                        switch (token.value)
640
                        {
641 1
                        case TOK.leftCurly:
642 1
                            ++braces;
643 1
                            continue;
644

645 1
                        case TOK.rightCurly:
646 1
                            if (--braces)
647 1
                                continue;
648 1
                            nextToken();
649 1
                            break;
650

651 0
                        case TOK.endOfFile:
652
                            /* { */
653 0
                            error(loc, "closing `}` of unittest not found before end of file");
654 0
                            goto Lerror;
655

656 1
                        default:
657 1
                            continue;
658
                        }
659 1
                        break;
660
                    }
661
                    // Workaround 14894. Add an empty unittest declaration to keep
662
                    // the number of symbols in this scope independent of -unittest.
663 1
                    s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
664
                }
665 1
                break;
666

667 1
            case TOK.new_:
668 1
                s = parseNew(pAttrs);
669 1
                break;
670

671 1
            case TOK.colon:
672 1
            case TOK.leftCurly:
673 1
                error("declaration expected, not `%s`", token.toChars());
674 1
                goto Lerror;
675

676 1
            case TOK.rightCurly:
677 1
            case TOK.endOfFile:
678 1
                if (once)
679 1
                    error("declaration expected, not `%s`", token.toChars());
680 1
                return decldefs;
681

682 1
            case TOK.static_:
683
                {
684 1
                    const next = peekNext();
685 1
                    if (next == TOK.this_)
686 1
                        s = parseStaticCtor(pAttrs);
687 1
                    else if (next == TOK.tilde)
688 1
                        s = parseStaticDtor(pAttrs);
689 1
                    else if (next == TOK.assert_)
690 1
                        s = parseStaticAssert();
691 1
                    else if (next == TOK.if_)
692
                    {
693 1
                        condition = parseStaticIfCondition();
694 1
                        AST.Dsymbols* athen;
695 1
                        if (token.value == TOK.colon)
696 1
                            athen = parseBlock(pLastDecl);
697
                        else
698
                        {
699 1
                            const lookingForElseSave = lookingForElse;
700 1
                            lookingForElse = token.loc;
701 1
                            athen = parseBlock(pLastDecl);
702 1
                            lookingForElse = lookingForElseSave;
703
                        }
704 1
                        AST.Dsymbols* aelse = null;
705 1
                        if (token.value == TOK.else_)
706
                        {
707 1
                            const elseloc = token.loc;
708 1
                            nextToken();
709 1
                            aelse = parseBlock(pLastDecl);
710 1
                            checkDanglingElse(elseloc);
711
                        }
712 1
                        s = new AST.StaticIfDeclaration(condition, athen, aelse);
713
                    }
714 1
                    else if (next == TOK.import_)
715
                    {
716 1
                        a = parseImport();
717
                        // keep pLastDecl
718
                    }
719 1
                    else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
720
                    {
721 1
                        s = parseForeach!(true,true)(token.loc, pLastDecl);
722
                    }
723
                    else
724
                    {
725 1
                        stc = STC.static_;
726 1
                        goto Lstc;
727
                    }
728 1
                    break;
729
                }
730 1
            case TOK.const_:
731 1
                if (peekNext() == TOK.leftParentheses)
732 1
                    goto Ldeclaration;
733 1
                stc = STC.const_;
734 1
                goto Lstc;
735

736 1
            case TOK.immutable_:
737 1
                if (peekNext() == TOK.leftParentheses)
738 1
                    goto Ldeclaration;
739 1
                stc = STC.immutable_;
740 1
                goto Lstc;
741

742 1
            case TOK.shared_:
743
                {
744 1
                    const next = peekNext();
745 1
                    if (next == TOK.leftParentheses)
746 1
                        goto Ldeclaration;
747 1
                    if (next == TOK.static_)
748
                    {
749 1
                        TOK next2 = peekNext2();
750 1
                        if (next2 == TOK.this_)
751
                        {
752 1
                            s = parseSharedStaticCtor(pAttrs);
753 1
                            break;
754
                        }
755 1
                        if (next2 == TOK.tilde)
756
                        {
757 1
                            s = parseSharedStaticDtor(pAttrs);
758 1
                            break;
759
                        }
760
                    }
761 1
                    stc = STC.shared_;
762 1
                    goto Lstc;
763
                }
764 1
            case TOK.inout_:
765 1
                if (peekNext() == TOK.leftParentheses)
766 1
                    goto Ldeclaration;
767 1
                stc = STC.wild;
768 1
                goto Lstc;
769

770 1
            case TOK.final_:
771 1
                stc = STC.final_;
772 1
                goto Lstc;
773

774 1
            case TOK.auto_:
775 1
                stc = STC.auto_;
776 1
                goto Lstc;
777

778 1
            case TOK.scope_:
779 1
                stc = STC.scope_;
780 1
                goto Lstc;
781

782 1
            case TOK.override_:
783 1
                stc = STC.override_;
784 1
                goto Lstc;
785

786 1
            case TOK.abstract_:
787 1
                stc = STC.abstract_;
788 1
                goto Lstc;
789

790 1
            case TOK.synchronized_:
791 1
                stc = STC.synchronized_;
792 1
                goto Lstc;
793

794 1
            case TOK.nothrow_:
795 1
                stc = STC.nothrow_;
796 1
                goto Lstc;
797

798 1
            case TOK.pure_:
799 1
                stc = STC.pure_;
800 1
                goto Lstc;
801

802 1
            case TOK.ref_:
803 1
                stc = STC.ref_;
804 1
                goto Lstc;
805

806 1
            case TOK.gshared:
807 1
                stc = STC.gshared;
808 1
                goto Lstc;
809

810 1
            case TOK.at:
811
                {
812 1
                    AST.Expressions* exps = null;
813 1
                    stc = parseAttribute(&exps);
814 1
                    if (stc)
815 1
                        goto Lstc; // it's a predefined attribute
816
                    // no redundant/conflicting check for UDAs
817 1
                    pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
818 1
                    goto Lautodecl;
819
                }
820
            Lstc:
821 1
                pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
822 1
                nextToken();
823

824
            Lautodecl:
825

826
                /* Look for auto initializers:
827
                 *      storage_class identifier = initializer;
828
                 *      storage_class identifier(...) = initializer;
829
                 */
830 1
                if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
831
                {
832 1
                    a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
833 1
                    if (a && a.dim)
834 1
                        *pLastDecl = (*a)[a.dim - 1];
835 1
                    if (pAttrs.udas)
836
                    {
837 1
                        s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
838 1
                        pAttrs.udas = null;
839
                    }
840 1
                    break;
841
                }
842

843
                /* Look for return type inference for template functions.
844
                 */
845 1
                Token* tk;
846 1
                if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
847 1
                    (tk.value == TOK.leftParentheses || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
848 1
                     tk.value == TOK.out_ || tk.value == TOK.do_ ||
849 0
                     tk.value == TOK.identifier && tk.ident == Id._body))
850
                {
851
                    version (none)
852
                    {
853
                        // This deprecation has been disabled for the time being, see PR10763
854
                        // @@@DEPRECATED@@@
855
                        // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
856
                        // Deprecated in 2.091 - Can be removed from 2.101
857
                        if (tk.value == TOK.identifier && tk.ident == Id._body)
858
                            deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
859
                    }
860 1
                    a = parseDeclarations(true, pAttrs, pAttrs.comment);
861 1
                    if (a && a.dim)
862 1
                        *pLastDecl = (*a)[a.dim - 1];
863 1
                    if (pAttrs.udas)
864
                    {
865 1
                        s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
866 1
                        pAttrs.udas = null;
867
                    }
868 1
                    break;
869
                }
870

871 1
                a = parseBlock(pLastDecl, pAttrs);
872 1
                auto stc2 = getStorageClass!AST(pAttrs);
873 1
                if (stc2 != STC.undefined_)
874
                {
875 1
                    s = new AST.StorageClassDeclaration(stc2, a);
876
                }
877 1
                if (pAttrs.udas)
878
                {
879 1
                    if (s)
880
                    {
881 1
                        a = new AST.Dsymbols();
882 1
                        a.push(s);
883
                    }
884 1
                    s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
885 1
                    pAttrs.udas = null;
886
                }
887 1
                break;
888

889 1
            case TOK.deprecated_:
890
                {
891 1
                    stc |= STC.deprecated_;
892 1
                    if (!parseDeprecatedAttribute(pAttrs.depmsg))
893 1
                        goto Lstc;
894

895 1
                    a = parseBlock(pLastDecl, pAttrs);
896 1
                    s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
897 1
                    pAttrs.depmsg = null;
898 1
                    break;
899
                }
900 1
            case TOK.leftBracket:
901
                {
902 1
                    if (peekNext() == TOK.rightBracket)
903 1
                        error("empty attribute list is not allowed");
904 1
                    error("use `@(attributes)` instead of `[attributes]`");
905 1
                    AST.Expressions* exps = parseArguments();
906
                    // no redundant/conflicting check for UDAs
907

908 1
                    pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
909 1
                    a = parseBlock(pLastDecl, pAttrs);
910 1
                    if (pAttrs.udas)
911
                    {
912 1
                        s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
913 1
                        pAttrs.udas = null;
914
                    }
915 1
                    break;
916
                }
917 1
            case TOK.extern_:
918
                {
919 1
                    if (peekNext() != TOK.leftParentheses)
920
                    {
921 1
                        stc = STC.extern_;
922 1
                        goto Lstc;
923
                    }
924

925 1
                    const linkLoc = token.loc;
926 1
                    AST.Identifiers* idents = null;
927 1
                    AST.Expressions* identExps = null;
928 1
                    CPPMANGLE cppmangle;
929 1
                    bool cppMangleOnly = false;
930 1
                    const link = parseLinkage(&idents, &identExps, cppmangle, cppMangleOnly);
931 1
                    if (pAttrs.link != LINK.default_)
932
                    {
933 1
                        if (pAttrs.link != link)
934
                        {
935 1
                            error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(link));
936
                        }
937 1
                        else if (idents || identExps || cppmangle != CPPMANGLE.def)
938
                        {
939
                            // Allow:
940
                            //      extern(C++, foo) extern(C++, bar) void foo();
941
                            // to be equivalent with:
942
                            //      extern(C++, foo.bar) void foo();
943
                            // Allow also:
944
                            //      extern(C++, "ns") extern(C++, class) struct test {}
945
                            //      extern(C++, class) extern(C++, "ns") struct test {}
946
                        }
947
                        else
948 1
                            error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
949
                    }
950 1
                    pAttrs.link = link;
951 1
                    this.linkage = link;
952 1
                    a = parseBlock(pLastDecl, pAttrs);
953 1
                    if (idents)
954
                    {
955 1
                        assert(link == LINK.cpp);
956 1
                        assert(idents.dim);
957 1
                        for (size_t i = idents.dim; i;)
958
                        {
959 1
                            Identifier id = (*idents)[--i];
960 1
                            if (s)
961
                            {
962 1
                                a = new AST.Dsymbols();
963 1
                                a.push(s);
964
                            }
965 1
                            if (cppMangleOnly)
966 0
                                s = new AST.CPPNamespaceDeclaration(id, a);
967
                            else
968 1
                                s = new AST.Nspace(linkLoc, id, null, a);
969
                        }
970 1
                        pAttrs.link = LINK.default_;
971
                    }
972 1
                    else if (identExps)
973
                    {
974 1
                        assert(link == LINK.cpp);
975 1
                        assert(identExps.dim);
976 1
                        for (size_t i = identExps.dim; i;)
977
                        {
978 1
                            AST.Expression exp = (*identExps)[--i];
979 1
                            if (s)
980
                            {
981 1
                                a = new AST.Dsymbols();
982 1
                                a.push(s);
983
                            }
984 1
                            if (cppMangleOnly)
985 1
                                s = new AST.CPPNamespaceDeclaration(exp, a);
986
                            else
987 0
                                s = new AST.Nspace(linkLoc, null, exp, a);
988
                        }
989 1
                        pAttrs.link = LINK.default_;
990
                    }
991 1
                    else if (cppmangle != CPPMANGLE.def)
992
                    {
993 1
                        assert(link == LINK.cpp);
994 1
                        s = new AST.CPPMangleDeclaration(cppmangle, a);
995
                    }
996 1
                    else if (pAttrs.link != LINK.default_)
997
                    {
998 1
                        s = new AST.LinkDeclaration(pAttrs.link, a);
999 1
                        pAttrs.link = LINK.default_;
1000
                    }
1001 1
                    break;
1002
                }
1003

1004 1
            case TOK.private_:
1005 1
                prot = AST.Prot.Kind.private_;
1006 1
                goto Lprot;
1007

1008 1
            case TOK.package_:
1009 1
                prot = AST.Prot.Kind.package_;
1010 1
                goto Lprot;
1011

1012 1
            case TOK.protected_:
1013 1
                prot = AST.Prot.Kind.protected_;
1014 1
                goto Lprot;
1015

1016 1
            case TOK.public_:
1017 1
                prot = AST.Prot.Kind.public_;
1018 1
                goto Lprot;
1019

1020 1
            case TOK.export_:
1021 1
                prot = AST.Prot.Kind.export_;
1022 1
                goto Lprot;
1023
            Lprot:
1024
                {
1025 1
                    if (pAttrs.protection.kind != AST.Prot.Kind.undefined)
1026
                    {
1027 1
                        if (pAttrs.protection.kind != prot)
1028 1
                            error("conflicting protection attribute `%s` and `%s`", AST.protectionToChars(pAttrs.protection.kind), AST.protectionToChars(prot));
1029
                        else
1030 1
                            error("redundant protection attribute `%s`", AST.protectionToChars(prot));
1031
                    }
1032 1
                    pAttrs.protection.kind = prot;
1033

1034 1
                    nextToken();
1035

1036
                    // optional qualified package identifier to bind
1037
                    // protection to
1038 1
                    AST.Identifiers* pkg_prot_idents = null;
1039 1
                    if (pAttrs.protection.kind == AST.Prot.Kind.package_ && token.value == TOK.leftParentheses)
1040
                    {
1041 1
                        pkg_prot_idents = parseQualifiedIdentifier("protection package");
1042 1
                        if (pkg_prot_idents)
1043 1
                            check(TOK.rightParentheses);
1044
                        else
1045
                        {
1046 1
                            while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1047 1
                                nextToken();
1048 1
                            nextToken();
1049 1
                            break;
1050
                        }
1051
                    }
1052

1053 1
                    const attrloc = token.loc;
1054 1
                    a = parseBlock(pLastDecl, pAttrs);
1055 1
                    if (pAttrs.protection.kind != AST.Prot.Kind.undefined)
1056
                    {
1057 1
                        if (pAttrs.protection.kind == AST.Prot.Kind.package_ && pkg_prot_idents)
1058 1
                            s = new AST.ProtDeclaration(attrloc, pkg_prot_idents, a);
1059
                        else
1060 1
                            s = new AST.ProtDeclaration(attrloc, pAttrs.protection, a);
1061

1062 1
                        pAttrs.protection = AST.Prot(AST.Prot.Kind.undefined);
1063
                    }
1064 1
                    break;
1065
                }
1066 1
            case TOK.align_:
1067
                {
1068 1
                    const attrLoc = token.loc;
1069

1070 1
                    nextToken();
1071

1072 1
                    AST.Expression e = null; // default
1073 1
                    if (token.value == TOK.leftParentheses)
1074
                    {
1075 1
                        nextToken();
1076 1
                        e = parseAssignExp();
1077 1
                        check(TOK.rightParentheses);
1078
                    }
1079

1080 1
                    if (pAttrs.setAlignment)
1081
                    {
1082 1
                        if (e)
1083 1
                            error("redundant alignment attribute `align(%s)`", e.toChars());
1084
                        else
1085 1
                            error("redundant alignment attribute `align`");
1086
                    }
1087

1088 1
                    pAttrs.setAlignment = true;
1089 1
                    pAttrs.ealign = e;
1090 1
                    a = parseBlock(pLastDecl, pAttrs);
1091 1
                    if (pAttrs.setAlignment)
1092
                    {
1093 1
                        s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
1094 1
                        pAttrs.setAlignment = false;
1095 1
                        pAttrs.ealign = null;
1096
                    }
1097 1
                    break;
1098
                }
1099 1
            case TOK.pragma_:
1100
                {
1101 1
                    AST.Expressions* args = null;
1102 1
                    const loc = token.loc;
1103

1104 1
                    nextToken();
1105 1
                    check(TOK.leftParentheses);
1106 1
                    if (token.value != TOK.identifier)
1107
                    {
1108 0
                        error("`pragma(identifier)` expected");
1109 0
                        goto Lerror;
1110
                    }
1111 1
                    Identifier ident = token.ident;
1112 1
                    nextToken();
1113 1
                    if (token.value == TOK.comma && peekNext() != TOK.rightParentheses)
1114 1
                        args = parseArguments(); // pragma(identifier, args...)
1115
                    else
1116 1
                        check(TOK.rightParentheses); // pragma(identifier)
1117

1118 1
                    AST.Dsymbols* a2 = null;
1119 1
                    if (token.value == TOK.semicolon)
1120
                    {
1121
                        /* https://issues.dlang.org/show_bug.cgi?id=2354
1122
                         * Accept single semicolon as an empty
1123
                         * DeclarationBlock following attribute.
1124
                         *
1125
                         * Attribute DeclarationBlock
1126
                         * Pragma    DeclDef
1127
                         *           ;
1128
                         */
1129 1
                        nextToken();
1130
                    }
1131
                    else
1132 1
                        a2 = parseBlock(pLastDecl);
1133 1
                    s = new AST.PragmaDeclaration(loc, ident, args, a2);
1134 1
                    break;
1135
                }
1136 1
            case TOK.debug_:
1137 1
                nextToken();
1138 1
                if (token.value == TOK.assign)
1139
                {
1140 1
                    nextToken();
1141 1
                    if (token.value == TOK.identifier)
1142 1
                        s = new AST.DebugSymbol(token.loc, token.ident);
1143 1
                    else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1144 1
                        s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
1145
                    else
1146
                    {
1147 0
                        error("identifier or integer expected, not `%s`", token.toChars());
1148 0
                        s = null;
1149
                    }
1150 1
                    nextToken();
1151 1
                    if (token.value != TOK.semicolon)
1152 0
                        error("semicolon expected");
1153 1
                    nextToken();
1154 1
                    break;
1155
                }
1156

1157 1
                condition = parseDebugCondition();
1158 1
                goto Lcondition;
1159

1160 1
            case TOK.version_:
1161 1
                nextToken();
1162 1
                if (token.value == TOK.assign)
1163
                {
1164 1
                    nextToken();
1165 1
                    if (token.value == TOK.identifier)
1166 1
                        s = new AST.VersionSymbol(token.loc, token.ident);
1167 1
                    else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1168 1
                        s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
1169
                    else
1170
                    {
1171 0
                        error("identifier or integer expected, not `%s`", token.toChars());
1172 0
                        s = null;
1173
                    }
1174 1
                    nextToken();
1175 1
                    if (token.value != TOK.semicolon)
1176 0
                        error("semicolon expected");
1177 1
                    nextToken();
1178 1
                    break;
1179
                }
1180 1
                condition = parseVersionCondition();
1181 1
                goto Lcondition;
1182

1183
            Lcondition:
1184
                {
1185 1
                    AST.Dsymbols* athen;
1186 1
                    if (token.value == TOK.colon)
1187 1
                        athen = parseBlock(pLastDecl);
1188
                    else
1189
                    {
1190 1
                        const lookingForElseSave = lookingForElse;
1191 1
                        lookingForElse = token.loc;
1192 1
                        athen = parseBlock(pLastDecl);
1193 1
                        lookingForElse = lookingForElseSave;
1194
                    }
1195 1
                    AST.Dsymbols* aelse = null;
1196 1
                    if (token.value == TOK.else_)
1197
                    {
1198 1
                        const elseloc = token.loc;
1199 1
                        nextToken();
1200 1
                        aelse = parseBlock(pLastDecl);
1201 1
                        checkDanglingElse(elseloc);
1202
                    }
1203 1
                    s = new AST.ConditionalDeclaration(condition, athen, aelse);
1204 1
                    break;
1205
                }
1206 1
            case TOK.semicolon:
1207
                // empty declaration
1208
                //error("empty declaration");
1209 1
                nextToken();
1210 1
                continue;
1211

1212 1
            default:
1213 1
                error("declaration expected, not `%s`", token.toChars());
1214
            Lerror:
1215 1
                while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1216 1
                    nextToken();
1217 1
                nextToken();
1218 1
                s = null;
1219 1
                continue;
1220
            }
1221

1222 1
            if (s)
1223
            {
1224 1
                if (!s.isAttribDeclaration())
1225 1
                    *pLastDecl = s;
1226 1
                decldefs.push(s);
1227 1
                addComment(s, pAttrs.comment);
1228
            }
1229 1
            else if (a && a.dim)
1230
            {
1231 1
                decldefs.append(a);
1232
            }
1233
        }
1234 1
        while (!once);
1235

1236 1
        linkage = linksave;
1237

1238 1
        return decldefs;
1239
    }
1240

1241
    /*****************************************
1242
     * Parse auto declarations of the form:
1243
     *   storageClass ident = init, ident = init, ... ;
1244
     * and return the array of them.
1245
     * Starts with token on the first ident.
1246
     * Ends with scanner past closing ';'
1247
     */
1248
    private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1249
    {
1250
        //printf("parseAutoDeclarations\n");
1251 1
        auto a = new AST.Dsymbols();
1252

1253 1
        while (1)
1254
        {
1255 1
            const loc = token.loc;
1256 1
            Identifier ident = token.ident;
1257 1
            nextToken(); // skip over ident
1258

1259 1
            AST.TemplateParameters* tpl = null;
1260 1
            if (token.value == TOK.leftParentheses)
1261 1
                tpl = parseTemplateParameterList();
1262

1263 1
            check(TOK.assign);   // skip over '='
1264 1
            AST.Initializer _init = parseInitializer();
1265 1
            auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1266

1267 1
            AST.Dsymbol s = v;
1268 1
            if (tpl)
1269
            {
1270 1
                auto a2 = new AST.Dsymbols();
1271 1
                a2.push(v);
1272 1
                auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1273 1
                s = tempdecl;
1274
            }
1275 1
            a.push(s);
1276 1
            switch (token.value)
1277
            {
1278 1
            case TOK.semicolon:
1279 1
                nextToken();
1280 1
                addComment(s, comment);
1281 1
                break;
1282

1283 1
            case TOK.comma:
1284 1
                nextToken();
1285 1
                if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1286
                {
1287 0
                    error("identifier expected following comma");
1288 0
                    break;
1289
                }
1290 1
                addComment(s, comment);
1291 1
                continue;
1292

1293 1
            default:
1294 1
                error("semicolon expected following auto declaration, not `%s`", token.toChars());
1295 1
                break;
1296
            }
1297 1
            break;
1298
        }
1299 1
        return a;
1300
    }
1301

1302
    /********************************************
1303
     * Parse declarations after an align, protection, or extern decl.
1304
     */
1305
    private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1306
    {
1307 1
        AST.Dsymbols* a = null;
1308

1309
        //printf("parseBlock()\n");
1310 1
        switch (token.value)
1311
        {
1312 1
        case TOK.semicolon:
1313 1
            error("declaration expected following attribute, not `;`");
1314 1
            nextToken();
1315 1
            break;
1316

1317 1
        case TOK.endOfFile:
1318 1
            error("declaration expected following attribute, not end of file");
1319 1
            break;
1320

1321 1
        case TOK.leftCurly:
1322
            {
1323 1
                const lookingForElseSave = lookingForElse;
1324 1
                lookingForElse = Loc();
1325

1326 1
                nextToken();
1327 1
                a = parseDeclDefs(0, pLastDecl);
1328 1
                if (token.value != TOK.rightCurly)
1329
                {
1330
                    /* { */
1331 0
                    error("matching `}` expected, not `%s`", token.toChars());
1332
                }
1333
                else
1334 1
                    nextToken();
1335 1
                lookingForElse = lookingForElseSave;
1336 1
                break;
1337
            }
1338 1
        case TOK.colon:
1339 1
            nextToken();
1340 1
            a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1341 1
            break;
1342

1343 1
        default:
1344 1
            a = parseDeclDefs(1, pLastDecl, pAttrs);
1345 1
            break;
1346
        }
1347 1
        return a;
1348
    }
1349

1350
    /*********************************************
1351
     * Give error on redundant/conflicting storage class.
1352
     */
1353
    private StorageClass appendStorageClass(StorageClass storageClass, StorageClass stc)
1354
    {
1355 1
        if ((storageClass & stc) || (storageClass & STC.in_ && stc & (STC.const_ | STC.scope_)) || (stc & STC.in_ && storageClass & (STC.const_ | STC.scope_)))
1356
        {
1357 1
            OutBuffer buf;
1358 1
            AST.stcToBuffer(&buf, stc);
1359 1
            error("redundant attribute `%s`", buf.peekChars());
1360 1
            return storageClass | stc;
1361
        }
1362

1363 1
        storageClass |= stc;
1364

1365 1
        if (stc & (STC.const_ | STC.immutable_ | STC.manifest))
1366
        {
1367 1
            StorageClass u = storageClass & (STC.const_ | STC.immutable_ | STC.manifest);
1368 1
            if (u & (u - 1))
1369 1
                error("conflicting attribute `%s`", Token.toChars(token.value));
1370
        }
1371 1
        if (stc & (STC.gshared | STC.shared_ | STC.tls))
1372
        {
1373 1
            StorageClass u = storageClass & (STC.gshared | STC.shared_ | STC.tls);
1374 1
            if (u & (u - 1))
1375 1
                error("conflicting attribute `%s`", Token.toChars(token.value));
1376
        }
1377 1
        if (stc & STC.safeGroup)
1378
        {
1379 1
            StorageClass u = storageClass & STC.safeGroup;
1380 1
            if (u & (u - 1))
1381 1
                error("conflicting attribute `@%s`", token.toChars());
1382
        }
1383

1384 1
        return storageClass;
1385
    }
1386

1387
    /***********************************************
1388
     * Parse attribute, lexer is on '@'.
1389
     * Input:
1390
     *      pudas           array of UDAs to append to
1391
     * Returns:
1392
     *      storage class   if a predefined attribute; also scanner remains on identifier.
1393
     *      0               if not a predefined attribute
1394
     *      *pudas          set if user defined attribute, scanner is past UDA
1395
     *      *pudas          NULL if not a user defined attribute
1396
     */
1397
    private StorageClass parseAttribute(AST.Expressions** pudas)
1398
    {
1399 1
        nextToken();
1400 1
        AST.Expressions* udas = null;
1401 1
        StorageClass stc = 0;
1402 1
        if (token.value == TOK.identifier)
1403
        {
1404 1
            stc = isBuiltinAtAttribute(token.ident);
1405 1
            if (!stc)
1406
            {
1407
                // Allow identifier, template instantiation, or function call
1408 1
                AST.Expression exp = parsePrimaryExp();
1409 1
                if (token.value == TOK.leftParentheses)
1410
                {
1411 1
                    const loc = token.loc;
1412 1
                    exp = new AST.CallExp(loc, exp, parseArguments());
1413
                }
1414

1415 1
                udas = new AST.Expressions();
1416 1
                udas.push(exp);
1417
            }
1418
        }
1419 1
        else if (token.value == TOK.leftParentheses)
1420
        {
1421
            // @( ArgumentList )
1422
            // Concatenate with existing
1423 1
            if (peekNext() == TOK.rightParentheses)
1424 1
                error("empty attribute list is not allowed");
1425 1
            udas = parseArguments();
1426
        }
1427
        else
1428
        {
1429 1
            error("@identifier or @(ArgumentList) expected, not `@%s`", token.toChars());
1430
        }
1431

1432 1
        if (stc)
1433
        {
1434
        }
1435 1
        else if (udas)
1436
        {
1437 1
            *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1438
        }
1439
        else
1440 1
            error("valid attributes are `@property`, `@safe`, `@trusted`, `@system`, `@disable`, `@nogc`");
1441 1
        return stc;
1442
    }
1443

1444
    /***********************************************
1445
     * Parse const/immutable/shared/inout/nothrow/pure postfix
1446
     */
1447
    private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1448
    {
1449 1
        while (1)
1450
        {
1451 1
            StorageClass stc;
1452 1
            switch (token.value)
1453
            {
1454 1
            case TOK.const_:
1455 1
                stc = STC.const_;
1456 1
                break;
1457

1458 1
            case TOK.immutable_:
1459 1
                stc = STC.immutable_;
1460 1
                break;
1461

1462 1
            case TOK.shared_:
1463 1
                stc = STC.shared_;
1464 1
                break;
1465

1466 1
            case TOK.inout_:
1467 1
                stc = STC.wild;
1468 1
                break;
1469

1470 1
            case TOK.nothrow_:
1471 1
                stc = STC.nothrow_;
1472 1
                break;
1473

1474 1
            case TOK.pure_:
1475 1
                stc = STC.pure_;
1476 1
                break;
1477

1478 1
            case TOK.return_:
1479 1
                stc = STC.return_;
1480 1
                break;
1481

1482 1
            case TOK.scope_:
1483 1
                stc = STC.scope_;
1484 1
                break;
1485

1486 1
            case TOK.at:
1487
                {
1488 1
                    AST.Expressions* udas = null;
1489 1
                    stc = parseAttribute(&udas);
1490 1
                    if (udas)
1491
                    {
1492 1
                        if (pudas)
1493 1
                            *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1494
                        else
1495
                        {
1496
                            // Disallow:
1497
                            //      void function() @uda fp;
1498
                            //      () @uda { return 1; }
1499 0
                            error("user-defined attributes cannot appear as postfixes");
1500
                        }
1501 1
                        continue;
1502
                    }
1503 1
                    break;
1504
                }
1505 1
            default:
1506 1
                return storageClass;
1507
            }
1508 1
            storageClass = appendStorageClass(storageClass, stc);
1509 1
            nextToken();
1510
        }
1511
    }
1512

1513
    private StorageClass parseTypeCtor()
1514
    {
1515 1
        StorageClass storageClass = STC.undefined_;
1516

1517 1
        while (1)
1518
        {
1519 1
            if (peekNext() == TOK.leftParentheses)
1520 1
                return storageClass;
1521

1522 1
            StorageClass stc;
1523 1
            switch (token.value)
1524
            {
1525 1
            case TOK.const_:
1526 1
                stc = STC.const_;
1527 1
                break;
1528

1529 1
            case TOK.immutable_:
1530 1
                stc = STC.immutable_;
1531 1
                break;
1532

1533 1
            case TOK.shared_:
1534 1
                stc = STC.shared_;
1535 1
                break;
1536

1537 1
            case TOK.inout_:
1538 1
                stc = STC.wild;
1539 1
                break;
1540

1541 1
            default:
1542 1
                return storageClass;
1543
            }
1544 1
            storageClass = appendStorageClass(storageClass, stc);
1545 1
            nextToken();
1546
        }
1547
    }
1548

1549
    /**************************************
1550
     * Parse constraint.
1551
     * Constraint is of the form:
1552
     *      if ( ConstraintExpression )
1553
     */
1554
    private AST.Expression parseConstraint()
1555
    {
1556 1
        AST.Expression e = null;
1557 1
        if (token.value == TOK.if_)
1558
        {
1559 1
            nextToken(); // skip over 'if'
1560 1
            check(TOK.leftParentheses);
1561 1
            e = parseExpression();
1562 1
            check(TOK.rightParentheses);
1563
        }
1564 1
        return e;
1565
    }
1566

1567
    /**************************************
1568
     * Parse a TemplateDeclaration.
1569
     */
1570
    private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1571
    {
1572 1
        AST.TemplateDeclaration tempdecl;
1573 1
        Identifier id;
1574 1
        AST.TemplateParameters* tpl;
1575 1
        AST.Dsymbols* decldefs;
1576 1
        AST.Expression constraint = null;
1577 1
        const loc = token.loc;
1578

1579 1
        nextToken();
1580 1
        if (token.value != TOK.identifier)
1581
        {
1582 1
            error("identifier expected following `template`");
1583 1
            goto Lerr;
1584
        }
1585 1
        id = token.ident;
1586 1
        nextToken();
1587 1
        tpl = parseTemplateParameterList();
1588 1
        if (!tpl)
1589 0
            goto Lerr;
1590

1591 1
        constraint = parseConstraint();
1592

1593 1
        if (token.value != TOK.leftCurly)
1594
        {
1595 0
            error("members of template declaration expected");
1596 0
            goto Lerr;
1597
        }
1598 1
        decldefs = parseBlock(null);
1599

1600 1
        tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1601 1
        return tempdecl;
1602

1603
    Lerr:
1604 1
        return null;
1605
    }
1606

1607
    /******************************************
1608
     * Parse template parameter list.
1609
     * Input:
1610
     *      flag    0: parsing "( list )"
1611
     *              1: parsing non-empty "list $(RPAREN)"
1612
     */
1613
    private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1614
    {
1615 1
        auto tpl = new AST.TemplateParameters();
1616

1617 1
        if (!flag && token.value != TOK.leftParentheses)
1618
        {
1619 0
            error("parenthesized template parameter list expected following template identifier");
1620 0
            goto Lerr;
1621
        }
1622 1
        nextToken();
1623

1624
        // Get array of TemplateParameters
1625 1
        if (flag || token.value != TOK.rightParentheses)
1626
        {
1627 1
            int isvariadic = 0;
1628 1
            while (token.value != TOK.rightParentheses)
1629
            {
1630 1
                AST.TemplateParameter tp;
1631 1
                Loc loc;
1632 1
                Identifier tp_ident = null;
1633 1
                AST.Type tp_spectype = null;
1634 1
                AST.Type tp_valtype = null;
1635 1
                AST.Type tp_defaulttype = null;
1636 1
                AST.Expression tp_specvalue = null;
1637 1
                AST.Expression tp_defaultvalue = null;
1638

1639
                // Get TemplateParameter
1640

1641
                // First, look ahead to see if it is a TypeParameter or a ValueParameter
1642 1
                const tv = peekNext();
1643 1
                if (token.value == TOK.alias_)
1644
                {
1645
                    // AliasParameter
1646 1
                    nextToken();
1647 1
                    loc = token.loc; // todo
1648 1
                    AST.Type spectype = null;
1649 1
                    if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1650
                    {
1651 1
                        spectype = parseType(&tp_ident);
1652
                    }
1653
                    else
1654
                    {
1655 1
                        if (token.value != TOK.identifier)
1656
                        {
1657 0
                            error("identifier expected for template alias parameter");
1658 0
                            goto Lerr;
1659
                        }
1660 1
                        tp_ident = token.ident;
1661 1
                        nextToken();
1662
                    }
1663 1
                    RootObject spec = null;
1664 1
                    if (token.value == TOK.colon) // : Type
1665
                    {
1666 1
                        nextToken();
1667 1
                        if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1668 1
                            spec = parseType();
1669
                        else
1670 0
                            spec = parseCondExp();
1671
                    }
1672 1
                    RootObject def = null;
1673 1
                    if (token.value == TOK.assign) // = Type
1674
                    {
1675 1
                        nextToken();
1676 1
                        if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1677 1
                            def = parseType();
1678
                        else
1679 1
                            def = parseCondExp();
1680
                    }
1681 1
                    tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1682
                }
1683 1
                else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParentheses)
1684
                {
1685
                    // TypeParameter
1686 1
                    if (token.value != TOK.identifier)
1687
                    {
1688 1
                        error("identifier expected for template type parameter");
1689 1
                        goto Lerr;
1690
                    }
1691 1
                    loc = token.loc;
1692 1
                    tp_ident = token.ident;
1693 1
                    nextToken();
1694 1
                    if (token.value == TOK.colon) // : Type
1695
                    {
1696 1
                        nextToken();
1697 1
                        tp_spectype = parseType();
1698
                    }
1699 1
                    if (token.value == TOK.assign) // = Type
1700
                    {
1701 1
                        nextToken();
1702 1
                        tp_defaulttype = parseType();
1703
                    }
1704 1
                    tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1705
                }
1706 1
                else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1707
                {
1708
                    // ident...
1709 1
                    if (isvariadic)
1710 0
                        error("variadic template parameter must be last");
1711 1
                    isvariadic = 1;
1712 1
                    loc = token.loc;
1713 1
                    tp_ident = token.ident;
1714 1
                    nextToken();
1715 1
                    nextToken();
1716 1
                    tp = new AST.TemplateTupleParameter(loc, tp_ident);
1717
                }
1718 1
                else if (token.value == TOK.this_)
1719
                {
1720
                    // ThisParameter
1721 1
                    nextToken();
1722 1
                    if (token.value != TOK.identifier)
1723
                    {
1724 0
                        error("identifier expected for template this parameter");
1725 0
                        goto Lerr;
1726
                    }
1727 1
                    loc = token.loc;
1728 1
                    tp_ident = token.ident;
1729 1
                    nextToken();
1730 1
                    if (token.value == TOK.colon) // : Type
1731
                    {
1732 0
                        nextToken();
1733 0
                        tp_spectype = parseType();
1734
                    }
1735 1
                    if (token.value == TOK.assign) // = Type
1736
                    {
1737 0
                        nextToken();
1738 0
                        tp_defaulttype = parseType();
1739
                    }
1740 1
                    tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1741
                }
1742
                else
1743
                {
1744
                    // ValueParameter
1745 1
                    loc = token.loc; // todo
1746 1
                    tp_valtype = parseType(&tp_ident);
1747 1
                    if (!tp_ident)
1748
                    {
1749 1
                        error("identifier expected for template value parameter");
1750 1
                        tp_ident = Identifier.idPool("error");
1751
                    }
1752 1
                    if (token.value == TOK.colon) // : CondExpression
1753
                    {
1754 1
                        nextToken();
1755 1
                        tp_specvalue = parseCondExp();
1756
                    }
1757 1
                    if (token.value == TOK.assign) // = CondExpression
1758
                    {
1759 1
                        nextToken();
1760 1
                        tp_defaultvalue = parseDefaultInitExp();
1761
                    }
1762 1
                    tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1763
                }
1764 1
                tpl.push(tp);
1765 1
                if (token.value != TOK.comma)
1766 1
                    break;
1767 1
                nextToken();
1768
            }
1769
        }
1770 1
        check(TOK.rightParentheses);
1771

1772
    Lerr:
1773 1
        return tpl;
1774
    }
1775

1776
    /******************************************
1777
     * Parse template mixin.
1778
     *      mixin Foo;
1779
     *      mixin Foo!(args);
1780
     *      mixin a.b.c!(args).Foo!(args);
1781
     *      mixin Foo!(args) identifier;
1782
     *      mixin typeof(expr).identifier!(args);
1783
     */
1784
    private AST.Dsymbol parseMixin()
1785
    {
1786 1
        AST.TemplateMixin tm;
1787 1
        Identifier id;
1788 1
        AST.Objects* tiargs;
1789

1790
        //printf("parseMixin()\n");
1791 1
        const locMixin = token.loc;
1792 1
        nextToken(); // skip 'mixin'
1793

1794 1
        auto loc = token.loc;
1795 1
        AST.TypeQualified tqual = null;
1796 1
        if (token.value == TOK.dot)
1797
        {
1798 1
            id = Id.empty;
1799
        }
1800
        else
1801
        {
1802 1
            if (token.value == TOK.typeof_)
1803
            {
1804 1
                tqual = parseTypeof();
1805 1
                check(TOK.dot);
1806
            }
1807 1
            if (token.value != TOK.identifier)
1808
            {
1809 1
                error("identifier expected, not `%s`", token.toChars());
1810 1
                id = Id.empty;
1811
            }
1812
            else
1813 1
                id = token.ident;
1814 1
            nextToken();
1815
        }
1816

1817 1
        while (1)
1818
        {
1819 1
            tiargs = null;
1820 1
            if (token.value == TOK.not)
1821
            {
1822 1
                tiargs = parseTemplateArguments();
1823
            }
1824

1825 1
            if (tiargs && token.value == TOK.dot)
1826
            {
1827 1
                auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1828 1
                if (!tqual)
1829 1
                    tqual = new AST.TypeInstance(loc, tempinst);
1830
                else
1831 0
                    tqual.addInst(tempinst);
1832 1
                tiargs = null;
1833
            }
1834
            else
1835
            {
1836 1
                if (!tqual)
1837 1
                    tqual = new AST.TypeIdentifier(loc, id);
1838
                else
1839 1
                    tqual.addIdent(id);
1840
            }
1841

1842 1
            if (token.value != TOK.dot)
1843 1
                break;
1844

1845 1
            nextToken();
1846 1
            if (token.value != TOK.identifier)
1847
            {
1848 0
                error("identifier expected following `.` instead of `%s`", token.toChars());
1849 0
                break;
1850
            }
1851 1
            loc = token.loc;
1852 1
            id = token.ident;
1853 1
            nextToken();
1854
        }
1855

1856 1
        id = null;
1857 1
        if (token.value == TOK.identifier)
1858
        {
1859 1
            id = token.ident;
1860 1
            nextToken();
1861
        }
1862

1863 1
        tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1864 1
        if (token.value != TOK.semicolon)
1865 1
            error("`;` expected after mixin");
1866 1
        nextToken();
1867

1868 1
        return tm;
1869
    }
1870

1871
    /******************************************
1872
     * Parse template arguments.
1873
     * Input:
1874
     *      current token is opening '!'
1875
     * Output:
1876
     *      current token is one after closing '$(RPAREN)'
1877
     */
1878
    private AST.Objects* parseTemplateArguments()
1879
    {
1880 1
        AST.Objects* tiargs;
1881

1882 1
        nextToken();
1883 1
        if (token.value == TOK.leftParentheses)
1884
        {
1885
            // ident!(template_arguments)
1886 1
            tiargs = parseTemplateArgumentList();
1887
        }
1888
        else
1889
        {
1890
            // ident!template_argument
1891 1
            tiargs = parseTemplateSingleArgument();
1892
        }
1893 1
        if (token.value == TOK.not)
1894
        {
1895 1
            TOK tok = peekNext();
1896 1
            if (tok != TOK.is_ && tok != TOK.in_)
1897
            {
1898 1
                error("multiple ! arguments are not allowed");
1899
            Lagain:
1900 1
                nextToken();
1901 1
                if (token.value == TOK.leftParentheses)
1902 1
                    parseTemplateArgumentList();
1903
                else
1904 1
                    parseTemplateSingleArgument();
1905 1
                if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1906 1
                    goto Lagain;
1907
            }
1908
        }
1909 1
        return tiargs;
1910
    }
1911

1912
    /******************************************
1913
     * Parse template argument list.
1914
     * Input:
1915
     *      current token is opening '$(LPAREN)',
1916
     *          or ',' for __traits
1917
     * Output:
1918
     *      current token is one after closing '$(RPAREN)'
1919
     */
1920
    private AST.Objects* parseTemplateArgumentList()
1921
    {
1922
        //printf("Parser::parseTemplateArgumentList()\n");
1923 1
        auto tiargs = new AST.Objects();
1924 1
        TOK endtok = TOK.rightParentheses;
1925 1
        assert(token.value == TOK.leftParentheses || token.value == TOK.comma);
1926 1
        nextToken();
1927

1928
        // Get TemplateArgumentList
1929 1
        while (token.value != endtok)
1930
        {
1931 1
            tiargs.push(parseTypeOrAssignExp());
1932 1
            if (token.value != TOK.comma)
1933 1
                break;
1934 1
            nextToken();
1935
        }
1936 1
        check(endtok, "template argument list");
1937 1
        return tiargs;
1938
    }
1939

1940
    /***************************************
1941
     * Parse a Type or an Expression
1942
     * Returns:
1943
     *  RootObject representing the AST
1944
     */
1945
    RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1946
    {
1947 1
        return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1948 1
            ? parseType()           // argument is a type
1949 1
            : parseAssignExp();     // argument is an expression
1950
    }
1951

1952
    /*****************************
1953
     * Parse single template argument, to support the syntax:
1954
     *      foo!arg
1955
     * Input:
1956
     *      current token is the arg
1957
     */
1958
    private AST.Objects* parseTemplateSingleArgument()
1959
    {
1960
        //printf("parseTemplateSingleArgument()\n");
1961 1
        auto tiargs = new AST.Objects();
1962 1
        AST.Type ta;
1963 1
        switch (token.value)
1964
        {
1965 1
        case TOK.identifier:
1966 1
            ta = new AST.TypeIdentifier(token.loc, token.ident);
1967 1
            goto LabelX;
1968

1969 0
        case TOK.vector:
1970 0
            ta = parseVector();
1971 0
            goto LabelX;
1972

1973 1
        case TOK.void_:
1974 1
            ta = AST.Type.tvoid;
1975 1
            goto LabelX;
1976

1977 1
        case TOK.int8:
1978 1
            ta = AST.Type.tint8;
1979 1
            goto LabelX;
1980

1981 1
        case TOK.uns8:
1982 1
            ta = AST.Type.tuns8;
1983 1
            goto LabelX;
1984

1985 1
        case TOK.int16:
1986 1
            ta = AST.Type.tint16;
1987 1
            goto LabelX;
1988

1989 1
        case TOK.uns16:
1990 1
            ta = AST.Type.tuns16;
1991 1
            goto LabelX;
1992

1993 1
        case TOK.int32:
1994 1
            ta = AST.Type.tint32;
1995 1
            goto LabelX;
1996

1997 1
        case TOK.uns32:
1998 1
            ta = AST.Type.tuns32;
1999 1
            goto LabelX;
2000

2001 1
        case TOK.int64:
2002 1
            ta = AST.Type.tint64;
2003 1
            goto LabelX;
2004

2005 1
        case TOK.uns64:
2006 1
            ta = AST.Type.tuns64;
2007 1
            goto LabelX;
2008

2009 1
        case TOK.int128:
2010 1
            ta = AST.Type.tint128;
2011 1
            goto LabelX;
2012

2013 1
        case TOK.uns128:
2014 1
            ta = AST.Type.tuns128;
2015 1
            goto LabelX;
2016

2017 1
        case TOK.float32:
2018 1
            ta = AST.Type.tfloat32;
2019 1
            goto LabelX;
2020

2021 1
        case TOK.float64:
2022 1
            ta = AST.Type.tfloat64;
2023 1
            goto LabelX;
2024

2025 1
        case TOK.float80:
2026 1
            ta = AST.Type.tfloat80;
2027 1
            goto LabelX;
2028

2029 1
        case TOK.imaginary32:
2030 1
            ta = AST.Type.timaginary32;
2031 1
            goto LabelX;
2032

2033 1
        case TOK.imaginary64:
2034 1
            ta = AST.Type.timaginary64;
2035 1
            goto LabelX;
2036

2037 1
        case TOK.imaginary80:
2038 1
            ta = AST.Type.timaginary80;
2039 1
            goto LabelX;
2040

2041 1
        case TOK.complex32:
2042 1
            ta = AST.Type.tcomplex32;
2043 1
            goto LabelX;
2044

2045 1
        case TOK.complex64:
2046 1
            ta = AST.Type.tcomplex64;
2047 1
            goto LabelX;
2048

2049 1
        case TOK.complex80:
2050 1
            ta = AST.Type.tcomplex80;
2051 1
            goto LabelX;
2052

2053 1
        case TOK.bool_:
2054 1
            ta = AST.Type.tbool;
2055 1
            goto LabelX;
2056

2057 1
        case TOK.char_:
2058 1
            ta = AST.Type.tchar;
2059 1
            goto LabelX;
2060

2061 1
        case TOK.wchar_:
2062 1
            ta = AST.Type.twchar;
2063 1
            goto LabelX;
2064

2065 1
        case TOK.dchar_:
2066 1
            ta = AST.Type.tdchar;
2067 1
            goto LabelX;
2068
        LabelX:
2069 1
            tiargs.push(ta);
2070 1
            nextToken();
2071 1
            break;
2072

2073 1
        case TOK.int32Literal:
2074 1
        case TOK.uns32Literal:
2075 1
        case TOK.int64Literal:
2076 1
        case TOK.uns64Literal:
2077 1
        case TOK.int128Literal:
2078 1
        case TOK.uns128Literal:
2079 1
        case TOK.float32Literal:
2080 1
        case TOK.float64Literal:
2081 1
        case TOK.float80Literal:
2082 1
        case TOK.imaginary32Literal:
2083 1
        case TOK.imaginary64Literal:
2084 1
        case TOK.imaginary80Literal:
2085 1
        case TOK.null_:
2086 1
        case TOK.true_:
2087 1
        case TOK.false_:
2088 1
        case TOK.charLiteral:
2089 1
        case TOK.wcharLiteral:
2090 1
        case TOK.dcharLiteral:
2091 1
        case TOK.string_:
2092 1
        case TOK.hexadecimalString:
2093 1
        case TOK.file:
2094 1
        case TOK.fileFullPath:
2095 1
        case TOK.line:
2096 1
        case TOK.moduleString:
2097 1
        case TOK.functionString:
2098 1
        case TOK.prettyFunction:
2099 1
        case TOK.this_:
2100
            {
2101
                // Template argument is an expression
2102 1
                AST.Expression ea = parsePrimaryExp();
2103 1
                tiargs.push(ea);
2104 1
                break;
2105
            }
2106 1
        default:
2107 1
            error("template argument expected following `!`");
2108 1
            break;
2109
        }
2110 1
        return tiargs;
2111
    }
2112

2113
    /**********************************
2114
     * Parse a static assertion.
2115
     * Current token is 'static'.
2116
     */
2117
    private AST.StaticAssert parseStaticAssert()
2118
    {
2119 1
        const loc = token.loc;
2120 1
        AST.Expression exp;
2121 1
        AST.Expression msg = null;
2122

2123
        //printf("parseStaticAssert()\n");
2124 1
        nextToken();
2125 1
        nextToken();
2126 1
        check(TOK.leftParentheses);
2127 1
        exp = parseAssignExp();
2128 1
        if (token.value == TOK.comma)
2129
        {
2130 1
            nextToken();
2131 1
            if (token.value != TOK.rightParentheses)
2132
            {
2133 1
                msg = parseAssignExp();
2134 1
                if (token.value == TOK.comma)
2135 1
                    nextToken();
2136
            }
2137
        }
2138 1
        check(TOK.rightParentheses);
2139 1
        check(TOK.semicolon);
2140 1
        return new AST.StaticAssert(loc, exp, msg);
2141
    }
2142

2143
    /***********************************
2144
     * Parse typeof(expression).
2145
     * Current token is on the 'typeof'.
2146
     */
2147
    private AST.TypeQualified parseTypeof()
2148
    {
2149 1
        AST.TypeQualified t;
2150 1
        const loc = token.loc;
2151

2152 1
        nextToken();
2153 1
        check(TOK.leftParentheses);
2154 1
        if (token.value == TOK.return_) // typeof(return)
2155
        {
2156 1
            nextToken();
2157 1
            t = new AST.TypeReturn(loc);
2158
        }
2159
        else
2160
        {
2161 1
            AST.Expression exp = parseExpression(); // typeof(expression)
2162 1
            t = new AST.TypeTypeof(loc, exp);
2163
        }
2164 1
        check(TOK.rightParentheses);
2165 1
        return t;
2166
    }
2167

2168
    /***********************************
2169
     * Parse __vector(type).
2170
     * Current token is on the '__vector'.
2171
     */
2172
    private AST.Type parseVector()
2173
    {
2174 1
        nextToken();
2175 1
        check(TOK.leftParentheses);
2176 1
        AST.Type tb = parseType();
2177 1
        check(TOK.rightParentheses);
2178 1
        return new AST.TypeVector(tb);
2179
    }
2180

2181
    /***********************************
2182
     * Parse:
2183
     *      extern (linkage)
2184
     *      extern (C++, namespaces)
2185
     *      extern (C++, "namespace", "namespaces", ...)
2186
     *      extern (C++, (StringExp))
2187
     * The parser is on the 'extern' token.
2188
     */
2189
    private LINK parseLinkage(AST.Identifiers** pidents, AST.Expressions** pIdentExps, out CPPMANGLE cppmangle, out bool cppMangleOnly)
2190
    {
2191 1
        AST.Identifiers* idents = null;
2192 1
        AST.Expressions* identExps = null;
2193 1
        cppmangle = CPPMANGLE.def;
2194 1
        LINK link = LINK.d; // default
2195 1
        nextToken();
2196 1
        assert(token.value == TOK.leftParentheses);
2197 1
        nextToken();
2198 1
        if (token.value == TOK.identifier)
2199
        {
2200 1
            Identifier id = token.ident;
2201 1
            nextToken();
2202 1
            if (id == Id.Windows)
2203 1
                link = LINK.windows;
2204 1
            else if (id == Id.Pascal)
2205
            {
2206 1
                deprecation("`extern(Pascal)` is deprecated. You might want to use `extern(Windows)` instead.");
2207 1
                link = LINK.pascal;
2208
            }
2209 1
            else if (id == Id.D)
2210
            { /* already set */}
2211 1
            else if (id == Id.C)
2212
            {
2213 1
                link = LINK.c;
2214 1
                if (token.value == TOK.plusPlus)
2215
                {
2216 1
                    link = LINK.cpp;
2217 1
                    nextToken();
2218 1
                    if (token.value == TOK.comma) // , namespaces or class or struct
2219
                    {
2220 1
                        nextToken();
2221 1
                        if (token.value == TOK.class_ || token.value == TOK.struct_)
2222
                        {
2223 1
                            cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2224 1
                            nextToken();
2225
                        }
2226 1
                        else if (token.value == TOK.identifier) // named scope namespace
2227
                        {
2228 1
                            idents = new AST.Identifiers();
2229 1
                            while (1)
2230
                            {
2231 1
                                Identifier idn = token.ident;
2232 1
                                idents.push(idn);
2233 1
                                nextToken();
2234 1
                                if (token.value == TOK.dot)
2235
                                {
2236 1
                                    nextToken();
2237 1
                                    if (token.value == TOK.identifier)
2238 1
                                        continue;
2239 1
                                    error("identifier expected for C++ namespace");
2240 1
                                    idents = null;  // error occurred, invalidate list of elements.
2241
                                }
2242 1
                                break;
2243
                            }
2244
                        }
2245
                        else // non-scoped StringExp namespace
2246
                        {
2247 1
                            cppMangleOnly = true;
2248 1
                            identExps = new AST.Expressions();
2249 1
                            while (1)
2250
                            {
2251 1
                                identExps.push(parseCondExp());
2252 1
                                if (token.value != TOK.comma)
2253 1
                                    break;
2254 1
                                nextToken();
2255
                                // Allow trailing commas as done for argument lists, arrays, ...
2256 1
                                if (token.value == TOK.rightParentheses)
2257 1
                                    break;
2258
                            }
2259
                        }
2260
                    }
2261
                }
2262
            }
2263 1
            else if (id == Id.Objective) // Looking for tokens "Objective-C"
2264
            {
2265 1
                if (token.value == TOK.min)
2266
                {
2267 1
                    nextToken();
2268 1
                    if (token.ident == Id.C)
2269
                    {
2270 1
                        link = LINK.objc;
2271 1
                        nextToken();
2272
                    }
2273
                    else
2274 0
                        goto LinvalidLinkage;
2275
                }
2276
                else
2277 0
                    goto LinvalidLinkage;
2278
            }
2279 1
            else if (id == Id.System)
2280
            {
2281 1
                link = LINK.system;
2282
            }
2283
            else
2284
            {
2285
            LinvalidLinkage:
2286 0
                error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Pascal`, `Windows`, `System`");
2287 0
                link = LINK.d;
2288
            }
2289
        }
2290 1
        check(TOK.rightParentheses);
2291 1
        *pidents = idents;
2292 1
        *pIdentExps = identExps;
2293 1
        return link;
2294
    }
2295

2296
    /***********************************
2297
     * Parse ident1.ident2.ident3
2298
     *
2299
     * Params:
2300
     *  entity = what qualified identifier is expected to resolve into.
2301
     *     Used only for better error message
2302
     *
2303
     * Returns:
2304
     *     array of identifiers with actual qualified one stored last
2305
     */
2306
    private AST.Identifiers* parseQualifiedIdentifier(const(char)* entity)
2307
    {
2308 1
        AST.Identifiers* qualified = null;
2309

2310
        do
2311
        {
2312 1
            nextToken();
2313 1
            if (token.value != TOK.identifier)
2314
            {
2315 1
                error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2316 1
                return null;
2317
            }
2318

2319 1
            Identifier id = token.ident;
2320 1
            if (!qualified)
2321 1
                qualified = new AST.Identifiers();
2322 1
            qualified.push(id);
2323

2324 1
            nextToken();
2325
        }
2326 1
        while (token.value == TOK.dot);
2327

2328 1
        return qualified;
2329
    }
2330

2331
    /**************************************
2332
     * Parse a debug conditional
2333
     */
2334
    private AST.Condition parseDebugCondition()
2335
    {
2336 1
        uint level = 1;
2337 1
        Identifier id = null;
2338

2339 1
        if (token.value == TOK.leftParentheses)
2340
        {
2341 1
            nextToken();
2342

2343 1
            if (token.value == TOK.identifier)
2344 1
                id = token.ident;
2345 1
            else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2346 1
                level = cast(uint)token.unsvalue;
2347
            else
2348 0
                error("identifier or integer expected inside debug(...), not `%s`", token.toChars());
2349 1
            nextToken();
2350 1
            check(TOK.rightParentheses);
2351
        }
2352 1
        return new AST.DebugCondition(mod, level, id);
2353
    }
2354

2355
    /**************************************
2356
     * Parse a version conditional
2357
     */
2358
    private AST.Condition parseVersionCondition()
2359
    {
2360 1
        uint level = 1;
2361 1
        Identifier id = null;
2362

2363 1
        if (token.value == TOK.leftParentheses)
2364
        {
2365 1
            nextToken();
2366
            /* Allow:
2367
             *    version (unittest)
2368
             *    version (assert)
2369
             * even though they are keywords
2370
             */
2371 1
            if (token.value == TOK.identifier)
2372 1
                id = token.ident;
2373 1
            else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2374 1
                level = cast(uint)token.unsvalue;
2375 1
            else if (token.value == TOK.unittest_)
2376 1
                id = Identifier.idPool(Token.toString(TOK.unittest_));
2377 1
            else if (token.value == TOK.assert_)
2378 1
                id = Identifier.idPool(Token.toString(TOK.assert_));
2379
            else
2380 0
                error("identifier or integer expected inside version(...), not `%s`", token.toChars());
2381 1
            nextToken();
2382 1
            check(TOK.rightParentheses);
2383
        }
2384
        else
2385 0
            error("(condition) expected following `version`");
2386 1
        return new AST.VersionCondition(mod, level, id);
2387
    }
2388

2389
    /***********************************************
2390
     *      static if (expression)
2391
     *          body
2392
     *      else
2393
     *          body
2394
     * Current token is 'static'.
2395
     */
2396
    private AST.Condition parseStaticIfCondition()
2397
    {
2398 1
        AST.Expression exp;
2399 1
        AST.Condition condition;
2400 1
        const loc = token.loc;
2401

2402 1
        nextToken();
2403 1
        nextToken();
2404 1
        if (token.value == TOK.leftParentheses)
2405
        {
2406 1
            nextToken();
2407 1
            exp = parseAssignExp();
2408 1
            check(TOK.rightParentheses);
2409
        }
2410
        else
2411
        {
2412 1
            error("(expression) expected following `static if`");
2413 1
            exp = null;
2414
        }
2415 1
        condition = new AST.StaticIfCondition(loc, exp);
2416 1
        return condition;
2417
    }
2418

2419
    /*****************************************
2420
     * Parse a constructor definition:
2421
     *      this(parameters) { body }
2422
     * or postblit:
2423
     *      this(this) { body }
2424
     * or constructor template:
2425
     *      this(templateparameters)(parameters) { body }
2426
     * Current token is 'this'.
2427
     */
2428
    private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2429
    {
2430 1
        AST.Expressions* udas = null;
2431 1
        const loc = token.loc;
2432 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2433

2434 1
        nextToken();
2435 1
        if (token.value == TOK.leftParentheses && peekNext() == TOK.this_ && peekNext2() == TOK.rightParentheses)
2436
        {
2437
            // this(this) { ... }
2438 1
            nextToken();
2439 1
            nextToken();
2440 1
            check(TOK.rightParentheses);
2441

2442 1
            stc = parsePostfix(stc, &udas);
2443 1
            if (stc & STC.immutable_)
2444 1
                deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2445 1
            if (stc & STC.shared_)
2446 1
                deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2447 1
            if (stc & STC.const_)
2448 1
                deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2449 1
            if (stc & STC.static_)
2450 1
                error(loc, "postblit cannot be `static`");
2451

2452 1
            auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2453 1
            AST.Dsymbol s = parseContracts(f);
2454 1
            if (udas)
2455
            {
2456 0
                auto a = new AST.Dsymbols();
2457 0
                a.push(f);
2458 0
                s = new AST.UserAttributeDeclaration(udas, a);
2459
            }
2460 1
            return s;
2461
        }
2462

2463
        /* Look ahead to see if:
2464
         *   this(...)(...)
2465
         * which is a constructor template
2466
         */
2467 1
        AST.TemplateParameters* tpl = null;
2468 1
        if (token.value == TOK.leftParentheses && peekPastParen(&token).value == TOK.leftParentheses)
2469
        {
2470 1
            tpl = parseTemplateParameterList();
2471
        }
2472

2473
        /* Just a regular constructor
2474
         */
2475 1
        auto parameterList = parseParameterList(null);
2476 1
        stc = parsePostfix(stc, &udas);
2477

2478 1
        if (parameterList.varargs != AST.VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2479
        {
2480 1
            if (stc & STC.static_)
2481 1
                error(loc, "constructor cannot be static");
2482
        }
2483 1
        else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2484
        {
2485 1
            if (ss == STC.static_)
2486 1
                error(loc, "use `static this()` to declare a static constructor");
2487 1
            else if (ss == (STC.shared_ | STC.static_))
2488 1
                error(loc, "use `shared static this()` to declare a shared static constructor");
2489
        }
2490

2491 1
        AST.Expression constraint = tpl ? parseConstraint() : null;
2492

2493 1
        AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2494 1
        tf = tf.addSTC(stc);
2495

2496 1
        auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2497 1
        AST.Dsymbol s = parseContracts(f);
2498 1
        if (udas)
2499
        {
2500 1
            auto a = new AST.Dsymbols();
2501 1
            a.push(f);
2502 1
            s = new AST.UserAttributeDeclaration(udas, a);
2503
        }
2504

2505 1
        if (tpl)
2506
        {
2507
            // Wrap a template around it
2508 1
            auto decldefs = new AST.Dsymbols();
2509 1
            decldefs.push(s);
2510 1
            s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2511
        }
2512

2513 1
        return s;
2514
    }
2515

2516
    /*****************************************
2517
     * Parse a destructor definition:
2518
     *      ~this() { body }
2519
     * Current token is '~'.
2520
     */
2521
    private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2522
    {
2523 1
        AST.Expressions* udas = null;
2524 1
        const loc = token.loc;
2525 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2526

2527 1
        nextToken();
2528 1
        check(TOK.this_);
2529 1
        check(TOK.leftParentheses);
2530 1
        check(TOK.rightParentheses);
2531

2532 1
        stc = parsePostfix(stc, &udas);
2533 1
        if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2534
        {
2535 1
            if (ss == STC.static_)
2536 1
                error(loc, "use `static ~this()` to declare a static destructor");
2537 1
            else if (ss == (STC.shared_ | STC.static_))
2538 1
                error(loc, "use `shared static ~this()` to declare a shared static destructor");
2539
        }
2540

2541 1
        auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2542 1
        AST.Dsymbol s = parseContracts(f);
2543 1
        if (udas)
2544
        {
2545 1
            auto a = new AST.Dsymbols();
2546 1
            a.push(f);
2547 1
            s = new AST.UserAttributeDeclaration(udas, a);
2548
        }
2549 1
        return s;
2550
    }
2551

2552
    /*****************************************
2553
     * Parse a static constructor definition:
2554
     *      static this() { body }
2555
     * Current token is 'static'.
2556
     */
2557
    private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2558
    {
2559
        //Expressions *udas = NULL;
2560 1
        const loc = token.loc;
2561 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2562

2563 1
        nextToken();
2564 1
        nextToken();
2565 1
        check(TOK.leftParentheses);
2566 1
        check(TOK.rightParentheses);
2567

2568 1
        stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2569 1
        if (stc & STC.shared_)
2570 1
            error(loc, "use `shared static this()` to declare a shared static constructor");
2571 1
        else if (stc & STC.static_)
2572 1
            appendStorageClass(stc, STC.static_); // complaint for the redundancy
2573 1
        else if (StorageClass modStc = stc & STC.TYPECTOR)
2574
        {
2575 1
            OutBuffer buf;
2576 1
            AST.stcToBuffer(&buf, modStc);
2577 1
            error(loc, "static constructor cannot be `%s`", buf.peekChars());
2578
        }
2579 1
        stc &= ~(STC.static_ | STC.TYPECTOR);
2580

2581 1
        auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2582 1
        AST.Dsymbol s = parseContracts(f);
2583 1
        return s;
2584
    }
2585

2586
    /*****************************************
2587
     * Parse a static destructor definition:
2588
     *      static ~this() { body }
2589
     * Current token is 'static'.
2590
     */
2591
    private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2592
    {
2593 1
        AST.Expressions* udas = null;
2594 1
        const loc = token.loc;
2595 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2596

2597 1
        nextToken();
2598 1
        nextToken();
2599 1
        check(TOK.this_);
2600 1
        check(TOK.leftParentheses);
2601 1
        check(TOK.rightParentheses);
2602

2603 1
        stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2604 1
        if (stc & STC.shared_)
2605 1
            error(loc, "use `shared static ~this()` to declare a shared static destructor");
2606 1
        else if (stc & STC.static_)
2607 0
            appendStorageClass(stc, STC.static_); // complaint for the redundancy
2608 1
        else if (StorageClass modStc = stc & STC.TYPECTOR)
2609
        {
2610 1
            OutBuffer buf;
2611 1
            AST.stcToBuffer(&buf, modStc);
2612 1
            error(loc, "static destructor cannot be `%s`", buf.peekChars());
2613
        }
2614 1
        stc &= ~(STC.static_ | STC.TYPECTOR);
2615

2616 1
        auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2617 1
        AST.Dsymbol s = parseContracts(f);
2618 1
        if (udas)
2619
        {
2620 1
            auto a = new AST.Dsymbols();
2621 1
            a.push(f);
2622 1
            s = new AST.UserAttributeDeclaration(udas, a);
2623
        }
2624 1
        return s;
2625
    }
2626

2627
    /*****************************************
2628
     * Parse a shared static constructor definition:
2629
     *      shared static this() { body }
2630
     * Current token is 'shared'.
2631
     */
2632
    private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2633
    {
2634
        //Expressions *udas = NULL;
2635 1
        const loc = token.loc;
2636 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2637

2638 1
        nextToken();
2639 1
        nextToken();
2640 1
        nextToken();
2641 1
        check(TOK.leftParentheses);
2642 1
        check(TOK.rightParentheses);
2643

2644 1
        stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2645 1
        if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2646 1
            appendStorageClass(stc, ss); // complaint for the redundancy
2647 1
        else if (StorageClass modStc = stc & STC.TYPECTOR)
2648
        {
2649 1
            OutBuffer buf;
2650 1
            AST.stcToBuffer(&buf, modStc);
2651 1
            error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2652
        }
2653 1
        stc &= ~(STC.static_ | STC.TYPECTOR);
2654

2655 1
        auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2656 1
        AST.Dsymbol s = parseContracts(f);
2657 1
        return s;
2658
    }
2659

2660
    /*****************************************
2661
     * Parse a shared static destructor definition:
2662
     *      shared static ~this() { body }
2663
     * Current token is 'shared'.
2664
     */
2665
    private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2666
    {
2667 1
        AST.Expressions* udas = null;
2668 1
        const loc = token.loc;
2669 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2670

2671 1
        nextToken();
2672 1
        nextToken();
2673 1
        nextToken();
2674 1
        check(TOK.this_);
2675 1
        check(TOK.leftParentheses);
2676 1
        check(TOK.rightParentheses);
2677

2678 1
        stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2679 1
        if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2680 0
            appendStorageClass(stc, ss); // complaint for the redundancy
2681 1
        else if (StorageClass modStc = stc & STC.TYPECTOR)
2682
        {
2683 1
            OutBuffer buf;
2684 1
            AST.stcToBuffer(&buf, modStc);
2685 1
            error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2686
        }
2687 1
        stc &= ~(STC.static_ | STC.TYPECTOR);
2688

2689 1
        auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2690 1
        AST.Dsymbol s = parseContracts(f);
2691 1
        if (udas)
2692
        {
2693 1
            auto a = new AST.Dsymbols();
2694 1
            a.push(f);
2695 1
            s = new AST.UserAttributeDeclaration(udas, a);
2696
        }
2697 1
        return s;
2698
    }
2699

2700
    /*****************************************
2701
     * Parse an invariant definition:
2702
     *      invariant { statements... }
2703
     *      invariant() { statements... }
2704
     *      invariant (expression);
2705
     * Current token is 'invariant'.
2706
     */
2707
    private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2708
    {
2709 1
        const loc = token.loc;
2710 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2711

2712 1
        nextToken();
2713 1
        if (token.value == TOK.leftParentheses) // optional () or invariant (expression);
2714
        {
2715 1
            nextToken();
2716 1
            if (token.value != TOK.rightParentheses) // invariant (expression);
2717
            {
2718 1
                AST.Expression e = parseAssignExp(), msg = null;
2719 1
                if (token.value == TOK.comma)
2720
                {
2721 1
                    nextToken();
2722 1
                    if (token.value != TOK.rightParentheses)
2723
                    {
2724 1
                        msg = parseAssignExp();
2725 1
                        if (token.value == TOK.comma)
2726 1
                            nextToken();
2727
                    }
2728
                }
2729 1
                check(TOK.rightParentheses);
2730 1
                check(TOK.semicolon);
2731 1
                e = new AST.AssertExp(loc, e, msg);
2732 1
                auto fbody = new AST.ExpStatement(loc, e);
2733 1
                auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2734 1
                return f;
2735
            }
2736 1
            nextToken();
2737
        }
2738

2739 1
        auto fbody = parseStatement(ParseStatementFlags.curly);
2740 1
        auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2741 1
        return f;
2742
    }
2743

2744
    /*****************************************
2745
     * Parse a unittest definition:
2746
     *      unittest { body }
2747
     * Current token is 'unittest'.
2748
     */
2749
    private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2750
    {
2751 1
        const loc = token.loc;
2752 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2753

2754 1
        nextToken();
2755

2756 1
        const(char)* begPtr = token.ptr + 1; // skip '{'
2757 1
        const(char)* endPtr = null;
2758 1
        AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2759

2760
        /** Extract unittest body as a string. Must be done eagerly since memory
2761
         will be released by the lexer before doc gen. */
2762 1
        char* docline = null;
2763 1
        if (global.params.doDocComments && endPtr > begPtr)
2764
        {
2765
            /* Remove trailing whitespaces */
2766 1
            for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2767
            {
2768 1
                endPtr = p;
2769
            }
2770

2771 1
            size_t len = endPtr - begPtr;
2772 1
            if (len > 0)
2773
            {
2774 1
                docline = cast(char*)mem.xmalloc_noscan(len + 2);
2775 1
                memcpy(docline, begPtr, len);
2776 1
                docline[len] = '\n'; // Terminate all lines by LF
2777 1
                docline[len + 1] = '\0';
2778
            }
2779
        }
2780

2781 1
        auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2782 1
        f.fbody = sbody;
2783 1
        return f;
2784
    }
2785

2786
    /*****************************************
2787
     * Parse a new definition:
2788
     *      new(parameters) { body }
2789
     * Current token is 'new'.
2790
     */
2791
    private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2792
    {
2793 1
        const loc = token.loc;
2794 1
        StorageClass stc = getStorageClass!AST(pAttrs);
2795

2796 1
        nextToken();
2797

2798 1
        auto parameterList = parseParameterList(null);
2799 1
        auto f = new AST.NewDeclaration(loc, Loc.initial, stc, parameterList);
2800 1
        AST.Dsymbol s = parseContracts(f);
2801 1
        return s;
2802
    }
2803

2804
    /**********************************************
2805
     * Parse parameter list.
2806
     */
2807
    private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2808
    {
2809 1
        auto parameters = new AST.Parameters();
2810 1
        AST.VarArg varargs = AST.VarArg.none;
2811 1
        int hasdefault = 0;
2812 1
        StorageClass varargsStc;
2813

2814
        // Attributes allowed for ...
2815
        enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_;
2816

2817 1
        check(TOK.leftParentheses);
2818