Browse Source

fcl-passrc: resolver: for item in class do

git-svn-id: trunk@37780 -
Mattias Gaertner 7 years ago
parent
commit
7b7948ca04

+ 4 - 0
packages/fcl-passrc/src/pasresolveeval.pas

@@ -148,6 +148,8 @@ const
   nPreviousDeclMissesOverload = 3075;
   nOverloadedProcMissesOverload = 3076;
   nMethodHidesMethodOfBaseType = 3077;
+  nContextExpectedXButFoundY = 3078;
+  nContextXInvalidY = 3079;
 
 // resourcestring patterns of messages
 resourcestring
@@ -228,6 +230,8 @@ resourcestring
   sPreviousDeclMissesOverload = 'Previous declaration of "%s" at %s was not marked with "overload" directive';
   sOverloadedProcMissesOverload = 'Overloaded procedure misses "overload" directive. Previous declaration is at %s';
   sMethodHidesMethodOfBaseType = 'Method "%s" hides method of base type "%s" at %s';
+  sContextExpectedXButFoundY = '%s: expected "%s", but found "%s"';
+  sContextXInvalidY = '%s: invalid %s';
 
 type
   { TResolveData - base class for data stored in TPasElement.CustomData }

+ 260 - 126
packages/fcl-passrc/src/pasresolver.pp

@@ -966,7 +966,6 @@ type
     function GetBaseTypeNames(bt: TResolverBaseType): string;
   protected
     const
-      cIncompatible = High(integer);
       cExact = 0;
       cCompatible = cExact+1;
       cIntToIntConversion = ord(High(TResolverBaseType));
@@ -974,6 +973,7 @@ type
       cTypeConversion = cExact+10000; // e.g. TObject to Pointer
       cLossyConversion = cExact+100000;
       cCompatibleWithDefaultParams = cLossyConversion+100000;
+      cIncompatible = High(integer);
     type
       TFindCallElData = record
         Params: TParamsExpr;
@@ -1129,6 +1129,8 @@ type
       var LHS: TPasResolverResult; const RHS: TPasResolverResult);
     procedure ConvertRangeToElement(var ResolvedEl: TPasResolverResult);
     function IsCharLiteral(const Value: string; ErrorPos: TPasElement): TResolverBaseType; virtual;
