Sfoglia il codice sorgente

fcl-passrc: pasresolver: resolve dotted unit names and default program namespace

git-svn-id: trunk@36069 -
Mattias Gaertner 8 anni fa
parent
commit
bf9dffbaf8

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

@@ -684,7 +684,7 @@ type
 
   TPasSectionScope = Class(TPasIdentifierScope)
   public
-    UsesList: TFPList; // list of TPasSectionScope
+    UsesScopes: TFPList; // list of TPasSectionScope
     constructor Create; override;
     destructor Destroy; override;
     function FindIdentifier(const Identifier: String): TPasIdentifier; override;
@@ -1025,6 +1025,7 @@ type
     FBaseTypeLength: TResolverBaseType;
     FBaseTypes: array[TResolverBaseType] of TPasUnresolvedSymbolRef;
     FBaseTypeString: TResolverBaseType;
+    FDefaultNameSpace: String;
     FDefaultScope: TPasDefaultScope;
     FLastCreatedData: array[TResolveDataListKind] of TResolveData;
     FLastElement: TPasElement;
@@ -1303,6 +1304,7 @@ type
       NoProcsWithArgs: boolean): TPasElement;
     function FindElementWithoutParams(const AName: String; out Data: TPRFindData;
       ErrorPosEl: TPasElement; NoProcsWithArgs: boolean): TPasElement;
+    procedure FindLongestUnitName(var El: TPasElement; Expr: TPasExpr);
     procedure IterateElements(const aName: string;
       const OnIterateElement: TIterateScopeElement; Data: Pointer;
       var Abort: boolean); virtual;
@@ -1444,6 +1446,9 @@ type
     function ResolveAliasType(aType: TPasType): TPasType;
     function ExprIsAddrTarget(El: TPasExpr): boolean;
     function IsNameExpr(El: TPasExpr): boolean; inline; // TSelfExpr or TPrimitiveExpr with Kind=pekIdent
+    function GetNameExprValue(El: TPasExpr): string; // TSelfExpr or TPrimitiveExpr with Kind=pekIdent
+    function GetNextDottedExpr(El: TPasExpr): TPasExpr;
+    function GetPathStart(El: TPasExpr): TPasExpr;
     function GetLastExprIdentifier(El: TPasExpr): TPasExpr;
     function ParentNeedsExprResult(El: TPasExpr): boolean;
     function GetReference_NewInstanceClass(Ref: TResolvedReference): TPasClassType;
@@ -1469,20 +1474,30 @@ type
     function GetCombinedChar(const Char1, Char2: TPasResolverResult; ErrorEl: TPasElement): TResolverBaseType; virtual;
     function GetCombinedString(const Str1, Str2: TPasResolverResult; ErrorEl: TPasElement): TResolverBaseType; virtual;
   public
+    // options
+    property Options: TPasResolverOptions read FOptions write FOptions;
+    property AnonymousElTypePostfix: String read FAnonymousElTypePostfix
+      write FAnonymousElTypePostfix; // default empty, if set, anonymous element types are named ArrayName+Postfix and added to declarations
     property BaseTypes[bt: TResolverBaseType]: TPasUnresolvedSymbolRef read GetBaseTypes;
     property BaseTypeChar: TResolverBaseType read FBaseTypeChar write FBaseTypeChar;
     property BaseTypeExtended: TResolverBaseType read FBaseTypeExtended write FBaseTypeExtended;
     property BaseTypeString: TResolverBaseType read FBaseTypeString write FBaseTypeString;
     property BaseTypeLength: TResolverBaseType read FBaseTypeLength write FBaseTypeLength;
-    property LastElement: TPasElement read FLastElement;
+    // parsed values
+    property DefaultNameSpace: String read FDefaultNameSpace;
+    property RootElement: TPasElement read FRootElement;
+    // scopes
     property StoreSrcColumns: boolean read FStoreSrcColumns write FStoreSrcColumns; {
        If true Line and Column is mangled together in TPasElement.SourceLineNumber.
        Use method UnmangleSourceLineNumber to extract. }
     property Scopes[Index: integer]: TPasScope read GetScopes;
     property ScopeCount: integer read FScopeCount;
     property TopScope: TPasScope read FTopScope;
-    property RootElement: TPasElement read FRootElement;
     property DefaultScope: TPasDefaultScope read FDefaultScope write FDefaultScope;
+    property ScopeClass_Class: TPasClassScopeClass read FScopeClass_Class write FScopeClass_Class;
+    property ScopeClass_WithExpr: TPasWithExprScopeClass read FScopeClass_WithExpr write FScopeClass_WithExpr;
+    // last element
+    property LastElement: TPasElement read FLastElement;
     property LastMsg: string read FLastMsg write FLastMsg;
     property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs;
     property LastMsgElement: TPasElement read FLastMsgElement write FLastMsgElement;
@@ -1491,11 +1506,6 @@ type
     property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern;
     property LastMsgType: TMessageType read FLastMsgType write FLastMsgType;
     property LastSourcePos: TPasSourcePos read FLastSourcePos write FLastSourcePos;
-    property Options: TPasResolverOptions read FOptions write FOptions;
-    property ScopeClass_Class: TPasClassScopeClass read FScopeClass_Class write FScopeClass_Class;
-    property ScopeClass_WithExpr: TPasWithExprScopeClass read FScopeClass_WithExpr write FScopeClass_WithExpr;
-    property AnonymousElTypePostfix: String read FAnonymousElTypePostfix
-      write FAnonymousElTypePostfix; // default empty, if set, anonymous element types are named ArrayName+Postfix and added to declarations
   end;
 
 function GetObjName(o: TObject): string;
@@ -1515,6 +1525,7 @@ procedure SetResolverValueExpr(out ResolvedType: TPasResolverResult;
   Flags: TPasResolverResultFlags); overload;
 
 function ProcNeedsImplProc(Proc: TPasProcedure): boolean;
+function ChompDottedIdentifier(const Identifier: string): string;
 
 function dbgs(const Flags: TPasResolverComputeFlags): string; overload;
 function dbgs(const a: TResolvedRefAccess): string;
@@ -1786,6 +1797,20 @@ begin
   Result:=false;
 end;
 
+function ChompDottedIdentifier(const Identifier: string): string;
+var
+  p: Integer;
+begin
+  Result:=Identifier;
+  p:=length(Identifier);
+  while (p>0) do
+    begin
+    if Identifier[p]='.' then
+      exit(LeftStr(Identifier,p-1));
+    dec(p);
+    end;
+end;
+
 function dbgs(const Flags: TPasResolverComputeFlags): string;
 var
   s: string;
