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