+    function CheckForInClass(Loop: TPasImplForLoop;
+      const VarResolved, InResolved: TPasResolverResult): boolean; virtual;
     function CheckBuiltInMinParamCount(Proc: TResElDataBuiltInProc; Expr: TPasExpr;
       MinCount: integer; RaiseOnError: boolean): boolean;
     function CheckBuiltInMaxParamCount(Proc: TResElDataBuiltInProc; Params: TParamsExpr;
@@ -1324,6 +1326,8 @@ type
     procedure RaiseInvalidScopeForElement(id: int64; El: TPasElement; const Msg: string = '');
     procedure RaiseIdentifierNotFound(id: int64; Identifier: string; El: TPasElement);
     procedure RaiseXExpectedButYFound(id: int64; const X,Y: string; El: TPasElement);
+    procedure RaiseContextXExpectedButYFound(id: int64; const C,X,Y: string; El: TPasElement);
+    procedure RaiseContextXInvalidY(id: int64; const X,Y: string; El: TPasElement);
     procedure RaiseConstantExprExp(id: int64; ErrorEl: TPasElement);
     procedure RaiseRangeCheck(id: int64; ErrorEl: TPasElement);
     procedure RaiseIncompatibleTypeDesc(id: int64; MsgNumber: integer;
@@ -5285,152 +5289,163 @@ begin
     begin
     // check range
     EnumeratorFound:=false;
-    VarRange:=EvalTypeRange(VarResolved.TypeEl,[]);
-    if VarRange=nil then
-      RaiseXExpectedButYFound(20171109191528,'range',
-                   GetResolverResultDescription(VarResolved),Loop.VariableName);
-    //writeln('TPasResolver.ResolveImplForLoop ForIn VarRange=',VarRange.AsDebugString);
-    InRange:=nil;
-    try
-      OrigStartResolved:=StartResolved;
-      if StartResolved.IdentEl is TPasType then
-        begin
-        // e.g. for e in TEnum do
-        TypeEl:=ResolveAliasType(StartResolved.TypeEl);
-        if TypeEl is TPasArrayType then
-          begin
-          if length(TPasArrayType(TypeEl).Ranges)=1 then
-            InRange:=Eval(TPasArrayType(TypeEl).Ranges[0],[refConst]);
-          end;
-        if InRange=nil then
-          InRange:=EvalTypeRange(TypeEl,[]);
-        {$IFDEF VerbosePasResolver}
-        if InRange<>nil then
-          writeln('TPasResolver.ResolveImplForLoop in type: InRange=',InRange.AsDebugString)
-        else
-          writeln('TPasResolver.ResolveImplForLoop in type: InRange=nil');
-        {$ENDIF}
-        end
-      else if rrfReadable in StartResolved.Flags then
-        begin
-        // value  (variable or expression)
-        bt:=StartResolved.BaseType;
-        if bt=btSet then
-          begin
-            writeln('AAA1 TPasResolver.ResolveImplForLoop ',GetObjName(StartResolved.ExprEl),' ',GetObjName(Loop.StartExpr));
-          if (StartResolved.IdentEl=nil) and (StartResolved.ExprEl<>nil) then
-            InRange:=Eval(StartResolved.ExprEl,[refAutoConst])
-          else
-            InRange:=EvalTypeRange(StartResolved.TypeEl,[]);
-          end
-        else if bt=btContext then
+
+    if (StartResolved.BaseType=btContext) then
+      begin
+      TypeEl:=ResolveAliasType(StartResolved.TypeEl);
+      C:=TypeEl.ClassType;
+      if C=TPasClassType then
+        EnumeratorFound:=CheckForInClass(Loop,VarResolved,StartResolved);
+      end;
+
+    if not EnumeratorFound then
+      begin
+      VarRange:=EvalTypeRange(VarResolved.TypeEl,[]);
+      if VarRange=nil then
+        RaiseXExpectedButYFound(20171109191528,'range',
+                     GetResolverResultDescription(VarResolved),Loop.VariableName);
+      //writeln('TPasResolver.ResolveImplForLoop ForIn VarRange=',VarRange.AsDebugString);
+      InRange:=nil;
+      try
+        OrigStartResolved:=StartResolved;
+        if StartResolved.IdentEl is TPasType then
           begin
+          // e.g. for e in TEnum do
           TypeEl:=ResolveAliasType(StartResolved.TypeEl);
-          C:=TypeEl.ClassType;
-          if C=TPasArrayType then
+          if TypeEl is TPasArrayType then
             begin
-            ComputeElement(TPasArrayType(TypeEl).ElType,StartResolved,[rcType]);
-            StartResolved.Flags:=OrigStartResolved.Flags*[rrfReadable,rrfWritable];
-            if CheckAssignResCompatibility(VarResolved,StartResolved,Loop.StartExpr,true)=cIncompatible then
-              RaiseIncompatibleTypeRes(20171112210138,nIncompatibleTypesGotExpected,
-                [],StartResolved,VarResolved,Loop.StartExpr);
-            EnumeratorFound:=true;
+            if length(TPasArrayType(TypeEl).Ranges)=1 then
+              InRange:=Eval(TPasArrayType(TypeEl).Ranges[0],[refConst]);
             end;
+          if InRange=nil then
+            InRange:=EvalTypeRange(TypeEl,[]);
+          {$IFDEF VerbosePasResolver}
+          if InRange<>nil then
+            writeln('TPasResolver.ResolveImplForLoop in type: InRange=',InRange.AsDebugString)
+          else
+            writeln('TPasResolver.ResolveImplForLoop in type: InRange=nil');
+          {$ENDIF}
           end
-        else
-          begin
-          bt:=GetActualBaseType(bt);
-          if bt=btAnsiString then
-            InRange:=TResEvalRangeInt.CreateValue(revskChar,nil,0,$ff)
-          else if bt=btUnicodeString then
-            InRange:=TResEvalRangeInt.CreateValue(revskChar,nil,0,$ffff);
-          end;
-        end;
-      if (not EnumeratorFound) and (InRange<>nil) then
-        begin
-        // for v in <constant> do
-        // -> check if same type
-        //writeln('TPasResolver.ResolveImplForLoop ForIn InRange=',InRange.AsDebugString,' ElType=',GetResolverResultDbg(StartResolved));
-        case InRange.Kind of
-        revkRangeInt,revkSetOfInt:
+        else if rrfReadable in StartResolved.Flags then
           begin
-          InRangeInt:=TResEvalRangeInt(InRange);
-          case VarRange.Kind of
-          revkRangeInt:
+          // value  (variable or expression)
+          bt:=StartResolved.BaseType;
+          if bt=btSet then
             begin
-            VarRangeInt:=TResEvalRangeInt(VarRange);
-            HasInValues:=(InRange.Kind<>revkSetOfInt) or (length(TResEvalSet(InRange).Ranges)>0);
-            case InRangeInt.ElKind of
-              revskEnum:
-                if (VarRangeInt.ElKind<>revskEnum)
-                    or not IsSameType(InRangeInt.ElType,VarRangeInt.ElType) then
-                  RaiseXExpectedButYFound(20171109200752,GetTypeDescription(InRangeInt.ElType),
-                    GetResolverResultDescription(VarResolved,true),loop.VariableName);
-              revskInt:
-                if VarRangeInt.ElKind<>revskInt then
-                  RaiseXExpectedButYFound(20171109200752,'integer',
-                    GetResolverResultDescription(VarResolved,true),loop.VariableName);
-              revskChar:
-                if VarRangeInt.ElKind<>revskChar then
-                  RaiseXExpectedButYFound(20171109200753,'char',
-                    GetResolverResultDescription(VarResolved,true),loop.VariableName);
-              revskBool:
-                if VarRangeInt.ElKind<>revskBool then
-                  RaiseXExpectedButYFound(20171109200754,'boolean',
-                    GetResolverResultDescription(VarResolved,true),loop.VariableName);
+            if (StartResolved.IdentEl=nil) and (StartResolved.ExprEl<>nil) then
+              InRange:=Eval(StartResolved.ExprEl,[refAutoConst])
             else
-              if HasInValues then
-                RaiseNotYetImplemented(20171109200954,Loop.StartExpr);
+              InRange:=EvalTypeRange(StartResolved.TypeEl,[]);
+            end
+          else if bt=btContext then
+            begin
+            TypeEl:=ResolveAliasType(StartResolved.TypeEl);
+            C:=TypeEl.ClassType;
+            if C=TPasArrayType then
+              begin
+              ComputeElement(TPasArrayType(TypeEl).ElType,StartResolved,[rcType]);
+              StartResolved.Flags:=OrigStartResolved.Flags*[rrfReadable,rrfWritable];
+              if CheckAssignResCompatibility(VarResolved,StartResolved,Loop.StartExpr,true)=cIncompatible then
+                RaiseIncompatibleTypeRes(20171112210138,nIncompatibleTypesGotExpected,
+                  [],StartResolved,VarResolved,Loop.StartExpr);
+              EnumeratorFound:=true;
+              end;
+            end
+          else
+            begin
+            bt:=GetActualBaseType(bt);
+            if bt=btAnsiString then
+              InRange:=TResEvalRangeInt.CreateValue(revskChar,nil,0,$ff)
+            else if bt=btUnicodeString then
+              InRange:=TResEvalRangeInt.CreateValue(revskChar,nil,0,$ffff);
             end;
-            if HasInValues then
+          end;
+        if (not EnumeratorFound) and (InRange<>nil) then
+          begin
+          // for v in <constant> do
+          // -> check if same type
+          //writeln('TPasResolver.ResolveImplForLoop ForIn InRange=',InRange.AsDebugString,' ElType=',GetResolverResultDbg(StartResolved));
+          case InRange.Kind of
+          revkRangeInt,revkSetOfInt:
+            begin
+            InRangeInt:=TResEvalRangeInt(InRange);
+            case VarRange.Kind of
+            revkRangeInt:
               begin
-              if (VarRangeInt.RangeStart>InRangeInt.RangeStart) then
-                begin
-                {$IFDEF VerbosePasResolver}
-                writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRangeInt.AsDebugString,' ',InRangeInt.AsDebugString);
-                {$ENDIF}
-                fExprEvaluator.EmitRangeCheckConst(20171109201428,
-                  InRangeInt.ElementAsString(InRangeInt.RangeStart),
-                  VarRangeInt.ElementAsString(VarRangeInt.RangeStart),
-                  VarRangeInt.ElementAsString(VarRangeInt.RangeEnd),Loop.VariableName,mtError);
-                end;
-              if (VarRangeInt.RangeEnd<InRangeInt.RangeEnd) then
+              VarRangeInt:=TResEvalRangeInt(VarRange);
+              HasInValues:=(InRange.Kind<>revkSetOfInt) or (length(TResEvalSet(InRange).Ranges)>0);
+              case InRangeInt.ElKind of
+                revskEnum:
+                  if (VarRangeInt.ElKind<>revskEnum)
+                      or not IsSameType(InRangeInt.ElType,VarRangeInt.ElType) then
+                    RaiseXExpectedButYFound(20171109200752,GetTypeDescription(InRangeInt.ElType),
+                      GetResolverResultDescription(VarResolved,true),loop.VariableName);
+                revskInt:
+                  if VarRangeInt.ElKind<>revskInt then
+                    RaiseXExpectedButYFound(20171109200752,'integer',
+                      GetResolverResultDescription(VarResolved,true),loop.VariableName);
+                revskChar:
+                  if VarRangeInt.ElKind<>revskChar then
+                    RaiseXExpectedButYFound(20171109200753,'char',
+                      GetResolverResultDescription(VarResolved,true),loop.VariableName);
+                revskBool:
+                  if VarRangeInt.ElKind<>revskBool then
+                    RaiseXExpectedButYFound(20171109200754,'boolean',
+                      GetResolverResultDescription(VarResolved,true),loop.VariableName);
+              else
+                if HasInValues then
+                  RaiseNotYetImplemented(20171109200954,Loop.StartExpr);
+              end;
+              if HasInValues then
                 begin
-                {$IFDEF VerbosePasResolver}
-                writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRangeInt.AsDebugString,' ',InRangeInt.AsDebugString);
-                {$ENDIF}
-                fExprEvaluator.EmitRangeCheckConst(20171109201429,
-                  InRangeInt.ElementAsString(InRangeInt.RangeEnd),
-                  VarRangeInt.ElementAsString(VarRangeInt.RangeStart),
-                  VarRangeInt.ElementAsString(VarRangeInt.RangeEnd),Loop.VariableName,mtError);
+                if (VarRangeInt.RangeStart>InRangeInt.RangeStart) then
+                  begin
+                  {$IFDEF VerbosePasResolver}
+                  writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRangeInt.AsDebugString,' ',InRangeInt.AsDebugString);
+                  {$ENDIF}
+                  fExprEvaluator.EmitRangeCheckConst(20171109201428,
+                    InRangeInt.ElementAsString(InRangeInt.RangeStart),
+                    VarRangeInt.ElementAsString(VarRangeInt.RangeStart),
+                    VarRangeInt.ElementAsString(VarRangeInt.RangeEnd),Loop.VariableName,mtError);
+                  end;
+                if (VarRangeInt.RangeEnd<InRangeInt.RangeEnd) then
+                  begin
+                  {$IFDEF VerbosePasResolver}
+                  writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRangeInt.AsDebugString,' ',InRangeInt.AsDebugString);
+                  {$ENDIF}
+                  fExprEvaluator.EmitRangeCheckConst(20171109201429,
+                    InRangeInt.ElementAsString(InRangeInt.RangeEnd),
+                    VarRangeInt.ElementAsString(VarRangeInt.RangeStart),
+                    VarRangeInt.ElementAsString(VarRangeInt.RangeEnd),Loop.VariableName,mtError);
+                  end;
                 end;