@@ -2100,7 +2125,7 @@ procedure TPasModuleDotScope.OnInternalIterate(El: TPasElement; ElScope,
 var
   FilterData: PPasIterateFilterData absolute Data;
 begin
-  if El.ClassType=TPasModule then
+  if (El.ClassType=TPasModule) or (El.ClassType=TPasUsesUnit) then
     exit; // skip used units
   // call the original iterator
   FilterData^.OnIterate(El,ElScope,StartScope,FilterData^.Data,Abort);
@@ -2153,11 +2178,11 @@ begin
   FilterData.Data:=Data;
   if ImplementationScope<>nil then
     begin
-    ImplementationScope.IterateElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
+    ImplementationScope.IterateLocalElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
     if Abort then exit;
     end;
   if InterfaceScope<>nil then
-    InterfaceScope.IterateElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
+    InterfaceScope.IterateLocalElements(aName,StartScope,@OnInternalIterate,@FilterData,Abort);
 end;
 
 procedure TPasModuleDotScope.WriteIdentifiers(Prefix: string);
@@ -2173,7 +2198,7 @@ end;
 constructor TPasSectionScope.Create;
 begin
   inherited Create;
-  UsesList:=TFPList.Create;
+  UsesScopes:=TFPList.Create;
 end;
 
 destructor TPasSectionScope.Destroy;
@@ -2181,7 +2206,7 @@ begin
   {$IFDEF VerbosePasResolverMem}
   writeln('TPasSectionScope.Destroy START ',ClassName);
   {$ENDIF}
-  FreeAndNil(UsesList);
+  FreeAndNil(UsesScopes);
   inherited Destroy;
   {$IFDEF VerbosePasResolverMem}
   writeln('TPasSectionScope.Destroy END ',ClassName);
@@ -2197,9 +2222,9 @@ begin
   Result:=inherited FindIdentifier(Identifier);
   if Result<>nil then
     exit;
-  for i:=0 to UsesList.Count-1 do
+  for i:=0 to UsesScopes.Count-1 do
     begin
-    UsesScope:=TPasIdentifierScope(UsesList[i]);
+    UsesScope:=TPasIdentifierScope(UsesScopes[i]);
     {$IFDEF VerbosePasResolver}
     writeln('TPasSectionScope.FindIdentifier "',Identifier,'" in used unit ',GetObjName(UsesScope.Element));
     {$ENDIF}
@@ -2217,9 +2242,9 @@ var
 begin
   inherited IterateElements(aName, StartScope, OnIterateElement, Data, Abort);
   if Abort then exit;
-  for i:=0 to UsesList.Count-1 do
+  for i:=0 to UsesScopes.Count-1 do
     begin
-    UsesScope:=TPasIdentifierScope(UsesList[i]);
+    UsesScope:=TPasIdentifierScope(UsesScopes[i]);
     {$IFDEF VerbosePasResolver}
     writeln('TPasSectionScope.IterateElements "',aName,'" in used unit ',GetObjName(UsesScope.Element));
     {$ENDIF}
@@ -2234,9 +2259,9 @@ var
   UsesScope: TPasIdentifierScope;
 begin
   inherited WriteIdentifiers(Prefix);
-  for i:=0 to UsesList.Count-1 do
+  for i:=0 to UsesScopes.Count-1 do
     begin
-    UsesScope:=TPasIdentifierScope(UsesList[i]);
+    UsesScope:=TPasIdentifierScope(UsesScopes[i]);
     writeln(Prefix+'Uses: '+GetObjName(UsesScope.Element));
     end;
 end;
@@ -2557,6 +2582,95 @@ begin
       or ((El.ClassType=TPrimitiveExpr) and (TPrimitiveExpr(El).Kind=pekIdent));
 end;
 
+function TPasResolver.GetNameExprValue(El: TPasExpr): string;
+begin
+  if El=nil then
+    Result:=''
+  else if El.ClassType=TPrimitiveExpr then
+    begin
+    if TPrimitiveExpr(El).Kind=pekIdent then
+      Result:=TPrimitiveExpr(El).Value
+    else
+      Result:='';
+    end
+  else if El.ClassType=TSelfExpr then
+    Result:='self'
+  else
+    Result:='';
+end;
+
+function TPasResolver.GetNextDottedExpr(El: TPasExpr): TPasExpr;
+// returns TSelfExpr or TPrimitiveExpr (Kind=pekIdent)
+var
+  Bin: TBinaryExpr;
+  C: TClass;
+begin
+  Result:=nil;
+  if El=nil then exit;
+  repeat
+    if not (El.Parent is TBinaryExpr) then exit;
+    Bin:=TBinaryExpr(El.Parent);
+    if Bin.OpCode<>eopSubIdent then exit;
+    if El=Bin.right then
+      El:=Bin
+    else
+      begin
+      El:=Bin.right;
+      // find left most
+      repeat
+        C:=El.ClassType;
+        if C=TSelfExpr then
+          exit(El)
+        else if C=TPrimitiveExpr then
+          begin
+          if TPrimitiveExpr(El).Kind<>pekIdent then
+            RaiseNotYetImplemented(20170502163825,El);
+          exit(El);
+          end
+        else if C=TBinaryExpr then
+          begin
+          if TBinaryExpr(El).OpCode<>eopSubIdent then
+            RaiseNotYetImplemented(20170502163718,El);
+          El:=TBinaryExpr(El).left;
+          end
+        else if C=TParamsExpr then
+          begin
+          if not (TParamsExpr(El).Kind in [pekFuncParams,pekArrayParams]) then
+            RaiseNotYetImplemented(20170502163908,El);
+          El:=TParamsExpr(El).Value;
+          end;
+      until El=nil;
+      RaiseNotYetImplemented(20170502163953,Bin);
+      end;
+  until false;
+end;
+
+function TPasResolver.GetPathStart(El: TPasExpr): TPasExpr;
+var
+  C: TClass;
+begin
+  Result:=nil;
+  while El<>nil do
+    begin
+    C:=El.ClassType;
+    if C=TPrimitiveExpr then
+      exit(El)
+    else if C=TSelfExpr then
+      exit(El)
+    else if C=TBinaryExpr then
+      begin
+      if TBinaryExpr(El).OpCode=eopSubIdent then
+        El:=TBinaryExpr(El).left
+      else
+        exit;
+      end
+    else if C=TParamsExpr then
+      El:=TParamsExpr(El).Value
+    else
+      exit;
+    end;
+end;
+
 procedure TPasResolver.ClearResolveDataList(Kind: TResolveDataListKind);
 var
   El: TPasElement;
@@ -3027,12 +3141,15 @@ end;
 
 procedure TPasResolver.FinishUsesClause;
 var
-  Section: TPasSection;
-  i: Integer;
+  Section, CurSection: TPasSection;
+  i, j: Integer;
   PublicEl, UseModule: TPasElement;
   Scope: TPasSectionScope;
   UsesScope: TPasIdentifierScope;
   UseUnit: TPasUsesUnit;
+  FirstName: String;
+  p: SizeInt;
+  OldIdentifier: TPasIdentifier;
 begin
   CheckTopScope(TPasSectionScope);
   Scope:=TPasSectionScope(TopScope);
@@ -3047,18 +3164,15 @@ begin
     writeln('TPasResolver.FinishUsesClause ',GetObjName(UseUnit));
     {$ENDIF}
     UseModule:=UseUnit.Module;
-    if (UseModule.ClassType=TProgramSection) then
-      RaiseInternalError(20160922163346,'used unit is a program: '+GetObjName(UseModule));
-
-    // add unitname as identifier
-    AddIdentifier(Scope,UseUnit.Name,UseModule,pikSimple);
 
     // check used unit
     PublicEl:=nil;
     if (UseModule.ClassType=TLibrarySection) then
       PublicEl:=UseModule
     else if (UseModule.ClassType=TPasModule) then
-      PublicEl:=TPasModule(UseModule).InterfaceSection;
+      PublicEl:=TPasModule(UseModule).InterfaceSection
+    else
+      RaiseXExpectedButYFound(20170503004803,'unit',UseModule.ElementTypeName,UseUnit);
     if PublicEl=nil then
       RaiseInternalError(20160922163352,'uses element has no interface section: '+GetObjName(UseModule));
     if PublicEl.CustomData=nil then
@@ -3068,14 +3182,56 @@ begin
       RaiseInternalError(20160922163403,'uses element has invalid resolver data: '
         +UseUnit.Name+'->'+GetObjName(PublicEl)+'->'+PublicEl.CustomData.ClassName);
 
+    // check if module was already used by a different name
+    j:=i;
+    CurSection:=Section;
+    repeat
+      dec(j);
+      if j<0 then
+        begin
+        if CurSection.ClassType<>TImplementationSection then
+          break;
+        CurSection:=CurSection.GetModule.InterfaceSection;
+        if CurSection=nil then break;
+        j:=length(CurSection.UsesClause)-1;
+        if j<0 then break;
+        end;
+      if CurSection.UsesClause[j].Module=UseModule then
+        RaiseMsg(20170503004022,nDuplicateIdentifier,sDuplicateIdentifier,
+          [UseModule.Name,GetElementSourcePosStr(CurSection.UsesClause[j])],UseUnit);
+    until false;
+
+    // add full uses name
+    AddIdentifier(Scope,UseUnit.Name,UseUnit,pikSimple);
+
+    // add scope
     UsesScope:=TPasIdentifierScope(PublicEl.CustomData);
     {$IFDEF VerbosePasResolver}
     writeln('TPasResolver.FinishUsesClause Add UsesScope=',GetObjName(UsesScope));
     {$ENDIF}
-    Scope.UsesList.Add(UsesScope);
+    Scope.UsesScopes.Add(UsesScope);
 
     EmitElementHints(Section,UseUnit);
     end;
+
+  // Note: a sub identifier (e.g. a class member) hides all unitnames starting
+  //       with this identifier
+  // -> add first name of dotted unitname as identifier
+  for i:=0 to Section.UsesList.Count-1 do
+    begin
+    UseUnit:=Section.UsesClause[i];
+    FirstName:=UseUnit.Name;
+    p:=Pos('.',FirstName);
+    if p<1 then continue;
+    FirstName:=LeftStr(FirstName,p-1);
+    OldIdentifier:=Scope.FindLocalIdentifier(FirstName);
+    if OldIdentifier=nil then
+      AddIdentifier(Scope,FirstName,UseUnit.Module,pikSimple)
+    else
+      // a reference in the implementation needs to find a match in the
+      // implementation clause -> replace identfier in the scope
+      OldIdentifier.Element:=UseUnit;
+    end;
 end;
 
 procedure TPasResolver.FinishTypeSection(El: TPasDeclarations);
@@ -4812,13 +4968,23 @@ var
   Proc: TPasProcedure;
   Ref: TResolvedReference;
   BuiltInProc: TResElDataBuiltInProc;
+  p: SizeInt;
+  DottedName: String;
 begin
   {$IFDEF VerbosePasResolver}
   writeln('TPasResolver.ResolveNameExpr El=',GetObjName(El),' Name="',aName,'" ',Access);
   {$ENDIF}
   DeclEl:=FindElementWithoutParams(aName,FindData,El,false);
+  if DeclEl.ClassType=TPasUsesUnit then
+    begin
+    // the first name of a unit matches -> find unit with longest match
+    FindLongestUnitName(DeclEl,El);
+    FindData.Found:=DeclEl;
+    end;
+
   Ref:=CreateReference(DeclEl,El,Access,@FindData);
   CheckFoundElement(FindData,Ref);
+
   if DeclEl is TPasProcedure then
     begin
     // identifier is a proc and args brackets are missing
@@ -4845,6 +5011,29 @@ begin
       BuiltInProc:=TResElDataBuiltInProc(DeclEl.CustomData);
       BuiltInProc.GetCallCompatibility(BuiltInProc,El,true);
       end;
+    end
+  else if (DeclEl.ClassType=TPasUsesUnit) or (DeclEl is TPasModule) then
+    begin
+    // unit reference
+    // dotted unit names needs a ref for each expression identifier
+    // Note: El is the first TPrimitiveExpr of the dotted unit name reference
+    DottedName:=DeclEl.Name;
+    repeat
+      p:=Pos('.',DottedName);
+      if p<1 then break;
+      Delete(DottedName,1,p);
+      El:=GetNextDottedExpr(El);
+      if El=nil then
+        RaiseInternalError(20170503002012);
+      CreateReference(DeclEl,El,Access);
+    until false;
+    // and add references to the binary expressions
+    while (El.Parent is TBinaryExpr) and (TBinaryExpr(El.Parent).right=El) do
+      begin
+      El:=TBinaryExpr(El.Parent);
+      if TBinaryExpr(El).OpCode<>eopSubIdent then break;
+      CreateReference(DeclEl,El,Access);
+      end;
     end;
 end;
 
@@ -4998,6 +5187,9 @@ var
   RecordEl: TPasRecordType;
   RecordScope: TPasDotRecordScope;
 begin
+  if El.CustomData is TResolvedReference then
+    exit; // for example, when a.b has a dotted unit name
+
   Left:=El.left;
   //writeln('TPasResolver.ResolveSubIdent Left=',GetObjName(Left));
   ComputeElement(Left,LeftResolved,[rcSetReferenceFlags]);
@@ -5638,11 +5830,18 @@ begin
 end;
 
 procedure TPasResolver.AddModule(El: TPasModule);
+var
+  C: TClass;
 begin
   if TopScope<>DefaultScope then
     RaiseInvalidScopeForElement(20160922163504,El);
   PushScope(El,TPasModuleScope);
   TPasModuleScope(TopScope).VisibilityContext:=El;
+  C:=El.ClassType;
+  if (C=TPasProgram) or (C=TPasLibrary) or (C=TPasPackage) then
+    FDefaultNameSpace:=ChompDottedIdentifier(El.Name)
+  else
+    FDefaultNameSpace:='';
 end;
 
 procedure TPasResolver.AddSection(El: TPasSection);
@@ -8210,6 +8409,7 @@ begin
       or (AClass=TPasProgram)
       or (AClass=TPasLibrary) then
     AddModule(TPasModule(El))
+  else if AClass=TPasUsesUnit then
   else if AClass.InheritsFrom(TPasExpr) then
     // resolved when finished
   else if AClass.InheritsFrom(TPasImplBlock) then
@@ -8336,6 +8536,88 @@ begin
       sWrongNumberOfParametersForCallTo,[GetProcTypeDescription(TPasProcedure(Result).ProcType)],ErrorPosEl);
 end;
 
