fix(parser): parse nested-proc-first bodies in unit impl routines (Andrew 82e770ac)
A unit implementation-section routine whose body opens with a nested
procedure/function declaration (before any var/const/type section) was
mis-parsed: ParseUnit called ParseMethodDecl without ACanHaveNestedProcs, so a
leading 'procedure'/'function' was treated as the next top-level decl and
parsing failed at the outer 'begin' ("Expected 'end' but got 'begin'").
Pass ACanHaveNestedProcs=True for the two impl-section ParseMethodDecl calls.
The IsForward guard already present in ParseMethodDecl (our commit 33db0f6)
keeps a forward decl from claiming the following sibling as its nested body, so
only the call-site change was needed — Andrew's branch independently added the
same forward-flag mechanism, which we already have.
Note: this is a PARSER fix (the unit body now parses correctly). Resolving a
CALL to such a nested procedure is a separate, pre-existing semantic-pass gap
that affects program routines too (Inner() reports "Cannot find declaration");
it is out of scope here. Andrew's tests are likewise parse-level.
Tests: cp.test.units.pas parse-level tests (outer routine is one decl with a
body holding the nested proc).
Cherry-pick policy: the forward-flag half was a duplicate of our work and
skipped; the ACanHaveNestedProcs threading + tests are taken.
Verified: FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK; full suite green
on both the QBE-built and native-built runners (3717 tests).
This commit is contained in:
parent
54cfe37e62
commit
cf994dcca4
|
|
@ -3954,7 +3954,7 @@ begin
|
|||
Check(tkVar) or Check(tkThreadVar) or Check(tkConst) or Check(tkType) do
|
||||
begin
|
||||
if Check(tkFunction) then
|
||||
Result.ImplBlock.ProcDecls.Add(ParseMethodDecl(True))
|
||||
Result.ImplBlock.ProcDecls.Add(ParseMethodDecl(True, True))
|
||||
else if Check(tkVar) or Check(tkThreadVar) then
|
||||
ParseVarBlock(Result.ImplBlock)
|
||||
else if Check(tkConst) then
|
||||
|
|
@ -3962,7 +3962,12 @@ begin
|
|||
else if Check(tkType) then
|
||||
ParseTypeSection(Result.ImplBlock)
|
||||
else
|
||||
Result.ImplBlock.ProcDecls.Add(ParseMethodDecl(False));
|
||||
{ ACanHaveNestedProcs=True: an impl routine whose body opens with a
|
||||
nested procedure/function (before any var/const/type) must parse as
|
||||
one routine WITH a body, not split into two bodyless decls. The
|
||||
IsForward guard inside ParseMethodDecl stops a forward decl from
|
||||
swallowing the following sibling. (Andrew's fix 82e770ac.) }
|
||||
Result.ImplBlock.ProcDecls.Add(ParseMethodDecl(False, True));
|
||||
end;
|
||||
|
||||
{ Optional initialization / finalization sections }
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@ type
|
|||
procedure TestParse_Unit_ForwardDeclHasNilBody;
|
||||
procedure TestParse_Unit_ImplHasFullBodies;
|
||||
procedure TestParse_Unit_ImplBodyIsNotNil;
|
||||
{ Nested procedure as the FIRST item in an impl routine's body must not be
|
||||
mistaken for a second top-level decl (impl-section parser). Andrew's
|
||||
fix 82e770ac. }
|
||||
procedure TestParse_Unit_ImplNestedProcFirst_SingleDecl;
|
||||
procedure TestParse_Unit_ImplNestedProcFirst_HasBody;
|
||||
procedure TestParse_Unit_IntfTypeDecl;
|
||||
procedure TestParse_Unit_ForwardDeclParamCount;
|
||||
procedure TestParse_Unit_MultipleForwardDecls;
|
||||
|
|
@ -205,6 +210,29 @@ const
|
|||
end.
|
||||
''';
|
||||
|
||||
{ Implementation-section routine whose body leads with a nested procedure
|
||||
declaration (no preceding var/const/type). The outer routine must parse
|
||||
as a single decl WITH a body, not split into two bodyless decls. }
|
||||
SrcUnitNestedProcFirst =
|
||||
'''
|
||||
unit Nested;
|
||||
interface
|
||||
function Outer(N: Integer): Integer;
|
||||
implementation
|
||||
function Outer(N: Integer): Integer;
|
||||
function Inner(X: Integer): Integer;
|
||||
begin
|
||||
Result := X + 1
|
||||
end;
|
||||
var
|
||||
T: Integer;
|
||||
begin
|
||||
T := Inner(N);
|
||||
Result := T
|
||||
end;
|
||||
end.
|
||||
''';
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Lexer tests }
|
||||
{ ------------------------------------------------------------------ }
|
||||
|
|
@ -299,6 +327,28 @@ begin
|
|||
finally U.Free(); end;
|
||||
end;
|
||||
|
||||
procedure TUnitTests.TestParse_Unit_ImplNestedProcFirst_SingleDecl;
|
||||
var U: TUnit;
|
||||
begin
|
||||
U := ParseUnit(SrcUnitNestedProcFirst);
|
||||
try
|
||||
AssertEquals('impl has 1 decl (outer routine only)',
|
||||
1, U.ImplBlock.ProcDecls.Count);
|
||||
finally U.Free(); end;
|
||||
end;
|
||||
|
||||
procedure TUnitTests.TestParse_Unit_ImplNestedProcFirst_HasBody;
|
||||
var U: TUnit; MD: TMethodDecl;
|
||||
begin
|
||||
U := ParseUnit(SrcUnitNestedProcFirst);
|
||||
try
|
||||
MD := TMethodDecl(U.ImplBlock.ProcDecls[0]);
|
||||
AssertNotNull('outer routine has a body', MD.Body);
|
||||
AssertEquals('outer body holds the nested proc',
|
||||
1, MD.Body.ProcDecls.Count);
|
||||
finally U.Free(); end;
|
||||
end;
|
||||
|
||||
procedure TUnitTests.TestParse_Unit_IntfTypeDecl;
|
||||
var U: TUnit;
|
||||
begin
|
||||
|
|
|
|||
Loading…
Reference in a new issue