Browse Source

fcl-passrc: fixed mode delphi static array of char = string literal, started $warn directive

git-svn-id: trunk@39313 -
Mattias Gaertner 7 years ago
parent
commit
6e807dfbc4

+ 1 - 1
packages/fcl-passrc/src/pasresolver.pp

@@ -17823,7 +17823,7 @@ function TPasResolver.CheckAssignCompatibilityArrayType(const LHS,
           exit;
           exit;
           end;
           end;
         end
         end
-      else if IsArrayOperatorAdd(Expr) then
+      else if IsArrayOperatorAdd(Expr) and not (Values.BaseType in btAllStrings) then
         begin
         begin
         // a:=left+right
         // a:=left+right
         if length(ArrType.Ranges)>0 then
         if length(ArrType.Ranges)>0 then

+ 203 - 12
packages/fcl-passrc/src/pscanner.pp

@@ -53,6 +53,7 @@ const
   nLogMacroDefined = 1026; // FPC=3101
   nLogMacroDefined = 1026; // FPC=3101
   nLogMacroUnDefined = 1027; // FPC=3102
   nLogMacroUnDefined = 1027; // FPC=3102
   nWarnIllegalCompilerDirectiveX = 1028;
   nWarnIllegalCompilerDirectiveX = 1028;
+  nIllegalStateForWarnDirective = 1027;
 
 
 // resourcestring patterns of messages
 // resourcestring patterns of messages
 resourcestring
 resourcestring
@@ -77,13 +78,14 @@ resourcestring
   SErrInvalidMode = 'Invalid mode: "%s"';
   SErrInvalidMode = 'Invalid mode: "%s"';
   SErrInvalidModeSwitch = 'Invalid mode switch: "%s"';
   SErrInvalidModeSwitch = 'Invalid mode switch: "%s"';
   SErrXExpectedButYFound = '"%s" expected, but "%s" found';
   SErrXExpectedButYFound = '"%s" expected, but "%s" found';
-  sErrRangeCheck = 'range check failed';
-  sErrDivByZero = 'division by zero';
-  sErrOperandAndOperatorMismatch = 'operand and operator mismatch';
+  SErrRangeCheck = 'range check failed';
+  SErrDivByZero = 'division by zero';
+  SErrOperandAndOperatorMismatch = 'operand and operator mismatch';
   SUserDefined = 'User defined: "%s"';
   SUserDefined = 'User defined: "%s"';
-  sLogMacroDefined = 'Macro defined: %s';
-  sLogMacroUnDefined = 'Macro undefined: %s';
-  sWarnIllegalCompilerDirectiveX = 'Illegal compiler directive "%s"';
+  SLogMacroDefined = 'Macro defined: %s';
+  SLogMacroUnDefined = 'Macro undefined: %s';
+  SWarnIllegalCompilerDirectiveX = 'Illegal compiler directive "%s"';
+  SIllegalStateForWarnDirective = 'Illegal state "%s" for $WARN directive';
 
 
 type
 type
   TMessageType = (
   TMessageType = (
@@ -354,6 +356,14 @@ const
   vsAllValueSwitches = [low(TValueSwitch)..high(TValueSwitch)];
   vsAllValueSwitches = [low(TValueSwitch)..high(TValueSwitch)];
   DefaultVSInterfaces = 'com';
   DefaultVSInterfaces = 'com';
 
 
+type
+  TWarnMsgState = (
+    wmsDefault,
+    wmsOn,
+    wmsOff,
+    wmsError
+  );
+
 type
 type
   TTokenOption = (toForceCaret,toOperatorToken);
   TTokenOption = (toForceCaret,toOperatorToken);
   TTokenOptions = Set of TTokenOption;
   TTokenOptions = Set of TTokenOption;
@@ -599,6 +609,13 @@ type
   TPScannerFormatPathEvent = function(const aPath: string): string of object;
   TPScannerFormatPathEvent = function(const aPath: string): string of object;
 
 
   TPascalScanner = class
   TPascalScanner = class
+  private
+    type
+      TWarnMsgNumberState = record
+        Number: integer;
+        State: TWarnMsgState;
+      end;
+      TWarnMsgNumberStateArr = array of TWarnMsgNumberState;
   private
   private
     FAllowedBoolSwitches: TBoolSwitches;
     FAllowedBoolSwitches: TBoolSwitches;
     FAllowedModes: TModeSwitches;
     FAllowedModes: TModeSwitches;
@@ -641,6 +658,7 @@ type
     FTokenStr: PChar;
     FTokenStr: PChar;
     FIncludeStack: TFPList;
     FIncludeStack: TFPList;
     FFiles: TStrings;
     FFiles: TStrings;
+    FWarnMsgStates: TWarnMsgNumberStateArr;
 
 
     // Preprocessor $IFxxx skipping data
     // Preprocessor $IFxxx skipping data
     PPSkipMode: TPascalScannerPPSkipMode;
     PPSkipMode: TPascalScannerPPSkipMode;
@@ -652,6 +670,7 @@ type
     function GetCurrentValueSwitch(V: TValueSwitch): string;
     function GetCurrentValueSwitch(V: TValueSwitch): string;
     function GetForceCaret: Boolean;
     function GetForceCaret: Boolean;
     function GetMacrosOn: boolean;
     function GetMacrosOn: boolean;
+    function IndexOfWarnMsgState(Number: integer; InsertPos: boolean): integer;
     function OnCondEvalFunction(Sender: TCondDirectiveEvaluator; Name,
     function OnCondEvalFunction(Sender: TCondDirectiveEvaluator; Name,
       Param: String; out Value: string): boolean;
       Param: String; out Value: string): boolean;
     procedure OnCondEvalLog(Sender: TCondDirectiveEvaluator;
     procedure OnCondEvalLog(Sender: TCondDirectiveEvaluator;
@@ -690,12 +709,14 @@ type
     procedure HandleError(Param: String); virtual;
     procedure HandleError(Param: String); virtual;
     procedure HandleMessageDirective(Param: String); virtual;
     procedure HandleMessageDirective(Param: String); virtual;
     procedure HandleIncludeFile(Param: String); virtual;
     procedure HandleIncludeFile(Param: String); virtual;
-    procedure HandleUnDefine(Param: String);virtual;
-    function HandleInclude(const Param: String): TToken;virtual;
-    procedure HandleMode(const Param: String);virtual;
-    procedure HandleModeSwitch(const Param: String);virtual;
-    function HandleMacro(AIndex: integer): TToken;virtual;
-    procedure HandleInterfaces(const Param: String);virtual;
+    procedure HandleUnDefine(Param: String); virtual;
+    function HandleInclude(const Param: String): TToken; virtual;
+    procedure HandleMode(const Param: String); virtual;
+    procedure HandleModeSwitch(const Param: String); virtual;
+    function HandleMacro(AIndex: integer): TToken; virtual;
+    procedure HandleInterfaces(const Param: String); virtual;
+    procedure HandleWarn(Param: String); virtual;
+    procedure HandleWarnIdentifier(IdentifierLoCase, ValueLoCase: String); virtual;
     procedure PushStackItem; virtual;
     procedure PushStackItem; virtual;
     function DoFetchTextToken: TToken;
     function DoFetchTextToken: TToken;
     function DoFetchToken: TToken;
     function DoFetchToken: TToken;
@@ -705,6 +726,8 @@ type
     procedure SetCurrentBoolSwitches(const AValue: TBoolSwitches); virtual;
     procedure SetCurrentBoolSwitches(const AValue: TBoolSwitches); virtual;
     procedure SetCurrentModeSwitches(AValue: TModeSwitches); virtual;
     procedure SetCurrentModeSwitches(AValue: TModeSwitches); virtual;
     procedure SetCurrentValueSwitch(V: TValueSwitch; const AValue: string);
     procedure SetCurrentValueSwitch(V: TValueSwitch; const AValue: string);
+    procedure SetWarnMsgState(Number: integer; State: TWarnMsgState); virtual;
+    function GetWarnMsgState(Number: integer): TWarnMsgState; virtual;
     function LogEvent(E : TPScannerLogEvent) : Boolean; inline;
     function LogEvent(E : TPScannerLogEvent) : Boolean; inline;
   public
   public
     constructor Create(AFileResolver: TBaseFileResolver);
     constructor Create(AFileResolver: TBaseFileResolver);
@@ -757,6 +780,7 @@ type
     property AllowedValueSwitches: TValueSwitches read FAllowedValueSwitches Write SetAllowedValueSwitches;
     property AllowedValueSwitches: TValueSwitches read FAllowedValueSwitches Write SetAllowedValueSwitches;
     property ReadOnlyValueSwitches: TValueSwitches read FReadOnlyValueSwitches Write SetReadOnlyValueSwitches;// cannot be changed by code
     property ReadOnlyValueSwitches: TValueSwitches read FReadOnlyValueSwitches Write SetReadOnlyValueSwitches;// cannot be changed by code
     property CurrentValueSwitch[V: TValueSwitch]: string read GetCurrentValueSwitch Write SetCurrentValueSwitch;
     property CurrentValueSwitch[V: TValueSwitch]: string read GetCurrentValueSwitch Write SetCurrentValueSwitch;
+    property WarnMsgState[Number: integer]: TWarnMsgState read GetWarnMsgState write SetWarnMsgState;
     property Options : TPOptions read FOptions write SetOptions;
     property Options : TPOptions read FOptions write SetOptions;
     Property SkipWhiteSpace : Boolean Read FSkipWhiteSpace Write FSkipWhiteSpace;
     Property SkipWhiteSpace : Boolean Read FSkipWhiteSpace Write FSkipWhiteSpace;
     Property SkipComments : Boolean Read FSkipComments Write FSkipComments;
     Property SkipComments : Boolean Read FSkipComments Write FSkipComments;
@@ -2777,6 +2801,79 @@ begin
   CurrentValueSwitch[vsInterfaces]:=NewValue;
   CurrentValueSwitch[vsInterfaces]:=NewValue;
 end;
 end;
 
 
+procedure TPascalScanner.HandleWarn(Param: String);
+// $warn identifier on|off|default|error
+var
+  p, StartPos: Integer;
+  Identifier, Value: String;
+begin
+  Param:=lowercase(Param);
+  p:=1;
+  while (p<=length(Param)) and (Param[p] in [' ',#9]) do inc(p);
+  StartPos:=p;
+  while (p<=length(Param)) and (Param[p] in ['a'..'z','0'..'9','_']) do inc(p);
+  Identifier:=copy(Param,StartPos,p-StartPos);
+  while (p<=length(Param)) and (Param[p] in [' ',#9]) do inc(p);
+  StartPos:=p;
+  while (p<=length(Param)) and (Param[p] in ['a'..'z']) do inc(p);
+  Value:=copy(Param,StartPos,p-StartPos);
+  HandleWarnIdentifier(Identifier,Value);
+end;
+
+procedure TPascalScanner.HandleWarnIdentifier(IdentifierLoCase,
+  ValueLoCase: String);
+var
+  Number: LongInt;
+  State: TWarnMsgState;
+begin
+  if IdentifierLoCase='' then
+    Error(nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,['']);
+  if IdentifierLoCase[1] in ['0'..'9'] then
+    begin
+    // fpc number
+    Number:=StrToIntDef(IdentifierLoCase,-1);
+    if Number<0 then
+      begin
+      DoLog(mtWarning,nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,[IdentifierLoCase]);
+      exit;
+      end;
+    end
+  else if (IdentifierLoCase[1]='w') and (msDelphi in CurrentModeSwitches) then
+    begin
+    // delphi W number
+    Number:=StrToIntDef(copy(IdentifierLoCase,2,10),-1);
+    if Number<0 then
+      begin
+      DoLog(mtWarning,nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,[IdentifierLoCase]);
+      exit;
+      end;
+    Number:=-1;
+    end
+  else
+    begin
+    DoLog(mtWarning,nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,[IdentifierLoCase]);
+    exit;
+    end;
+
+  if ValueLoCase='' then
+    begin
+    DoLog(mtWarning,nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,['']);
+    exit;
+    end;
+  case ValueLoCase of
+  'on': State:=wmsOn;
+  'off': State:=wmsOff;
+  'default': State:=wmsDefault;
+  'error': State:=wmsError;
+  else
+    DoLog(mtWarning,nIllegalStateForWarnDirective,SIllegalStateForWarnDirective,[ValueLoCase]);
+    exit;
+  end;
+
+  if Number>=0 then
+    SetWarnMsgState(Number,State);
+end;
+
 procedure TPascalScanner.HandleDefine(Param: String);
 procedure TPascalScanner.HandleDefine(Param: String);
 
 
 Var
 Var
@@ -3193,6 +3290,8 @@ begin
           DoBoolDirective(bsTypeInfo);
           DoBoolDirective(bsTypeInfo);
         'UNDEF':
         'UNDEF':
           HandleUnDefine(Param);
           HandleUnDefine(Param);
+        'WARN':
+          HandleWarn(Param);
         'WARNING':
         'WARNING':
           DoLog(mtWarning,nUserDefined,SUserDefined,[Param]);
           DoLog(mtWarning,nUserDefined,SUserDefined,[Param]);
         'WARNINGS':
         'WARNINGS':
@@ -3755,6 +3854,34 @@ begin
   Result:=bsMacro in FCurrentBoolSwitches;
   Result:=bsMacro in FCurrentBoolSwitches;
 end;
 end;
 
 
+function TPascalScanner.IndexOfWarnMsgState(Number: integer; InsertPos: boolean
+  ): integer;
+var
+  l, r, m, CurNumber: Integer;
+begin
+  l:=0;
+  r:=length(FWarnMsgStates)-1;
+  m:=0;
+  while l<=r do
+    begin
+    m:=(l+r) div 2;
+    CurNumber:=FWarnMsgStates[m].Number;
+    if Number>CurNumber then
+      l:=m+1
+    else if Number<CurNumber then
+      r:=m-1
+    else
+      exit(m);
+    end;
+  if not InsertPos then
+    exit(-1);
+  if length(FWarnMsgStates)=0 then
+    exit(0);
+  if (m<length(FWarnMsgStates)) and (FWarnMsgStates[m].Number<=Number) then
+    inc(m);
+  Result:=m;
+end;
+
 function TPascalScanner.OnCondEvalFunction(Sender: TCondDirectiveEvaluator;
 function TPascalScanner.OnCondEvalFunction(Sender: TCondDirectiveEvaluator;
   Name, Param: String; out Value: string): boolean;
   Name, Param: String; out Value: string): boolean;
 begin
 begin
@@ -3922,6 +4049,70 @@ begin
   FCurrentValueSwitches[V]:=AValue;
   FCurrentValueSwitches[V]:=AValue;
 end;
 end;
 
 
+procedure TPascalScanner.SetWarnMsgState(Number: integer; State: TWarnMsgState);
+
+  {$IF FPC_FULLVERSION<30101}
+  procedure Delete(var A: TWarnMsgNumberStateArr; Index, Count: integer); overload;
+  var
+    i: Integer;
+  begin
+    if Index<0 then
+      Error(nErrDivByZero,'[20180627142123]');
+    if Index+Count>length(A) then
+      Error(nErrDivByZero,'[20180627142127]');
+    for i:=Index+Count to length(A)-1 do
+      A[i-Count]:=A[i];
+    SetLength(A,length(A)-Count);
+  end;
+
+  procedure Insert(Item: TWarnMsgNumberState; var A: TWarnMsgNumberStateArr; Index: integer); overload;
+  var
+    i: Integer;
+  begin
+    if Index<0 then
+      Error(nErrDivByZero,'[20180627142133]');
+    if Index>length(A) then
+      Error(nErrDivByZero,'[20180627142137]');
+    SetLength(A,length(A)+1);
+    for i:=length(A)-1 downto Index+1 do
+      A[i]:=A[i-1];
+    A[Index]:=Item;
+  end;
+  {$ENDIF}
+
+var
+  i: Integer;
+  Item: TWarnMsgNumberState;
+begin
+  i:=IndexOfWarnMsgState(Number,true);
+  if (i<length(FWarnMsgStates)) and (FWarnMsgStates[i].Number=Number) then
+    begin
+    // already exists
+    if State=wmsDefault then
+      Delete(FWarnMsgStates,i,1)
+    else
+      FWarnMsgStates[i].State:=State;
+    end
+  else if State<>wmsDefault then
+    begin
+    // new state
+    Item.Number:=Number;
+    Item.State:=State;
+    Insert(Item,FWarnMsgStates,i);
+    end;
+end;
+
+function TPascalScanner.GetWarnMsgState(Number: integer): TWarnMsgState;
+var
+  i: Integer;
+begin
+  i:=IndexOfWarnMsgState(Number,false);
+  if i<0 then
+    Result:=wmsDefault
+  else
+    Result:=FWarnMsgStates[i].State;
+end;
+
 procedure TPascalScanner.SetMacrosOn(const AValue: boolean);
 procedure TPascalScanner.SetMacrosOn(const AValue: boolean);
 begin
 begin
   if AValue then
   if AValue then

+ 55 - 14
packages/fcl-passrc/tests/tcresolver.pas

@@ -682,6 +682,7 @@ type
     Procedure TestDynArrayOfLongint;
     Procedure TestDynArrayOfLongint;
     Procedure TestStaticArray;
     Procedure TestStaticArray;
     Procedure TestStaticArrayOfChar;
     Procedure TestStaticArrayOfChar;
+    Procedure TestStaticArrayOfCharDelphi;
     Procedure TestStaticArrayOfRangeElCheckFail;
     Procedure TestStaticArrayOfRangeElCheckFail;
     Procedure TestArrayOfArray;
     Procedure TestArrayOfArray;
     Procedure TestArrayOfArray_NameAnonymous;
     Procedure TestArrayOfArray_NameAnonymous;
@@ -801,6 +802,7 @@ type
     Procedure TestHint_ElementHints;
     Procedure TestHint_ElementHints;
     Procedure TestHint_ElementHintsMsg;
     Procedure TestHint_ElementHintsMsg;
     Procedure TestHint_ElementHintsAlias;
     Procedure TestHint_ElementHintsAlias;
+    Procedure TestHint_ElementHints_WarnOff_SymbolDeprecated;
 
 
     // attributes
     // attributes
     Procedure TestAttributes_Ignore;
     Procedure TestAttributes_Ignore;
@@ -11795,19 +11797,44 @@ procedure TTestResolver.TestStaticArrayOfChar;
 begin
 begin
   ResolverEngine.ExprEvaluator.DefaultStringCodePage:=CP_UTF8;
   ResolverEngine.ExprEvaluator.DefaultStringCodePage:=CP_UTF8;
   StartProgram(false);
   StartProgram(false);
-  Add('type');
-  Add('  TArrA = array[1..3] of char;');
-  Add('const');
-  Add('  A: TArrA = (''p'',''a'',''p'');'); // duplicate allowed, this bracket is not a set
-  Add('  B: TArrA = ''pas'';');
-  Add('  Three = length(TArrA);');
-  Add('  C: array[1..Three] of char = ''pas'';');
-  Add('  D = ''pp'';');
-  Add('  E: array[length(D)..Three] of char = D;');
-  Add('  F: array[1..2] of widechar = ''äö'';');
-  Add('  G: array[1..2] of char = ''ä'';');
-  Add('  H: array[1..4] of char = ''äö'';');
-  Add('begin');
+  Add([
+  'type',
+  '  TArrA = array[1..3] of char;',
+  'const',
+  '  A: TArrA = (''p'',''a'',''p'');', // duplicate allowed, this bracket is not a set
+  '  B: TArrA = ''pas'';',
+  '  Three = length(TArrA);',
+  '  C: array[1..Three] of char = ''pas'';',
+  '  D = ''pp'';',
+  '  E: array[length(D)..Three] of char = D;',
+  '  F: array[1..2] of widechar = ''äö'';',
+  '  G: array[1..2] of char = ''ä'';',
+  '  H: array[1..4] of char = ''äö'';',
+  '  I: array[1..4] of char = ''ä''+''ö'';',
+  'begin']);
+  ParseProgram;
+end;
+
+procedure TTestResolver.TestStaticArrayOfCharDelphi;
+begin
+  ResolverEngine.ExprEvaluator.DefaultStringCodePage:=CP_UTF8;
+  StartProgram(false);
+  Add([
+  '{$mode delphi}',
+  'type',
+  '  TArrA = array[1..3] of char;',
+  'const',
+  '  A: TArrA = (''p'',''a'',''p'');', // duplicate allowed, this bracket is not a set
+  '  B: TArrA = ''pas'';',
+  '  Three = length(TArrA);',
+  '  C: array[1..Three] of char = ''pas'';',
+  '  D = ''pp'';',
+  '  E: array[length(D)..Three] of char = D;',
+  '  F: array[1..2] of widechar = ''äö'';',
+  '  G: array[1..2] of char = ''ä'';',
+  '  H: array[1..4] of char = ''äö'';',
+  '  I: array[1..4] of char = ''ä''+''ö'';',
+  'begin']);
   ParseProgram;
   ParseProgram;
 end;
 end;
 
 
@@ -14184,7 +14211,7 @@ begin
   'begin',
   'begin',
   '']);
   '']);
   ParseProgram;
   ParseProgram;
-  WriteSources('afile.pp',3,4);
+  //WriteSources('afile.pp',3,4);
 
 
   aMarker:=FirstSrcMarker;
   aMarker:=FirstSrcMarker;
   while aMarker<>nil do
   while aMarker<>nil do
@@ -14197,6 +14224,20 @@ begin
   CheckResolverUnexpectedHints(true);
   CheckResolverUnexpectedHints(true);
 end;
 end;
 
 
+procedure TTestResolver.TestHint_ElementHints_WarnOff_SymbolDeprecated;
+begin
+  exit;  // ToDo
+  StartProgram(false);
+  Add([
+  '{$warn symbol_deprecated off}',
+  'type',
+  '  i: byte; deprecated;',
+  'begin',
+  '']);
+  ParseProgram;
+  CheckResolverUnexpectedHints(true);
+end;
+
 procedure TTestResolver.TestAttributes_Ignore;
 procedure TTestResolver.TestAttributes_Ignore;
 begin
 begin
   StartProgram(false);
   StartProgram(false);