+procedure TPasResolver.FindLongestUnitName(var El: TPasElement; Expr: TPasExpr);
+// Input: El is TPasUsesUnit
+// Output: El is either a TPasUsesUnit or the root module
+var
+  CurUsesUnit: TPasUsesUnit;
+  BestEl: TPasElement;
+  aName, CurName: String;
+  Clause: TPasUsesClause;
+  i, CurLen: Integer;
+  Section: TPasSection;
+begin
+  {$IFDEF VerbosePasResolver}
+  //writeln('TPasResolver.FindLongestUnitName El=',GetObjName(El),' Expr=',GetObjName(Expr));
+  {$ENDIF}
+  if not (El is TPasUsesUnit) then
+    RaiseInternalError(20170503000945);
+  aName:=GetNameExprValue(Expr);
+  if aName='' then
+    RaiseNotYetImplemented(20170503110217,Expr);
+  repeat
+    Expr:=GetNextDottedExpr(Expr);
+    if Expr=nil then break;
+    CurName:=GetNameExprValue(Expr);
+    if CurName='' then
+      RaiseNotYetImplemented(20170502164242,Expr);
+    aName:=aName+'.'+CurName;
+  until false;
+
+  {$IFDEF VerbosePasResolver}
+  //writeln('TPasResolver.FindLongestUnitName Dotted="',aName,'"');
+  {$ENDIF}
+  // search in uses clause
+  BestEl:=nil;
+  Section:=TPasUsesUnit(El).Parent as TPasSection;
+  repeat
+    Clause:=Section.UsesClause;
+    for i:=0 to length(Clause)-1 do
+      begin
+      CurUsesUnit:=Clause[i];
+      CurName:=CurUsesUnit.Name;
+      CurLen:=length(CurName);
+      if (CompareText(CurName,LeftStr(aName,CurLen))=0)
+          and ((CurLen=length(aName)) or (aName[CurLen+1]='.')) then
+        begin
+        // a match
+        if (BestEl=nil) or (CurLen>length(BestEl.Name)) then
+          BestEl:=CurUsesUnit; // a better match
+        end;
+      end;
+    if Section is TImplementationSection then
+      begin
+      // search in interface uses clause too
+      Section:=(Section.Parent as TPasModule).InterfaceSection;
+      end
+    else
+      break;
+  until Section=nil;
+  {$IFDEF VerbosePasResolver}
+  //writeln('TPasResolver.FindLongestUnitName LongestUnit="',GetObjName(BestEl),'"');
+  {$ENDIF}
+
+  // check module name
+  CurName:=El.GetModule.Name;
+  CurLen:=length(CurName);
+  if (CompareText(CurName,LeftStr(aName,CurLen))=0)
+      and ((CurLen=length(aName)) or (aName[CurLen+1]='.')) then
+    begin
+    // a match
+    if (BestEl=nil) or (CurLen>length(BestEl.Name)) then
+      BestEl:=El.GetModule; // a better match
+    end;
+  if BestEl=nil then
+    begin
+    // no dotted module name fits the expression
+    RaiseIdentifierNotFound(20170503140643,GetNameExprValue(Expr),Expr);
+    end;
+  El:=BestEl;
+  {$IFDEF VerbosePasResolver}
+  //writeln('TPasResolver.FindLongestUnitName END Best="',GetObjName(El),'"');
+  {$ENDIF}
+end;
+
 procedure TPasResolver.IterateElements(const aName: string;
   const OnIterateElement: TIterateScopeElement; Data: Pointer;
   var Abort: boolean);

