From 5ee9328d0be8176afbc4ad098b579b7880f6a013 Mon Sep 17 00:00:00 2001 From: 0-v-0 Date: Mon, 25 May 2026 09:17:36 +0800 Subject: [PATCH] Fix dlang-community/D-Scanner#895 --- src/dparse/ast.d | 27 +++++++++++++++++++++++++++ src/dparse/parser.d | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/dparse/ast.d b/src/dparse/ast.d index 6ab70d84..a89907e2 100644 --- a/src/dparse/ast.d +++ b/src/dparse/ast.d @@ -4195,6 +4195,33 @@ unittest // issue #526 assert(ed.type.typeSuffixes.length == 2); } +unittest // issue #895 +{ + import dparse.lexer, dparse.parser, dparse.rollback_allocator; + + string src = q{ + private immutable bool hasPreviewIn = ((in int[256] a) { return __traits(isRef, a); })(int[256].init); + private enum Foo = int[256].init; + }; + + RollbackAllocator ra; + auto cf = LexerConfig("", StringBehavior.source); + auto ca = StringCache(16); + uint errorCount; + Module m = parseModule(getTokensForParser(src, cf, &ca), "", &ra, null, &errorCount); + + assert(m); + assert(errorCount == 0); + assert(m.declarations.length == 2); + auto decl = m.declarations[0].variableDeclaration; + assert(decl); + auto init = decl.declarators[0].initializer; + assert(init); + assert(init.nonVoidInitializer); + assert(init.nonVoidInitializer.assignExpression); + assert(m.declarations[1].variableDeclaration); +} + unittest // Differentiate between no and empty DDOC comments, e.g. for DDOC unittests { import dparse.lexer, dparse.parser, dparse.rollback_allocator; diff --git a/src/dparse/parser.d b/src/dparse/parser.d index 93b5df3f..9884e0e8 100644 --- a/src/dparse/parser.d +++ b/src/dparse/parser.d @@ -5902,6 +5902,36 @@ class Parser * | $(LITERAL IstringLiteral) * ;) */ + Type parsePrimaryExpressionType() + { + auto startIndex = index; + auto node = allocator.make!Type; + if (!moreTokens) + { + error("type expected"); + return null; + } + switch (current.type) + { + case tok!"const": + case tok!"immutable": + case tok!"inout": + case tok!"shared": + if (!peekIs(tok!"(")) + mixin(parseNodeQ!(`node.typeConstructors`, `TypeConstructors`)); + break; + default: + } + mixin(parseNodeQ!(`node.type2`, `Type2`)); + StackBuffer typeSuffixes; + while (moreTokens() && currentIsOneOf(tok!"[", tok!"*", tok!"delegate", tok!"function")) + if (!typeSuffixes.put(parseTypeSuffix())) + return null; + ownArray(node.typeSuffixes, typeSuffixes); + node.tokens = tokens[startIndex .. index]; + return node; + } + PrimaryExpression parsePrimaryExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); @@ -5930,7 +5960,7 @@ class Parser { node.typeConstructor = advance(); mixin(tokenCheck!"("); - mixin(parseNodeQ!(`node.type`, `Type`)); + mixin(parseNodeQ!(`node.type`, `PrimaryExpressionType`)); mixin(tokenCheck!")"); mixin(tokenCheck!"."); const ident = expect(tok!"identifier"); @@ -5940,7 +5970,7 @@ class Parser } foreach (B; BasicTypes) { case B: } { - node.type = parseType(); + node.type = parsePrimaryExpressionType(); if (currentIs(tok!".")) { advance();