diff --git a/compiler/src/main/pascal/uParser.pas b/compiler/src/main/pascal/uParser.pas index 491b325..a01db4c 100644 --- a/compiler/src/main/pascal/uParser.pas +++ b/compiler/src/main/pascal/uParser.pas @@ -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 } diff --git a/compiler/src/test/pascal/cp.test.units.pas b/compiler/src/test/pascal/cp.test.units.pas index 95c3ec9..ed5e07c 100644 --- a/compiler/src/test/pascal/cp.test.units.pas +++ b/compiler/src/test/pascal/cp.test.units.pas @@ -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