+ 19 - 9
packages/fcl-passrc/src/pastree.pp

@@ -310,7 +310,7 @@ type
     Functions, Variables, Properties, ExportSymbols: TFPList;
   end;
 
-  { TPasUsesUnit }
+  { TPasUsesUnit - Parent is TPasSection }
 
   TPasUsesUnit = class(TPasElement)
   public
@@ -332,7 +332,8 @@ type
     constructor Create(const AName: string; AParent: TPasElement); override;
     destructor Destroy; override;
     function AddUnitToUsesList(const AUnitName: string; aName: TPasExpr = nil;
-      InFilename: TPrimitiveExpr = nil; aModule: TPasElement = nil): TPasUsesUnit;
+      InFilename: TPrimitiveExpr = nil; aModule: TPasElement = nil;
+      UsesUnit: TPasUsesUnit = nil): TPasUsesUnit;
     function ElementTypeName: string; override;
     procedure ForEachCall(const aMethodCall: TOnForEachPasElement;
       const Arg: Pointer); override;
@@ -4029,8 +4030,8 @@ begin
 end;
 
 function TPasSection.AddUnitToUsesList(const AUnitName: string;
-  aName: TPasExpr; InFilename: TPrimitiveExpr; aModule: TPasElement
-  ): TPasUsesUnit;
+  aName: TPasExpr; InFilename: TPrimitiveExpr; aModule: TPasElement;
+  UsesUnit: TPasUsesUnit): TPasUsesUnit;
 var
   l: Integer;
 begin