+              EnumeratorFound:=true;
               end;
-            EnumeratorFound:=true;
+            else
+              {$IFDEF VerbosePasResolver}
+              writeln('TPasResolver.ResolveImplForLoop ForIn VarRange=',VarRange.AsDebugString);
+              {$ENDIF}
+            end;
             end;
           else
             {$IFDEF VerbosePasResolver}
-            writeln('TPasResolver.ResolveImplForLoop ForIn VarRange=',VarRange.AsDebugString);
+            writeln('TPasResolver.ResolveImplForLoop ForIn InRange=',InRange.AsDebugString);
             {$ENDIF}
           end;
           end;
-        else
+        if not EnumeratorFound then
+          begin
           {$IFDEF VerbosePasResolver}
-          writeln('TPasResolver.ResolveImplForLoop ForIn InRange=',InRange.AsDebugString);
+          writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRange.AsDebugString,' StartResolved=',GetResolverResultDbg(StartResolved));
           {$ENDIF}
-        end;
-        end;
-      if not EnumeratorFound then
-        begin
-        {$IFDEF VerbosePasResolver}
-        writeln('TPasResolver.ResolveImplForLoop VarRange=',VarRange.AsDebugString,' StartResolved=',GetResolverResultDbg(StartResolved));
-        {$ENDIF}
-        RaiseMsg(20171108223818,nCannotFindEnumeratorForType,sCannotFindEnumeratorForType,
-          [GetBaseDescription(OrigStartResolved)],Loop.StartExpr);
-        end;
-    finally
-      ReleaseEvalValue(VarRange);
-      ReleaseEvalValue(InRange);
-    end;
+          RaiseMsg(20171108223818,nCannotFindEnumeratorForType,sCannotFindEnumeratorForType,
+            [GetBaseDescription(OrigStartResolved)],Loop.StartExpr);
+          end;
+      finally
+        ReleaseEvalValue(VarRange);
+        ReleaseEvalValue(InRange);
+      end;
+      end;
 
     end;
   else
