Jelajahi Sumber

fcl-passrc: optional parse units queued instead of recursively

git-svn-id: trunk@38414 -
Mattias Gaertner 7 tahun lalu
induk
melakukan
d34e9b79bf

+ 62 - 28
packages/fcl-passrc/src/pasresolver.pp

@@ -665,6 +665,7 @@ type
       Data: Pointer; var Abort: boolean);
   public
     UsesScopes: TFPList; // list of TPasSectionScope
+    UsesFinished: boolean;
     Finished: boolean;
     constructor Create; override;
     destructor Destroy; override;
@@ -1434,8 +1435,9 @@ type
     function GetVisibilityContext: TPasElement;
     procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); override;
     function IsUnitIntfFinished(AModule: TPasModule): boolean;
+    procedure NotifyPendingUsedInterfaces; virtual;
     function GetPendingUsedInterface(Section: TPasSection): TPasUsesUnit;
-    procedure CheckPendingUsedInterface(Section: TPasSection); override;
+    function CheckPendingUsedInterface(Section: TPasSection): boolean; override;
     procedure ContinueParsing; virtual;
     function NeedArrayValues(El: TPasElement): boolean; override;
     function GetDefaultClassVisibility(AClass: TPasClassType
@@ -4045,6 +4047,10 @@ begin
   {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.FinishUsesClause Section=',Section.ClassName,' Section.UsesList.Count=',Section.UsesList.Count);
   {$ENDIF}
+  if Scope.UsesFinished then
+    RaiseInternalError(20180305145220);
+  Scope.UsesFinished:=true;
+
   for i:=0 to Section.UsesList.Count-1 do
     begin
     UseUnit:=Section.UsesClause[i];
@@ -4055,8 +4061,8 @@ begin
 
     // check used unit
     PublicEl:=nil;
-    if (UseModule.ClassType=TLibrarySection) then
-      PublicEl:=UseModule
+    if (UseModule.ClassType=TPasLibrary) then
+      PublicEl:=TPasLibrary(UseModule).LibrarySection
     else if (UseModule.ClassType=TPasModule) then
       PublicEl:=TPasModule(UseModule).InterfaceSection
     else
@@ -4131,29 +4137,12 @@ begin
 end;
 
 procedure TPasResolver.FinishInterfaceSection(Section: TPasSection);
-var
-  ModuleScope: TPasModuleScope;
-  PendingResolver: TPasResolver;
-  PendingParser: TPasParser;
-  PendingModule: TPasModule;
-  PendingImpl: TImplementationSection;
 begin
   {$IFDEF VerbosePasResolver}
   if not IsUnitIntfFinished(Section.GetModule) then
     RaiseInternalError(20171214004323,'TPasResolver.FinishInterfaceSection "'+CurrentParser.CurModule.Name+'" "'+Section.GetModule.Name+'" IsUnitIntfFinished=false');
   {$ENDIF}
-  ModuleScope:=CurrentParser.CurModule.CustomData as TPasModuleScope;
-  while ModuleScope.PendingResolvers.Count>0 do
-    begin
-    PendingResolver:=TObject(ModuleScope.PendingResolvers[0]) as TPasResolver;
-    PendingParser:=PendingResolver.CurrentParser;
-    PendingModule:=PendingParser.CurModule;
-    PendingImpl:=PendingModule.ImplementationSection;
-    {$IFDEF VerbosePasResolver}
-    writeln('TPasResolver.FinishInterfaceSection "',ModuleScope.Element.Name,'" Pending="',PendingModule.Name,'"');
-    {$ENDIF}
-    PendingResolver.CheckPendingUsedInterface(PendingImpl);
-    end;
+  NotifyPendingUsedInterfaces;
   if Section=nil then ;
 end;
 
@@ -11835,6 +11824,45 @@ begin
       and TPasSectionScope(CurIntf.CustomData).Finished;
 end;
 
+procedure TPasResolver.NotifyPendingUsedInterfaces;
+// called after unit interface is ready to be used by other modules
+var
+  ModuleScope: TPasModuleScope;
+  i: Integer;
+  PendingResolver: TPasResolver;
+  PendingParser: TPasParser;
+  PendingSection: TPasSection;
+  Changed: Boolean;
+begin
+  // call all PendingResolvers
+  // Note that a waiting resolver might continue parsing, so this
+  // recursively solves all unit cycles
+  ModuleScope:=RootElement.CustomData as TPasModuleScope;
+  i:=ModuleScope.PendingResolvers.Count-1;
+  while i>=0 do
+    begin
+    PendingResolver:=TObject(ModuleScope.PendingResolvers[i]) as TPasResolver;
+    PendingParser:=PendingResolver.CurrentParser;
+    PendingSection:=PendingParser.GetLastSection;
+    {$IFDEF VerbosePasResolver}
+    writeln('TPasResolver.FinishInterfaceSection "',ModuleScope.Element.Name,'" Pending="',PendingResolver.RootElement.Name,'"');
+    {$ENDIF}
+    if PendingSection=nil then
+      RaiseInternalError(20180305141421);
+    Changed:=PendingResolver.CheckPendingUsedInterface(PendingSection); // beware: this might alter the ModuleScope.PendingResolvers
+    if Changed and (PendingSection.PendingUsedIntf=nil) then
+      begin
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasResolver.FinishInterfaceSection "',ModuleScope.Element.Name,'" Continue="',PendingResolver.RootElement.Name,'"');
+      {$ENDIF}
+      PendingParser.ParseContinue;
+      end;
+    dec(i);
+    if i>=ModuleScope.PendingResolvers.Count then
+      i:=ModuleScope.PendingResolvers.Count-1;
+    end;
+end;
+
 function TPasResolver.GetPendingUsedInterface(Section: TPasSection
   ): TPasUsesUnit;
 var
@@ -11842,7 +11870,6 @@ var
   UseUnit: TPasUsesUnit;
 begin
   Result:=nil;
-  if not (Section is TImplementationSection) then exit;
   for i:=0 to length(Section.UsesClause)-1 do
     begin
     UseUnit:=Section.UsesClause[i];
@@ -11852,7 +11879,7 @@ begin
     end;
 end;
 
-procedure TPasResolver.CheckPendingUsedInterface(Section: TPasSection);
+function TPasResolver.CheckPendingUsedInterface(Section: TPasSection): boolean;
 var
   PendingModule: TPasModule;
   PendingModuleScope: TPasModuleScope;
@@ -11860,7 +11887,7 @@ var
   WasPending: Boolean;
 begin
   {$IFDEF VerbosePasResolver}
-  //writeln('TPasResolver.CheckPendingUsedInterface START "',CurrentParser.CurModule.Name,'"');
+  //writeln('TPasResolver.CheckPendingUsedInterface START "',CurrentParser.CurModule.Name,'" Section.PendingUsedIntf=',Section.PendingUsedIntf<>nil);
   {$ENDIF}
   WasPending:=Section.PendingUsedIntf<>nil;
   if WasPending then
@@ -11869,6 +11896,9 @@ begin
     if not IsUnitIntfFinished(PendingModule) then
       exit; // still pending
     // other unit interface is finished
+    {$IFDEF VerbosePasResolver}
+    writeln('TPasResolver.CheckPendingUsedInterface "',CurrentParser.CurModule.Name,'" UnitIntf finished of "',PendingModule.Name,'"');
+    {$ENDIF}
     PendingModuleScope:=NoNil(PendingModule.CustomData) as TPasModuleScope;
     PendingModuleScope.PendingResolvers.Remove(Self);
     Section.PendingUsedIntf:=nil;
@@ -11877,7 +11907,7 @@ begin
   Section.PendingUsedIntf:=GetPendingUsedInterface(Section);
   if Section.PendingUsedIntf<>nil then
     begin
-    // unit not yet finished due to pending used interfaces
+    // module not yet finished due to pending used interfaces
     PendingModule:=Section.PendingUsedIntf.Module as TPasModule;
     PendingModuleScope:=NoNil(PendingModule.CustomData) as TPasModuleScope;
     {$IFDEF VerbosePasResolver}
@@ -11886,12 +11916,16 @@ begin
     List:=PendingModuleScope.PendingResolvers;
     if List.IndexOf(Self)<0 then
       List.Add(Self);
+    Result:=not WasPending;
     end
   else
     begin
+    {$IFDEF VerbosePasResolver}
     if WasPending then
-      // can now continue parsing
-      ContinueParsing;
+      writeln('TPasResolver.CheckPendingUsedInterface "',CurrentParser.CurModule.Name,'" uses section complete: ',Section.ClassName);
+    {$ENDIF}
+
+    Result:=WasPending;
     end;
 end;
 
@@ -11902,7 +11936,7 @@ begin
   {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.ContinueParsing "',CurrentParser.CurModule.Name,'"...');
   {$ENDIF}
-  CurrentParser.ParseContinueImplementation;
+  CurrentParser.ParseContinue;
 end;
 
 function TPasResolver.NeedArrayValues(El: TPasElement): boolean;

+ 152 - 25
packages/fcl-passrc/src/pparser.pp

@@ -192,7 +192,7 @@ type
     procedure FinishScope(ScopeType: TPasScopeType; El: TPasElement); virtual;
     function FindModule(const AName: String): TPasModule; virtual;
     function FindModule(const AName: String; NameExpr, InFileExpr: TPasExpr): TPasModule; virtual;
-    procedure CheckPendingUsedInterface(Section: TPasSection); virtual;
+    function CheckPendingUsedInterface(Section: TPasSection): boolean; virtual; // true if changed
     function NeedArrayValues(El: TPasElement): boolean; virtual;
     function GetDefaultClassVisibility(AClass: TPasClassType): TPasMemberVisibility; virtual;
     property Package: TPasPackage read FPackage;
@@ -401,7 +401,9 @@ type
     // Main scope parsing
     procedure ParseMain(var Module: TPasModule);
     procedure ParseUnit(var Module: TPasModule);
-    procedure ParseContinueImplementation;
+    function GetLastSection: TPasSection; virtual;
+    function CanParseContinue(out Section: TPasSection): boolean; virtual;
+    procedure ParseContinue; virtual;
     procedure ParseProgram(var Module: TPasModule; SkipHeader : Boolean = False);
     procedure ParseLibrary(var Module: TPasModule);
     procedure ParseOptionalUsesList(ASection: TPasSection);
@@ -778,9 +780,11 @@ begin
   if InFileExpr=nil then ;
 end;
 
-procedure TPasTreeContainer.CheckPendingUsedInterface(Section: TPasSection);
+function TPasTreeContainer.CheckPendingUsedInterface(Section: TPasSection
+  ): boolean;
 begin
   if Section=nil then ;  // avoid compiler warning
+  Result:=false;
 end;
 
 function TPasTreeContainer.NeedArrayValues(El: TPasElement): boolean;
@@ -2727,39 +2731,137 @@ begin
       end;
     CheckHint(Module,True);
     ExpectToken(tkInterface);
-    If LogEvent(pleInterface) then
-      DoLog(mtInfo,nLogStartInterface,SLogStartInterface);
+    if po_StopOnUnitInterface in Options then
+      begin
+      HasFinished:=false;
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasParser.ParseUnit pause parsing after unit name ',CurModule.Name);
+      {$ENDIF}
+      exit;
+      end;
     ParseInterface;
+    if (Module.InterfaceSection<>nil)
+        and (Module.InterfaceSection.PendingUsedIntf<>nil) then
+      begin
+      HasFinished:=false;
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasParser.ParseUnit pause parsing after interface uses list ',CurModule.Name);
+      {$ENDIF}
+      end;
     if (Module.ImplementationSection<>nil)
         and (Module.ImplementationSection.PendingUsedIntf<>nil) then
+      begin
       HasFinished:=false;
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasParser.ParseUnit pause parsing after implementation uses list ',CurModule.Name);
+      {$ENDIF}
+      end;
     if HasFinished then
       Engine.FinishScope(stModule,Module);
   finally
     if HasFinished then
-      FCurModule:=nil;
+      FCurModule:=nil; // clear module if there is an error or finished parsing
   end;
 end;
 
-procedure TPasParser.ParseContinueImplementation;
+function TPasParser.GetLastSection: TPasSection;
+begin
+  Result:=nil;
+  if FCurModule=nil then
+    exit; // parse completed
+  if CurModule is TPasProgram then
+    Result:=TPasProgram(CurModule).ProgramSection
+  else if CurModule is TPasLibrary then
+    Result:=TPasLibrary(CurModule).LibrarySection
+  else if (CurModule.ClassType=TPasModule) or (CurModule is TPasUnitModule) then
+    begin
+    if CurModule.ImplementationSection<>nil then
+      Result:=CurModule.ImplementationSection
+    else
+      Result:=CurModule.InterfaceSection; // might be nil
+    end;
+end;
+
+function TPasParser.CanParseContinue(out Section: TPasSection): boolean;
+begin
+  Result:=false;
+  Section:=nil;
+  if FCurModule=nil then
+    exit; // parse completed
+  if (LastMsg<>'') and (LastMsgType<=mtError) then
+    begin
+    {$IFDEF VerbosePasResolver}
+    writeln('TPasParser.CanParseContinue ',CurModule.Name,' LastMsg="',LastMsgType,':',LastMsg,'"');
+    {$ENDIF}
+    exit;
+    end;
+  if (Scanner.LastMsg<>'') and (Scanner.LastMsgType<=mtError) then
+    begin
+    {$IFDEF VerbosePasResolver}
+    writeln('TPasParser.CanParseContinue ',CurModule.Name,' Scanner.LastMsg="',Scanner.LastMsgType,':',Scanner.LastMsg,'"');
+    {$ENDIF}
+    exit;
+    end;
+
+  Section:=GetLastSection;
+  if Section=nil then
+    if (po_StopOnUnitInterface in Options)
+        and ((CurModule is TPasUnitModule) or (CurModule.ClassType=TPasModule))
+        and (CurModule.InterfaceSection=nil) then
+      exit(true)
+    else
+      exit(false);
+  Result:=Section.PendingUsedIntf=nil;
+end;
+
+procedure TPasParser.ParseContinue;
+// continue parsing after stopped due to pending uses
+var
+  Section: TPasSection;
+  HasFinished: Boolean;
 begin
+  if CurModule=nil then
+    ParseExcTokenError('TPasParser.ParseContinue missing module');
+  {$IFDEF VerbosePasParser}
+  writeln('TPasParser.ParseContinue ',CurModule.Name);
+  {$ENDIF}
+  if not CanParseContinue(Section) then
+    ParseExcTokenError('TPasParser.ParseContinue missing section');
+  HasFinished:=true;
   try
-    ParseDeclarations(CurModule.ImplementationSection);
-    Engine.FinishScope(stModule,CurModule);
+    if Section=nil then
+      begin
+      // continue after unit name
+      ParseInterface;
+      end
+    else
+      begin
+      // continue after uses clause
+      Engine.FinishScope(stUsesClause,Section);
+      ParseDeclarations(Section);
+      end;
+    Section:=GetLastSection;
+    if (Section<>nil) and (Section.PendingUsedIntf<>nil) then
+      HasFinished:=false;
+    if HasFinished then
+      Engine.FinishScope(stModule,CurModule);
   finally
-    FCurModule:=nil;
+    if HasFinished then
+      FCurModule:=nil; // clear module if there is an error or finished parsing
   end;
 end;
 
 // Starts after the "program" token
 procedure TPasParser.ParseProgram(var Module: TPasModule; SkipHeader : Boolean = False);
-
 Var
   PP : TPasProgram;
   Section : TProgramSection;
   N : String;
   StartPos: TPasSourcePos;
-
+  HasFinished: Boolean;
+  {$IFDEF VerbosePasResolver}
+  aSection: TPasSection;
+  {$ENDIF}
 begin
   StartPos:=CurTokenPos;
   if SkipHeader then
@@ -2779,6 +2881,7 @@ begin
   Module := nil;
   PP:=TPasProgram(CreateElement(TPasProgram, N, Engine.Package, StartPos));
   Module :=PP;
+  HasFinished:=true;
   FCurModule:=Module;
   try
     if Assigned(Engine.Package) then
@@ -2806,10 +2909,26 @@ begin
     Section := TProgramSection(CreateElement(TProgramSection, '', CurModule));
     PP.ProgramSection := Section;
     ParseOptionalUsesList(Section);
+    HasFinished:=Section.PendingUsedIntf=nil;
+    if not HasFinished then
+      begin
+      {$IFDEF VerbosePasResolver}
+      writeln('TPasParser.ParseProgram pause parsing after uses list of "',CurModule.Name,'"');
+      if CanParseContinue(aSection) then
+        begin
+        writeln('TPasParser.ParseProgram Section=',Section.ClassName,' Section.PendingUsedIntf=',Section.PendingUsedIntf<>nil);
+        if aSection<>nil then
+          writeln('TPasParser.ParseProgram aSection=',aSection.ClassName,' ',Section=aSection);
+        ParseExc(nErrNoSourceGiven,'[20180305172432] ');
+        end;
+      {$ENDIF}
+      exit;
+      end;
     ParseDeclarations(Section);
     Engine.FinishScope(stModule,Module);
   finally
-    FCurModule:=nil;
+    if HasFinished then
+      FCurModule:=nil; // clear module if there is an error or finished parsing
   end;
 end;
 
@@ -2820,6 +2939,7 @@ Var
   Section : TLibrarySection;
   N: String;
   StartPos: TPasSourcePos;
+  HasFinished: Boolean;
 
 begin
   StartPos:=CurTokenPos;
@@ -2835,6 +2955,7 @@ begin
   Module := nil;
   PP:=TPasLibrary(CreateElement(TPasLibrary, N, Engine.Package, StartPos));
   Module :=PP;
+  HasFinished:=true;
   FCurModule:=Module;
   try
     if Assigned(Engine.Package) then
@@ -2848,10 +2969,14 @@ begin
     Section := TLibrarySection(CreateElement(TLibrarySection, '', CurModule));
     PP.LibrarySection := Section;
     ParseOptionalUsesList(Section);
+    HasFinished:=Section.PendingUsedIntf=nil;
+    if not HasFinished then
+      exit;
     ParseDeclarations(Section);
     Engine.FinishScope(stModule,Module);
   finally
-    FCurModule:=nil;
+    if HasFinished then
+      FCurModule:=nil; // clear module if there is an error or finished parsing
   end;
 end;
 
@@ -2859,13 +2984,15 @@ procedure TPasParser.ParseOptionalUsesList(ASection: TPasSection);
 // checks if next token is Uses keyword and reads the uses list
 begin
   NextToken;
+  CheckImplicitUsedUnits(ASection);
   if CurToken=tkuses then
     ParseUsesList(ASection)
-  else begin
-    CheckImplicitUsedUnits(ASection);
-    Engine.FinishScope(stUsesClause,ASection);
+  else
     UngetToken;
-  end;
+  Engine.CheckPendingUsedInterface(ASection);
+  if ASection.PendingUsedIntf<>nil then
+    exit;
+  Engine.FinishScope(stUsesClause,ASection);
 end;
 
 // Starts after the "interface" token
@@ -2873,9 +3000,13 @@ procedure TPasParser.ParseInterface;
 var
   Section: TInterfaceSection;
 begin
+  If LogEvent(pleInterface) then
+    DoLog(mtInfo,nLogStartInterface,SLogStartInterface);
   Section := TInterfaceSection(CreateElement(TInterfaceSection, '', CurModule));
   CurModule.InterfaceSection := Section;
   ParseOptionalUsesList(Section);
+  if Section.PendingUsedIntf<>nil then
+    exit;
   ParseDeclarations(Section); // this also parses the Implementation section
 end;
 
@@ -2887,7 +3018,6 @@ begin
   Section := TImplementationSection(CreateElement(TImplementationSection, '', CurModule));
   CurModule.ImplementationSection := Section;
   ParseOptionalUsesList(Section);
-  Engine.CheckPendingUsedInterface(Section);
   if Section.PendingUsedIntf<>nil then
     exit;
   ParseDeclarations(Section);
@@ -3412,8 +3542,6 @@ var
   NamePos, SrcPos: TPasSourcePos;
   aModule: TPasModule;
 begin
-  CheckImplicitUsedUnits(ASection);
-
   NameExpr:=nil;
   InFileExpr:=nil;
   FreeExpr:=true;
@@ -3439,8 +3567,9 @@ begin
         if (msDelphi in CurrentModeswitches) then
           begin
           aModule:=ASection.GetModule;
-          if (aModule<>nil) and ((aModule.ClassType=TPasModule) or (aModule is TPasUnitModule)) then
-            CheckToken(tkSemicolon); // delphi does not allow it in units
+          if (aModule<>nil)
+              and ((aModule.ClassType=TPasModule) or (aModule is TPasUnitModule)) then
+            CheckToken(tkSemicolon); // delphi does not allow in-filename in units
           end;
         ExpectToken(tkString);
         InFileExpr:=CreatePrimitiveExpr(ASection,pekString,CurTokenString);
@@ -3461,8 +3590,6 @@ begin
       ReleaseAndNil(TPasElement(InFileExpr));
       end;
   end;
-
-  Engine.FinishScope(stUsesClause,ASection);
 end;
 
 // Starts after the variable name

+ 5 - 4
packages/fcl-passrc/src/pscanner.pp

@@ -558,10 +558,11 @@ type
     po_KeepClassForward,     // disabled: delete class fowards when there is a class declaration
     po_ArrayRangeExpr,       // enable: create TPasArrayType.IndexRange, disable: create TPasArrayType.Ranges
     po_SelfToken,            // Self is a token. For backward compatibility.
-    po_CheckModeSwitches,    // stop on unknown modeswitch with an error
-    po_CheckCondFunction,    // stop on unknown function in conditional expression, default: return '0'
-    po_StopOnErrorDirective, // stop on user $Error, $message error|fatal
-    po_ExtClassConstWithoutExpr // allow const without expression in external class
+    po_CheckModeSwitches,    // error on unknown modeswitch with an error
+    po_CheckCondFunction,    // error on unknown function in conditional expression, default: return '0'
+    po_StopOnErrorDirective, // error on user $Error, $message error|fatal
+    po_ExtClassConstWithoutExpr, // allow const without expression in external class
+    po_StopOnUnitInterface   // parse only a unit name and stop at interface keyword
     );
   TPOptions = set of TPOption;
 

+ 1 - 1
packages/fcl-passrc/tests/tcbaseparser.pas

@@ -62,7 +62,7 @@ Type
     Procedure Add(Const Lines : array of String);
     Procedure StartParsing;
     Procedure ParseDeclarations;
-    Procedure ParseModule;
+    Procedure ParseModule; virtual;
     procedure ResetParser;
     Procedure CheckHint(AHint : TPasMemberHint);
     Function AssertExpression(Const Msg: String; AExpr : TPasExpr; aKind : TPasExprKind; AClass : TClass) : TPasExpr;

+ 43 - 11
packages/fcl-passrc/tests/tcresolver.pas

@@ -56,7 +56,6 @@ type
   private
     FFilename: string;
     FModule: TPasModule;
-    FOnContinueParsing: TOnContinueParsing;
     FOnFindUnit: TOnFindUnit;
     FParser: TPasParser;
     FStreamResolver: TStreamResolver;
@@ -72,8 +71,6 @@ type
       overload; override;
     function FindUnit(const AName, InFilename: String; NameExpr,
       InFileExpr: TPasExpr): TPasModule; override;
-    procedure ContinueParsing; override;
-    property OnContinueParsing: TOnContinueParsing read FOnContinueParsing write FOnContinueParsing;
     property OnFindUnit: TOnFindUnit read FOnFindUnit write FOnFindUnit;
     property Filename: string read FFilename write FFilename;
     property StreamResolver: TStreamResolver read FStreamResolver write FStreamResolver;
@@ -135,6 +132,7 @@ type
     Procedure SetUp; override;
     Procedure TearDown; override;
     procedure CreateEngine(var TheEngine: TPasTreeContainer); override;
+    procedure ParseModule; override;
     procedure ParseProgram; virtual;
     procedure ParseUnit; virtual;
     procedure CheckReferenceDirectives; virtual;
@@ -777,11 +775,6 @@ begin
   Result:=OnFindUnit(Self,AName,InFilename,NameExpr,InFileExpr);
 end;
 
-procedure TTestEnginePasResolver.ContinueParsing;
-begin
-  OnContinueParsing(Self);
-end;
-
 { TCustomTestResolver }
 
 procedure TCustomTestResolver.SetUp;
@@ -830,6 +823,45 @@ begin
   TheEngine:=ResolverEngine;
 end;
 
+procedure TCustomTestResolver.ParseModule;
+var
+  Section: TPasSection;
+  i: Integer;
+  CurResolver: TTestEnginePasResolver;
+  Found: Boolean;
+begin
+  inherited ParseModule;
+  repeat
+    Found:=false;
+    for i:=0 to ModuleCount-1 do
+      begin
+      CurResolver:=Modules[i];
+      if CurResolver.Parser=nil then continue;
+      if CurResolver.Parser.CanParseContinue(Section) then
+        begin
+        {$IFDEF VerbosePasResolver}
+        writeln('TCustomTestResolver.ParseModule continue parsing section=',GetObjName(Section),' of ',CurResolver.Filename);
+        {$ENDIF}
+        Found:=true;
+        CurResolver.Parser.ParseContinue;
+        break;
+        end;
+      end;
+  until not Found;
+
+  for i:=0 to ModuleCount-1 do
+    begin
+    CurResolver:=Modules[i];
+    if CurResolver.CurrentParser.CurModule<>nil then
+      begin
+      {$IFDEF VerbosePasResolver}
+      writeln('TCustomTestResolver.ParseModule module not finished "',CurResolver.RootElement.Name,'"');
+      {$ENDIF}
+      Fail('module not finished "'+CurResolver.RootElement.Name+'"');
+      end;
+    end;
+end;
+
 procedure TCustomTestResolver.ParseProgram;
 var
   aFilename: String;
@@ -1592,7 +1624,7 @@ begin
   ErrFilename:=CurEngine.Scanner.CurFilename;
   ErrRow:=CurEngine.Scanner.CurRow;
   ErrCol:=CurEngine.Scanner.CurColumn;
-  writeln('ERROR: TTestResolver.OnPasResolverFindUnit during parsing: '+E.ClassName+':'+E.Message
+  writeln('ERROR: TCustomTestResolver.HandleError during parsing: '+E.ClassName+':'+E.Message
     +' File='+ErrFilename
     +' LineNo='+IntToStr(ErrRow)
     +' Col='+IntToStr(ErrCol)
@@ -1636,7 +1668,6 @@ begin
   Result.Filename:=aFilename;
   Result.AddObjFPCBuiltInIdentifiers;
   Result.OnFindUnit:=@OnPasResolverFindUnit;
-  Result.OnContinueParsing:=@OnPasResolverContinueParsing;
   Result.OnLog:=@OnPasResolverLog;
   FModules.Add(Result);
 end;
@@ -1787,6 +1818,7 @@ function TCustomTestResolver.OnPasResolverFindUnit(SrcResolver: TPasResolver;
     CurEngine.Scanner.CurrentBoolSwitches:=[bsHints,bsNotes,bsWarnings];
     CurEngine.Parser:=TPasParser.Create(CurEngine.Scanner,
                                         CurEngine.StreamResolver,CurEngine);
+    CurEngine.Parser.Options:=CurEngine.Parser.Options+[po_StopOnUnitInterface];
     if CompareText(ExtractFileUnitName(CurEngine.Filename),'System')=0 then
       CurEngine.Parser.ImplicitUses.Clear;
     CurEngine.Scanner.OpenFile(CurEngine.Filename);
@@ -2056,7 +2088,7 @@ begin
   writeln('TCustomTestResolver.OnPasResolverContinueParsing "',CurEngine.Module.Name,'"...');
   {$ENDIF}
   try
-    CurEngine.Parser.ParseContinueImplementation;
+    CurEngine.Parser.ParseContinue;
   except
     on E: Exception do
       HandleError(CurEngine,E);