@@ -4040,11 +4041,20 @@ begin
     aModule:=TPasUnresolvedUnitRef.Create(AUnitName, Self);
   l:=length(UsesClause);
   SetLength(UsesClause,l+1);
-  Result:=TPasUsesUnit.Create(AUnitName,Self);
-  UsesClause[l]:=Result;
-  Result.Expr:=aName;
-  Result.InFilename:=InFilename;
-  Result.Module:=aModule;
+  if UsesUnit=nil then
+    begin
+    UsesUnit:=TPasUsesUnit.Create(AUnitName,Self);
+    if aName<>nil then
+      begin
+      Result.SourceFilename:=aName.SourceFilename;
+      Result.SourceLinenumber:=aName.SourceLinenumber;
+      end;
+    end;
+  UsesClause[l]:=UsesUnit;
+  UsesUnit.Expr:=aName;
+  UsesUnit.InFilename:=InFilename;
+  UsesUnit.Module:=aModule;
+  Result:=UsesUnit;
 
   UsesList.Add(aModule);
   aModule.AddRef;

+ 43 - 12
packages/fcl-passrc/src/pparser.pp

@@ -312,8 +312,8 @@ type
     function DoParseExpression(AParent: TPaselement;InitExpr: TPasExpr=nil; AllowEqual : Boolean = True): TPasExpr;
     function DoParseConstValueExpression(AParent: TPasElement): TPasExpr;
     function CheckPackMode: TPackMode;
-    function AddUseUnit(ASection: TPasSection; AUnitName : string;
-      NameExpr: TPasExpr; InFileExpr: TPrimitiveExpr): TPasElement;
+    function AddUseUnit(ASection: TPasSection; const NamePos: TPasSourcePos;
+      AUnitName : string; NameExpr: TPasExpr; InFileExpr: TPrimitiveExpr): TPasElement;
     procedure CheckImplicitUsedUnits(ASection: TPasSection);
     // Overload handling
     procedure AddProcOrFunction(Decs: TPasDeclarations; AProc: TPasProcedure);
@@ -2319,21 +2319,21 @@ begin
   AUnitName := ExpectIdentifier;
   NextToken;
   while CurToken = tkDot do
-  begin
+    begin
     ExpectIdentifier;
     AUnitName := AUnitName + '.' + CurTokenString;
     NextToken;
-  end;
+    end;
   UngetToken;
   Module := TPasModule(CreateElement(TPasModule, AUnitName,
     Engine.Package));
   FCurModule:=Module;
   try
     if Assigned(Engine.Package) then
-    begin
+      begin
       Module.PackageName := Engine.Package.Name;
       Engine.Package.Modules.Add(Module);
-    end;
+      end;
     CheckHint(Module,True);
 //    ExpectToken(tkSemicolon);
     ExpectToken(tkInterface);
@@ -2358,7 +2358,17 @@ begin
   if SkipHeader then
     N:=ChangeFileExt(Scanner.CurFilename,'')
   else
+    begin
     N:=ExpectIdentifier;
+    NextToken;
+    while CurToken = tkDot do
+      begin
+      ExpectIdentifier;
+      N := N + '.' + CurTokenString;
+      NextToken;
+      end;
+    UngetToken;
+    end;
   Module := nil;
   PP:=TPasProgram(CreateElement(TPasProgram, N, Engine.Package));
   Module :=PP;
@@ -2396,14 +2406,25 @@ begin
   end;
 end;
 
+// Starts after the "library" token
 procedure TPasParser.ParseLibrary(var Module: TPasModule);
 Var
   PP : TPasLibrary;
   Section : TLibrarySection;
+  N: String;
 
 begin
+  N:=ExpectIdentifier;
+  NextToken;
+  while CurToken = tkDot do
+    begin
+    ExpectIdentifier;
+    N := N + '.' + CurTokenString;
+    NextToken;
+    end;
+  UngetToken;
   Module := nil;
-  PP:=TPasLibrary(CreateElement(TPasLibrary, ExpectIdentifier, Engine.Package));
+  PP:=TPasLibrary(CreateElement(TPasLibrary, N, Engine.Package));
   Module :=PP;
   FCurModule:=Module;
   try
@@ -2853,8 +2874,9 @@ begin
   SetBlock(declNone);
 end;
 
-function TPasParser.AddUseUnit(ASection: TPasSection; AUnitName: string;
-  NameExpr: TPasExpr; InFileExpr: TPrimitiveExpr): TPasElement;
+function TPasParser.AddUseUnit(ASection: TPasSection;
+  const NamePos: TPasSourcePos; AUnitName: string; NameExpr: TPasExpr;
+  InFileExpr: TPrimitiveExpr): TPasElement;
 
   procedure CheckDuplicateInUsesList(AUnitName : string; UsesClause: TPasUsesClause);
   var