@@ -8175,6 +8190,113 @@ begin
     end;
 end;
 
+function TPasResolver.CheckForInClass(Loop: TPasImplForLoop; const VarResolved,
+  InResolved: TPasResolverResult): boolean;
+var
+  TypeEl: TPasType;
+  aClass: TPasClassType;
+  ClassScope: TPasDotClassScope;
+  Getter, MoveNext, Current: TPasIdentifier;
+  GetterFunc, MoveNextFunc: TPasFunction;
+  ptm: TProcTypeModifier;
+  ResultResolved, MoveNextResolved, CurrentResolved: TPasResolverResult;
+  CurrentProp: TPasProperty;
+begin
+  Result:=false;
+  TypeEl:=ResolveAliasType(InResolved.TypeEl);
+  if TypeEl is TPasClassType then
+    begin
+    if not (rrfReadable in InResolved.Flags) then
+      RaiseMsg(20171221195421,nCannotFindEnumeratorForType,sCannotFindEnumeratorForType,
+        [GetBaseDescription(InResolved)],Loop.StartExpr);
+
+    // check function GetEnumerator: class
+    aClass:=TPasClassType(TypeEl);
+    // find aClass.GetEnumerator
+    ClassScope:=PushClassDotScope(aClass);
+    Getter:=ClassScope.FindIdentifier('GetEnumerator');
+    PopScope;
+    if Getter=nil then
+      RaiseIdentifierNotFound(20171221191511,'GetEnumerator',Loop.StartExpr);
+    // check is function
+    if Getter.Element.ClassType<>TPasFunction then
+      RaiseContextXExpectedButYFound(20171221191638,'GetEnumerator','function',Getter.Element.ElementTypeName,Loop.StartExpr);
+    GetterFunc:=TPasFunction(Getter.Element);
+    // check visibility
+    if not (GetterFunc.Visibility in [visPublic,visPublished]) then
+      RaiseContextXExpectedButYFound(20171221191824,'function GetEnumerator','public',VisibilityNames[GetterFunc.Visibility],Loop.StartExpr);
+    // check arguments
+    if GetterFunc.FuncType.Args.Count>0 then
+      RaiseContextXExpectedButYFound(20171221191944,'function GetEnumerator','no arguments',IntToStr(GetterFunc.ProcType.Args.Count),Loop.StartExpr);
+    // check proc type modifiers
+    for ptm in GetterFunc.ProcType.Modifiers do
+      if not (ptm in [ptmOfObject]) then
+        RaiseContextXInvalidY(20171221193455,'function GetEnumerator','modifier '+ProcTypeModifiers[ptm],Loop.StartExpr);
+    // check result type
+    ComputeElement(GetterFunc.FuncType.ResultEl,ResultResolved,[rcType]);
+    if (ResultResolved.BaseType<>btContext) then
+      RaiseContextXExpectedButYFound(20171221193749,'function GetEnumerator','result class',GetTypeDescription(ResultResolved),Loop.StartExpr);
+    TypeEl:=ResolveAliasType(ResultResolved.TypeEl);
+    if not (TypeEl is TPasClassType) then
+      RaiseContextXExpectedButYFound(20171221193749,'function GetEnumerator','result class',GetTypeDescription(ResultResolved.TypeEl),Loop.StartExpr);
+    if not (rrfReadable in ResultResolved.Flags) then
+      RaiseContextXExpectedButYFound(20171221195506,'function GetEnumerator','result class instance',GetTypeDescription(ResultResolved.TypeEl),Loop.StartExpr);
+
+    // check function MoveNext: boolean
+    aClass:=TPasClassType(TypeEl);
+    ClassScope:=PushClassDotScope(aClass);
+    MoveNext:=ClassScope.FindIdentifier('MoveNext');
+    if MoveNext=nil then
+      RaiseIdentifierNotFound(20171221195632,'MoveNext',Loop.StartExpr);
+    // check is function
+    if MoveNext.Element.ClassType<>TPasFunction then
+      RaiseContextXExpectedButYFound(20171221195651,'MoveNext','function',MoveNext.Element.ElementTypeName,Loop.StartExpr);
+    MoveNextFunc:=TPasFunction(MoveNext.Element);
+    // check visibility
+    if not (MoveNextFunc.Visibility in [visPublic,visPublished]) then
+      RaiseContextXExpectedButYFound(20171221195712,'function MoveNext','public',VisibilityNames[MoveNextFunc.Visibility],Loop.StartExpr);
+    // check arguments
+    if MoveNextFunc.FuncType.Args.Count>0 then
+      RaiseContextXExpectedButYFound(20171221195723,'function MoveNext','no arguments',IntToStr(MoveNextFunc.ProcType.Args.Count),Loop.StartExpr);
+    // check proc type modifiers
+    for ptm in MoveNextFunc.ProcType.Modifiers do
+      if not (ptm in [ptmOfObject]) then
+        RaiseContextXInvalidY(20171221195732,'function MoveNext','modifier '+ProcTypeModifiers[ptm],Loop.StartExpr);
+    // check result type
+    ComputeElement(MoveNextFunc.FuncType.ResultEl,MoveNextResolved,[rcType]);
+    if not (MoveNextResolved.BaseType in btAllBooleans) then
+      RaiseContextXExpectedButYFound(20171221200337,'function MoveNext','result boolean',GetTypeDescription(MoveNextResolved),Loop.StartExpr);
+
+    // check property Current
+    Current:=ClassScope.FindIdentifier('Current');
+    if Current=nil then
+      RaiseIdentifierNotFound(20171221200433,'Current',Loop.StartExpr);
+    // check is property
+    if Current.Element.ClassType<>TPasProperty then
+      RaiseContextXExpectedButYFound(20171221200508,'Current','property',Current.Element.ElementTypeName,Loop.StartExpr);
+    CurrentProp:=TPasProperty(Current.Element);
+    // check visibility
+    if not (CurrentProp.Visibility in [visPublic,visPublished]) then
+      RaiseContextXExpectedButYFound(20171221200546,'property Current','public',VisibilityNames[CurrentProp.Visibility],Loop.StartExpr);
+    // check arguments
+    if CurrentProp.Args.Count>0 then
+      RaiseContextXExpectedButYFound(20171221200638,'property Current','no arguments',IntToStr(CurrentProp.Args.Count),Loop.StartExpr);
+    // check readable
+    if GetPasPropertyGetter(CurrentProp)=nil then
+      RaiseContextXInvalidY(20171221200823,'property Current','read accessor',Loop.StartExpr);
+    // check result type fits for-loop variable
+    ComputeElement(CurrentProp,CurrentResolved,[rcType]);
+    if CheckAssignResCompatibility(VarResolved,CurrentResolved,Loop.VariableName,false)=cIncompatible then
+      RaiseIncompatibleTypeRes(20171221200018,nIncompatibleTypesGotExpected,[],VarResolved,CurrentResolved,Loop.VariableName);
+
+    PopScope;
+    exit(true);
+    end;
+
+  RaiseMsg(20171221192929,nCannotFindEnumeratorForType,sCannotFindEnumeratorForType,
+    [GetBaseDescription(InResolved)],Loop.StartExpr);
+end;
+
 function TPasResolver.CheckBuiltInMinParamCount(Proc: TResElDataBuiltInProc;
   Expr: TPasExpr; MinCount: integer; RaiseOnError: boolean): boolean;
 begin