@@ -2868,8 +2890,10 @@ function TPasParser.AddUseUnit(ASection: TPasSection; AUnitName: string;
 
 var
   UnitRef: TPasElement;
+  UsesUnit: TPasUsesUnit;
 begin
   Result:=nil;
+  UsesUnit:=nil;
   try
     {$IFDEF VerbosePasParser}
     writeln('TPasParser.AddUseUnit AUnitName=',AUnitName,' CurModule.Name=',CurModule.Name);
@@ -2891,7 +2915,8 @@ begin
       UnitRef := TPasUnresolvedUnitRef(CreateElement(TPasUnresolvedUnitRef,
         AUnitName, ASection));
 
-    Result:=ASection.AddUnitToUsesList(AUnitName,NameExpr,InFileExpr,UnitRef);
+    UsesUnit:=TPasUsesUnit(CreateElement(TPasUsesUnit,AUnitName,ASection,NamePos));
+    Result:=ASection.AddUnitToUsesList(AUnitName,NameExpr,InFileExpr,UnitRef,UsesUnit);
     if InFileExpr<>nil then
       begin
       if UnitRef is TPasModule then
@@ -2905,6 +2930,8 @@ begin
   finally
     if Result=nil then
       begin
+      if UsesUnit<>nil then
+        UsesUnit.Release;
       if NameExpr<>nil then
         NameExpr.Release;
       if InFileExpr<>nil then
@@ -2916,12 +2943,14 @@ end;
 procedure TPasParser.CheckImplicitUsedUnits(ASection: TPasSection);
 var
   i: Integer;
+  NamePos: TPasSourcePos;
 begin
   If not (ASection.ClassType=TImplementationSection) Then // interface,program,library,package
     begin
     // load implicit units, like 'System'
+    NamePos:=Scanner.CurSourcePos;
     for i:=0 to ImplicitUses.Count-1 do
-      AddUseUnit(ASection,ImplicitUses[i],nil,nil);
+      AddUseUnit(ASection,NamePos,ImplicitUses[i],nil,nil);
     end;
 end;
 
@@ -2932,6 +2961,7 @@ var
   NameExpr: TPasExpr;
   InFileExpr: TPrimitiveExpr;
   FreeExpr: Boolean;
+  NamePos: TPasSourcePos;
 begin
   CheckImplicitUsedUnits(ASection);
 
@@ -2942,6 +2972,7 @@ begin
     Repeat
       FreeExpr:=true;
       AUnitName := ExpectIdentifier;
+      NamePos:=Scanner.CurSourcePos;
       NameExpr:=CreatePrimitiveExpr(ASection,pekString,AUnitName);
       NextToken;
       while CurToken = tkDot do
@@ -2959,7 +2990,7 @@ begin
         NextToken;
         end;
       FreeExpr:=false;
-      AddUseUnit(ASection,AUnitName,NameExpr,InFileExpr);
+      AddUseUnit(ASection,NamePos,AUnitName,NameExpr,InFileExpr);
       InFileExpr:=nil;
       NameExpr:=nil;
 

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

@@ -8,7 +8,7 @@ uses
   Classes, SysUtils, fpcunit, pastree, pscanner, pparser, testregistry;
 
 const
-  MainFilename = 'afile.pp';
+  DefaultMainFilename = 'afile.pp';
 Type
   { TTestEngine }
 
@@ -32,6 +32,7 @@ Type
     FDeclarations: TPasDeclarations;
     FDefinition: TPasElement;
     FEngine : TPasTreeContainer;
+    FMainFilename: string;
     FModule: TPasModule;
     FParseResult: TPasElement;
     FScanner : TPascalScanner;
@@ -98,6 +99,7 @@ Type
     // If set, Will be freed in teardown
     Property ParseResult : TPasElement Read FParseResult Write FParseResult;
     Property UseImplementation : Boolean Read FUseImplementation Write FUseImplementation;
+    Property MainFilename: string read FMainFilename write FMainFilename;
   end;
 
 function ExtractFileUnitName(aFilename: string): string;
@@ -517,6 +519,7 @@ end;
 
 procedure TTestParser.SetUp;
 begin
+  FMainFilename:=DefaultMainFilename;
   Inherited;
   SetupParser;
 end;

+ 277 - 65
packages/fcl-passrc/tests/tcresolver.pas

@@ -45,7 +45,7 @@ const
     '='  // mkDirectReference
     );
 type
-  TOnFindUnit = function(const aUnitName: String): TPasModule of object;
+  TOnFindUnit = function(Sender: TPasResolver; const aUnitName: String): TPasModule of object;
 
   { TTestEnginePasResolver }
 
@@ -109,7 +109,8 @@ type
     function GetModules(Index: integer): TTestEnginePasResolver;
     function GetMsgCount: integer;
     function GetMsgs(Index: integer): TTestResolverMessage;
-    function OnPasResolverFindUnit(const aUnitName: String): TPasModule;
+    function OnPasResolverFindUnit(SrcResolver: TPasResolver;
+      const aUnitName: String): TPasModule;
     procedure OnFindReference(El: TPasElement; FindData: pointer);
     procedure OnCheckElementParent(El: TPasElement; arg: pointer);
     procedure FreeSrcMarkers;
@@ -271,10 +272,18 @@ type
 
     // units
     Procedure TestUnitOverloads;
-    Procedure TestUnitIntfInitalization;
+    Procedure TestUnitIntfInitialization;
     Procedure TestUnitUseIntf;
     Procedure TestUnitUseImplFail;
+    Procedure TestUnit_DuplicateUsesFail;
     Procedure TestUnit_NestedFail;
+    Procedure TestUnitUseDotted;
+    Procedure TestUnit_ProgramDefaultNamespace;
+    Procedure TestUnit_DottedIdentifier;
+    Procedure TestUnit_DuplicateDottedUsesFail;
+    Procedure TestUnit_DuplicateUsesDiffNameFail;
+    Procedure TestUnit_Unit1DotUnit2Fail;
+    Procedure TestUnit_InFilename; // ToDo
 
     // procs
     Procedure TestProcParam;
@@ -614,7 +623,7 @@ function TTestEnginePasResolver.FindModule(const AName: String): TPasModule;
 begin
   Result:=nil;
   if Assigned(OnFindUnit) then
-    Result:=OnFindUnit(AName);
+    Result:=OnFindUnit(Self,AName);
 end;
 
 { TCustomTestResolver }
@@ -1193,6 +1202,8 @@ begin
   except
     on E: EParserError do
       begin
+      if (Parser.LastMsg<>Msg) and (Parser.LastMsgPattern<>Msg) then
+        Fail('Expected msg {'+Msg+'}, but got {'+Parser.LastMsg+'} OR pattern {'+Parser.LastMsgPattern+'}');
       AssertEquals('Expected {'+Msg+'}, but got msg {'+E.Message+'} number',
         MsgNumber,Parser.LastMsgNumber);
       ok:=true;
@@ -1554,61 +1565,83 @@ begin
   Add('unit '+ExtractFileUnitName(MainFilename)+';');
 end;
 
-function TCustomTestResolver.OnPasResolverFindUnit(const aUnitName: String
-  ): TPasModule;
-var
-  i, ErrRow, ErrCol: Integer;
-  CurEngine: TTestEnginePasResolver;
-  CurUnitName, ErrFilename: String;
-begin
-  //writeln('TTestResolver.OnPasResolverFindUnit START Unit="',aUnitName,'"');
-  Result:=nil;
-  for i:=0 to ModuleCount-1 do
-    begin
-    CurEngine:=Modules[i];
-    CurUnitName:=ExtractFileUnitName(CurEngine.Filename);
-    //writeln('TTestResolver.OnPasResolverFindUnit Checking ',i,'/',ModuleCount,' ',CurEngine.Filename,' ',CurUnitName);
-    if CompareText(aUnitName,CurUnitName)=0 then
+function TCustomTestResolver.OnPasResolverFindUnit(SrcResolver: TPasResolver;
+  const aUnitName: String): TPasModule;
+
+  function FindUnit(const aUnitName: String): TPasModule;
+  var
+    i, ErrRow, ErrCol: Integer;
+    CurEngine: TTestEnginePasResolver;
+    CurUnitName, ErrFilename: String;
+  begin
+    {$IFDEF VerboseUnitSearch}
+    writeln('TTestResolver.OnPasResolverFindUnit START Unit="',aUnitName,'"');
+    {$ENDIF}
+    Result:=nil;
+    for i:=0 to ModuleCount-1 do
       begin
-      Result:=CurEngine.Module;
-      if Result<>nil then exit;
-      //writeln('TTestResolver.OnPasResolverFindUnit PARSING unit "',CurEngine.Filename,'"');
-      //Resolver.FindSourceFile(aUnitName);
-
-      CurEngine.Resolver:=Resolver;
-      //CurEngine.Resolver:=TStreamResolver.Create;
-      //CurEngine.Resolver.OwnsStreams:=True;
-      //writeln('TTestResolver.OnPasResolverFindUnit SOURCE=',CurEngine.Source);
-      CurEngine.Resolver.AddStream(CurEngine.FileName,TStringStream.Create(CurEngine.Source));
-      CurEngine.Scanner:=TPascalScanner.Create(CurEngine.Resolver);
-      CurEngine.Parser:=TPasParser.Create(CurEngine.Scanner,CurEngine.Resolver,CurEngine);
-      if CompareText(CurUnitName,'System')=0 then
-        CurEngine.Parser.ImplicitUses.Clear;
-      CurEngine.Scanner.OpenFile(CurEngine.Filename);
-      try
-        CurEngine.Parser.NextToken;
-        CurEngine.Parser.ParseUnit(CurEngine.FModule);
-      except
-        on E: Exception do
-          begin
-          ErrFilename:=CurEngine.Scanner.CurFilename;
-          ErrRow:=CurEngine.Scanner.CurRow;
-          ErrCol:=CurEngine.Scanner.CurColumn;
-          writeln('ERROR: TTestResolver.OnPasResolverFindUnit during parsing: '+E.ClassName+':'+E.Message
-            +' File='+ErrFilename
-            +' LineNo='+IntToStr(ErrRow)
-            +' Col='+IntToStr(ErrCol)
-            +' Line="'+CurEngine.Scanner.CurLine+'"'
-            );
-          WriteSources(ErrFilename,ErrRow,ErrCol);
-          Fail(E.Message);
-          end;
-      end;
-      //writeln('TTestResolver.OnPasResolverFindUnit END ',CurUnitName);
-      Result:=CurEngine.Module;
-      exit;
+      CurEngine:=Modules[i];
+      CurUnitName:=ExtractFileUnitName(CurEngine.Filename);
+      {$IFDEF VerboseUnitSearch}
+      writeln('TTestResolver.OnPasResolverFindUnit Checking ',i,'/',ModuleCount,' ',CurEngine.Filename,' ',CurUnitName);
+      {$ENDIF}
+      if CompareText(aUnitName,CurUnitName)=0 then
+        begin
+        Result:=CurEngine.Module;
+        {$IFDEF VerboseUnitSearch}
+        writeln('TTestResolver.OnPasResolverFindUnit Found unit "',CurEngine.Filename,'" Module=',GetObjName(Result));
+        {$ENDIF}
+        if Result<>nil then exit;
+        {$IFDEF VerboseUnitSearch}
+        writeln('TTestResolver.OnPasResolverFindUnit PARSING unit "',CurEngine.Filename,'"');
+        {$ENDIF}
+
+        CurEngine.Resolver:=Resolver;
+        //writeln('TTestResolver.OnPasResolverFindUnit SOURCE=',CurEngine.Source);
+        CurEngine.Resolver.AddStream(CurEngine.FileName,TStringStream.Create(CurEngine.Source));
+        CurEngine.Scanner:=TPascalScanner.Create(CurEngine.Resolver);
+        CurEngine.Parser:=TPasParser.Create(CurEngine.Scanner,CurEngine.Resolver,CurEngine);
+        if CompareText(CurUnitName,'System')=0 then
+          CurEngine.Parser.ImplicitUses.Clear;
+        CurEngine.Scanner.OpenFile(CurEngine.Filename);
+        try
+          CurEngine.Parser.NextToken;
+          CurEngine.Parser.ParseUnit(CurEngine.FModule);
+        except
+          on E: Exception do
+            begin
+            ErrFilename:=CurEngine.Scanner.CurFilename;
+            ErrRow:=CurEngine.Scanner.CurRow;
+            ErrCol:=CurEngine.Scanner.CurColumn;
+            writeln('ERROR: TTestResolver.OnPasResolverFindUnit during parsing: '+E.ClassName+':'+E.Message
+              +' File='+ErrFilename
+              +' LineNo='+IntToStr(ErrRow)
+              +' Col='+IntToStr(ErrCol)
+              +' Line="'+CurEngine.Scanner.CurLine+'"'
+              );
+            WriteSources(ErrFilename,ErrRow,ErrCol);
+            Fail(E.Message);
+            end;
+        end;
+        //writeln('TTestResolver.OnPasResolverFindUnit END ',CurUnitName);
+        Result:=CurEngine.Module;
+        exit;
+        end;
       end;
+  end;
+begin
+  if SrcResolver=nil then ;
+  if (Pos('.',aUnitName)<1) and (ResolverEngine.DefaultNameSpace<>'') then
+    begin
+    // first search in default program namespace
+    {$IFDEF VerbosePasResolver}
+    writeln('TCustomTestResolver.OnPasResolverFindUnit searching "',aUnitName,'" in default program/library namespace "',ResolverEngine.DefaultNameSpace,'"');
+    {$ENDIF}
+    Result:=FindUnit(ResolverEngine.DefaultNameSpace+'.'+aUnitName);
+    if Result<>nil then exit;
     end;
+  Result:=FindUnit(aUnitName);
+  if Result<>nil then exit;
   writeln('TTestResolver.OnPasResolverFindUnit missing unit "',aUnitName,'"');
   Fail('can''t find unit "'+aUnitName+'"');
 end;
@@ -2070,7 +2103,7 @@ begin
   Add('  i: longint');
   Add('begin');
   Add('  doit;');