@@ -11276,6 +11398,18 @@ begin
   RaiseMsg(id,nXExpectedButYFound,sXExpectedButYFound,[X,Y],El);
 end;
 
+procedure TPasResolver.RaiseContextXExpectedButYFound(id: int64; const C, X,
+  Y: string; El: TPasElement);
+begin
+  RaiseMsg(id,nContextExpectedXButFoundY,sContextExpectedXButFoundY,[C,X,Y],El);
+end;
+
+procedure TPasResolver.RaiseContextXInvalidY(id: int64; const X, Y: string;
+  El: TPasElement);
+begin
+  RaiseMsg(id,nContextXInvalidY,sContextXInvalidY,[X,Y],El);
+end;
+
 procedure TPasResolver.RaiseConstantExprExp(id: int64; ErrorEl: TPasElement);
 begin
   RaiseMsg(id,nConstantExpressionExpected,sConstantExpressionExpected,[],ErrorEl);

+ 61 - 0
packages/fcl-passrc/tests/tcresolver.pas

@@ -489,6 +489,8 @@ type
     Procedure TestClass_VarExternal;
     Procedure TestClass_WarnOverrideLowerVisibility;
     Procedure TestClass_Const;
+    Procedure TestClass_Enumerator;
+    Procedure TestClass_EnumeratorFunc;
     // Todo: Fail to use class.method in constant or type, e.g. const p = @o.doit;
 
     // published