-  CheckParserException('Expected ";" at token "begin" in file afile.pp at line 5 column 5',
+  CheckParserException('Expected ";"',
     nParserExpectTokenError);
 end;
 
@@ -3545,7 +3578,7 @@ begin
   ParseUnit;
 end;
 
-procedure TTestResolver.TestUnitIntfInitalization;
+procedure TTestResolver.TestUnitIntfInitialization;
 var
   El, DeclEl, OtherUnit: TPasElement;
   LocalVar: TPasVariable;
@@ -3622,7 +3655,7 @@ begin
   AssertEquals('other unit assign unit ref resolved',TResolvedReference,Prim1.CustomData.ClassType);
   DeclEl:=TResolvedReference(Prim1.CustomData).Declaration;
   OtherUnit:=DeclEl;
-  AssertEquals('other unit assign unit ref',TPasModule,DeclEl.ClassType);
+  AssertEquals('other unit assign unit ref',TPasUsesUnit,DeclEl.ClassType);
   AssertEquals('other unit assign unit ref system','system',lowercase(DeclEl.Name));
 
   AssertEquals('other unit assign dot',eopSubIdent,BinExp.OpCode);
@@ -3636,7 +3669,7 @@ begin
   DeclEl:=TResolvedReference(Prim2.CustomData).Declaration;
   AssertEquals('other unit assign var',TPasVariable,DeclEl.ClassType);
   AssertEquals('other unit assign var exitcode','exitcode',lowercase(DeclEl.Name));
-  AssertSame('other unit assign var exitcode',OtherUnit,DeclEl.GetModule);
+  AssertSame('other unit assign var exitcode',(OtherUnit as TPasUsesUnit).Module,DeclEl.GetModule);
 end;
 
 procedure TTestResolver.TestUnitUseIntf;
@@ -3672,6 +3705,22 @@ begin
   CheckResolverException('identifier not found "DoIt"',nIdentifierNotFound);
 end;
 
+procedure TTestResolver.TestUnit_DuplicateUsesFail;
+begin
+  AddModuleWithIntfImplSrc('unit2.pp',
+    LinesToStr([
+    'var i: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add('uses unit2, unit2;');
+  Add('begin');
+  Add('  i:=3;');
+  CheckParserException('Duplicate identifier "unit2"',
+    nParserDuplicateIdentifier);
+end;
+
 procedure TTestResolver.TestUnit_NestedFail;
 begin
   AddModuleWithIntfImplSrc('unit2.pp',
@@ -3688,13 +3737,176 @@ begin
     '']));
 
   StartProgram(true);
-  Add('uses unit1;');
-  Add('begin');
-  Add('  if j1=0 then ;');
-  Add('  if i2=0 then ;');
+  Add([
+  'uses unit1;',
+  'begin',
+  '  if j1=0 then ;',
+  '  if i2=0 then ;',
+  '']);
   CheckResolverException('identifier not found "i2"',nIdentifierNotFound);
 end;
 
+procedure TTestResolver.TestUnitUseDotted;
+begin
+  AddModuleWithIntfImplSrc('ns1.unit2.pp',
+    LinesToStr([
+    'var i2: longint;']),
+    LinesToStr([
+    '']));
+
+  AddModuleWithIntfImplSrc('ns2.ns2A.unit1.pp',
+    LinesToStr([
+    'uses ns1.unit2;',
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses ns2.ns2A.unit1;',
+  'begin',
+  '  if j1=0 then ;',
+  '']);
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestUnit_ProgramDefaultNamespace;
+begin
+  MainFilename:='ns1.main1.pas';
+
+  AddModuleWithIntfImplSrc('ns1.unit2.pp',
+    LinesToStr([
+    'var i2: longint;']),
+    LinesToStr([
+    '']));
+
+  AddModuleWithIntfImplSrc('ns1.unit1.pp',
+    LinesToStr([
+    'uses unit2;',
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses unit1;',
+  'begin',
+  '  if j1=0 then ;',
+  '']);
+  writeln('TTestResolver.TestUnit_ProgramDefaultNamespace ');
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestUnit_DottedIdentifier;
+begin
+  MainFilename:='unitdots.main1.pas';
+  AddModuleWithIntfImplSrc('unitdots.unit1.pp',
+    LinesToStr([
+    'var i1: longint;']),
+    LinesToStr([
+    '']));
+
+  AddModuleWithIntfImplSrc('unitdots.pp',
+    LinesToStr([
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses unitdots.unit1, unitdots;',
+  'var k1: longint;',
+  'begin',
+  '  if unitdots.main1.k1=0 then ;',
+  '  if unitdots.j1=0 then ;',
+  '  if unitdots.unit1.i1=0 then ;',
+  '']);
+  writeln('TTestResolver.TestUnit_DottedIdentifier ');
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestUnit_DuplicateDottedUsesFail;
+begin
+  AddModuleWithIntfImplSrc('ns.unit2.pp',
+    LinesToStr([
+    'var i: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add('uses ns.unit2, ns.unit2;');
+  Add('begin');
+  Add('  i:=3;');
+  CheckParserException('Duplicate identifier "ns.unit2"',
+    nParserDuplicateIdentifier);
+end;
+
+procedure TTestResolver.TestUnit_DuplicateUsesDiffNameFail;
+begin
+  MainFilename:='unitdots.main1.pas';
+  AddModuleWithIntfImplSrc('unitdots.unit1.pp',
+    LinesToStr([
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses unitdots.unit1, unit1;',
+  'var k1: longint;',
+  'begin',
+  '  if unitdots.main1.k1=0 then ;',
+  '  if unit1.j1=0 then ;',
+  '  if unitdots.unit1.j1=0 then ;',
+  '']);
+  CheckResolverException('Duplicate identifier "unitdots.unit1" at unitdots.main1.pas(2,13)',
+    nDuplicateIdentifier);
+end;
+
+procedure TTestResolver.TestUnit_Unit1DotUnit2Fail;
+begin
+  AddModuleWithIntfImplSrc('unit1.pp',
+    LinesToStr([
+    'var i1: longint;']),
+    LinesToStr([
+    '']));
+
+  AddModuleWithIntfImplSrc('unit2.pp',
+    LinesToStr([
+    'uses unit1;',
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses unit2;',
+  'begin',
+  '  if unit2.unit1.i1=0 then ;',
+  '']);
+  CheckResolverException('identifier not found "unit1"',
+    nIdentifierNotFound);
+end;
+
+procedure TTestResolver.TestUnit_InFilename;
+begin
+  exit;
+  AddModuleWithIntfImplSrc('unit2.pp',
+    LinesToStr([
+    'uses unit1;',
+    'var j1: longint;']),
+    LinesToStr([
+    '']));
+
+  StartProgram(true);
+  Add([
+  'uses foo in ''unit2.pas'';',
+  'begin',
+  '  if foo.i1=0 then ;',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolver.TestProcParam;
 begin
   StartProgram(false);
@@ -3747,7 +3959,7 @@ begin
   StartProgram(false);
   Add('procedure A: longint; begin end;');
   Add('begin');
-  CheckParserException('Expected ";" at token ":" in file afile.pp at line 2 column 12',
+  CheckParserException('Expected ";"',
     nParserExpectTokenError);
 end;