@@ -8099,6 +8101,65 @@ begin
   CheckResolverUnexpectedHints;
 end;
 
+procedure TTestResolver.TestClass_Enumerator;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  TObject = class end;',
+  '  TItem = TObject;',
+  '  TEnumerator = class',
+  '    FCurrent: TItem;',
+  '    property Current: TItem read FCurrent;',
+  '    function MoveNext: boolean;',
+  '  end;',
+  '  TBird = class',
+  '    function GetEnumerator: TEnumerator;',
+  '  end;',
+  'function TEnumerator.MoveNext: boolean;',
+  'begin',
+  'end;',
+  'function TBird.GetEnumerator: TEnumerator;',
+  'begin',
+  'end;',
+  'var',
+  '  b: TBird;',
+  '  i: TItem;',
+  '  {#i2}i2: TItem;',
+  'begin',
+  '  for i in b do {@i2}i2:=i;']);
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestClass_EnumeratorFunc;
+begin
+  StartProgram(false);
+  Add([
+  'type',
+  '  TObject = class end;',
+  '  TItem = longint;',
+  '  TEnumerator = class',
+  '    FCurrent: TItem;',
+  '    property Current: TItem read FCurrent;',
+  '    function MoveNext: boolean;',
+  '    function GetEnumerator: TEnumerator;',
+  '  end;',
+  'function TEnumerator.MoveNext: boolean;',
+  'begin',
+  'end;',
+  'function TEnumerator.GetEnumerator: TEnumerator;',
+  'begin',
+  'end;',
+  'function GetIt: TEnumerator;',
+  'begin',
+  'end;',
+  'var',
+  '  i, i2: TItem;',
+  'begin',
+  '  for i in GetIt do i2:=i;']);
+  ParseProgram;
+end;
+
 procedure TTestResolver.TestClass_PublishedClassVarFail;
 begin
   StartProgram(false);