123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- unit TCPas2JSAnalyzer;
- {$mode ObjFPC}{$H+}
- interface
- uses
- Classes, SysUtils, fpcunit, testregistry, StrUtils, TCModules, PasTree,
- PScanner, PasResolver, PasUseAnalyzer, PasResolveEval, Pas2jsUseAnalyzer;
- type
- { TCustomTestPas2jsAnalyzer }
- TCustomTestPas2jsAnalyzer = class(TCustomTestModule)
- private
- FAnalyzer: TPas2JSAnalyzer;
- FPAMessages: TFPList; // list of TPAMessage
- FPAGoodMessages: TFPList;
- FProcAnalyzer: TPas2JSAnalyzer;
- function GetPAMessages(Index: integer): TPAMessage;
- procedure OnAnalyzerMessage(Sender: TObject; Msg: TPAMessage);
- protected
- procedure SetUp; override;
- procedure TearDown; override;
- procedure ParseModule; override;
- procedure AnalyzeModule; virtual;
- procedure AnalyzeProgram; virtual;
- procedure AnalyzeUnit; virtual;
- procedure AnalyzeWholeProgram; virtual;
- procedure CheckUsedMarkers; virtual;
- procedure CheckUseAnalyzerHint(MsgType: TMessageType; MsgNumber: integer;
- const MsgText: string); virtual;
- procedure CheckUseAnalyzerUnexpectedHints; virtual;
- procedure CheckUnitUsed(const aFilename: string; Used: boolean); virtual;
- procedure CheckScopeReferences(const ScopeName: string;
- const RefNames: array of string);
- public
- property Analyzer: TPas2JSAnalyzer read FAnalyzer;
- property ProcAnalyzer: TPas2JSAnalyzer read FProcAnalyzer;
- function PAMessageCount: integer;
- property PAMessages[Index: integer]: TPAMessage read GetPAMessages;
- end;
- { TTestPas2jsAnalyzer }
- TTestPas2jsAnalyzer = class(TCustomTestPas2jsAnalyzer)
- Published
- procedure TestM_ProgramLocalVar;
- procedure TestM_PassRecordToJSValue;
- procedure TestM_StaticArrayDim2;
- end;
- implementation
- { TTestPas2jsAnalyzer }
- procedure TTestPas2jsAnalyzer.TestM_ProgramLocalVar;
- begin
- StartProgram(false);
- Add([
- 'procedure {#DoIt_used}DoIt;',
- 'var {#l_notused}l: longint;',
- 'begin',
- 'end;',
- 'begin',
- ' DoIt;',
- 'end.']);
- AnalyzeProgram;
- end;
- procedure TTestPas2jsAnalyzer.TestM_PassRecordToJSValue;
- begin
- StartProgram(false);
- Add([
- 'type',
- ' {#trec_used}TRec = record',
- ' {#x_used}x: word;',
- ' end;',
- ' {#tbig_used}TBig = record',
- ' {#r_used}r: TRec;',
- ' end;',
- ' {#tnope_used}TNope = record',
- ' {#a_notused}a: word;',
- ' {#b_used}b: word;',
- ' end;',
- 'procedure DoIt(v: JSValue);',
- 'begin',
- 'end;',
- 'var big: TBig;',
- ' n: TNope;',
- 'begin',
- ' DoIt(big);',
- ' DoIt(n.b);',
- 'end.']);
- AnalyzeProgram;
- end;
- procedure TTestPas2jsAnalyzer.TestM_StaticArrayDim2;
- begin
- StartProgram(false);
- Add([
- 'type',
- ' TObject = class',
- ' end;',
- 'var',
- ' oa: array [1..10, 1..10] of TObject;',
- 'begin',
- 'end.']);
- AnalyzeProgram;
- CheckUseAnalyzerHint(mtHint,nPALocalXYNotUsed,'Local class "TObject" not used');
- CheckUseAnalyzerHint(mtHint,nPALocalVariableNotUsed,'Local variable "oa" not used');
- CheckUseAnalyzerUnexpectedHints;
- end;
- { TCustomTestPas2jsAnalyzer }
- function TCustomTestPas2jsAnalyzer.GetPAMessages(Index: integer): TPAMessage;
- begin
- Result:=TPAMessage(FPAMessages[Index]);
- end;
- procedure TCustomTestPas2jsAnalyzer.OnAnalyzerMessage(Sender: TObject;
- Msg: TPAMessage);
- begin
- Msg.AddRef;
- FPAMessages.Add(Msg);
- end;
- procedure TCustomTestPas2jsAnalyzer.SetUp;
- begin
- inherited SetUp;
- FPAMessages:=TFPList.Create;
- FPAGoodMessages:=TFPList.Create;
- FAnalyzer:=TPas2JSAnalyzer.Create;
- FAnalyzer.Resolver:=ResolverEngine;
- Analyzer.OnMessage:=@OnAnalyzerMessage;
- end;
- procedure TCustomTestPas2jsAnalyzer.TearDown;
- var
- i: Integer;
- begin
- FreeAndNil(FPAGoodMessages);
- for i:=0 to FPAMessages.Count-1 do
- TPAMessage(FPAMessages[i]).Release;
- FreeAndNil(FPAMessages);
- FreeAndNil(FAnalyzer);
- FreeAndNil(FProcAnalyzer);
- inherited TearDown;
- end;
- procedure TCustomTestPas2jsAnalyzer.ParseModule;
- begin
- inherited ParseModule;
- if SkipTests then exit;
- CheckReferenceDirectives;
- end;
- procedure TCustomTestPas2jsAnalyzer.AnalyzeModule;
- begin
- Analyzer.AnalyzeModule(Module);
- Analyzer.EmitModuleHints(Module);
- CheckUsedMarkers;
- end;
- procedure TCustomTestPas2jsAnalyzer.AnalyzeProgram;
- begin
- ParseProgram;
- AnalyzeModule;
- end;
- procedure TCustomTestPas2jsAnalyzer.AnalyzeUnit;
- begin
- ParseUnit;
- AnalyzeModule;
- end;
- procedure TCustomTestPas2jsAnalyzer.AnalyzeWholeProgram;
- begin
- ParseProgram;
- Analyzer.AnalyzeWholeProgram(Module as TPasProgram);
- CheckUsedMarkers;
- end;
- procedure TCustomTestPas2jsAnalyzer.CheckUsedMarkers;
- type
- TUsed = (
- uUsed,
- uNotUsed,
- uTypeInfo,
- uNoTypeinfo
- );
- var
- aMarker: PSrcMarker;
- p: SizeInt;
- Postfix: String;
- Elements: TFPList;
- i: Integer;
- El, FoundEl: TPasElement;
- ExpectedUsed: TUsed;
- begin
- aMarker:=FirstSrcMarker;
- while aMarker<>nil do
- begin
- {$IFDEF VerbosePasAnalyzer}
- writeln('TCustomTestPas2jsAnalyzer.CheckUsedMarkers ',aMarker^.Identifier,' Line=',aMarker^.Row,' StartCol=',aMarker^.StartCol,' EndCol=',aMarker^.EndCol);
- {$ENDIF}
- p:=RPos('_',aMarker^.Identifier);
- if p>1 then
- begin
- Postfix:=copy(aMarker^.Identifier,p+1);
- if Postfix='used' then
- ExpectedUsed:=uUsed
- else if Postfix='notused' then
- ExpectedUsed:=uNotUsed
- else if Postfix='typeinfo' then
- ExpectedUsed:=uTypeInfo
- else if Postfix='notypeinfo' then
- ExpectedUsed:=uNoTypeInfo
- else
- RaiseErrorAtSrcMarker('TCustomTestPas2jsAnalyzer.CheckUsedMarkers unknown postfix "'+Postfix+'"',aMarker);
- Elements:=FindElementsAt(aMarker);
- try
- FoundEl:=nil;
- for i:=0 to Elements.Count-1 do
- begin
- El:=TPasElement(Elements[i]);
- {$IFDEF VerbosePasAnalyzer}
- writeln('TCustomTestPas2jsAnalyzer.CheckUsedMarkers ',aMarker^.Identifier,' ',i,'/',Elements.Count,' El=',GetObjName(El),' ',GetObjName(El.CustomData));
- {$ENDIF}
- case ExpectedUsed of
- uUsed,uNotUsed:
- if Analyzer.IsUsed(El) then
- begin
- FoundEl:=El;
- break;
- end;
- uTypeInfo,uNoTypeinfo:
- if Analyzer.IsTypeInfoUsed(El) then
- begin
- FoundEl:=El;
- break;
- end;
- end;
- end;
- if FoundEl<>nil then
- case ExpectedUsed of
- uNotUsed:
- RaiseErrorAtSrcMarker('expected element to be *not* used, but it is marked',aMarker);
- uNoTypeinfo:
- RaiseErrorAtSrcMarker('expected element to have *no* typeinfo, but it is marked',aMarker);
- end
- else
- case ExpectedUsed of
- uUsed:
- RaiseErrorAtSrcMarker('expected element to be used, but it is not marked',aMarker);
- uTypeInfo:
- RaiseErrorAtSrcMarker('expected element to have typeinfo, but it is not marked',aMarker);
- end;
- finally
- Elements.Free;
- end;
- end;
- aMarker:=aMarker^.Next;
- end;
- end;
- procedure TCustomTestPas2jsAnalyzer.CheckUseAnalyzerHint(MsgType: TMessageType;
- MsgNumber: integer; const MsgText: string);
- var
- i: Integer;
- Msg: TPAMessage;
- s: string;
- begin
- i:=PAMessageCount-1;
- while i>=0 do
- begin
- Msg:=PAMessages[i];
- if (Msg.MsgNumber=MsgNumber) then
- begin
- if (Msg.MsgType=MsgType) and (Msg.MsgText=MsgText) then
- begin
- FPAGoodMessages.Add(Msg);
- exit;
- end;
- end;
- dec(i);
- end;
- // mismatch
- writeln('TCustomTestPas2jsAnalyzer.CheckHasHint: ');
- for i:=0 to PAMessageCount-1 do
- begin
- Msg:=PAMessages[i];
- writeln(' ',i,'/',PAMessageCount,': [',Msg.Id,'] ',Msg.MsgType,': (',Msg.MsgNumber,') {',Msg.MsgText,'}');
- end;
- s:='';
- str(MsgType,s);
- Fail('Analyzer Message not found: '+s+': ('+IntToStr(MsgNumber)+') {'+MsgText+'}');
- end;
- procedure TCustomTestPas2jsAnalyzer.CheckUseAnalyzerUnexpectedHints;
- var
- i: Integer;
- Msg: TPAMessage;
- s: String;
- begin
- for i:=0 to PAMessageCount-1 do
- begin
- Msg:=PAMessages[i];
- if FPAGoodMessages.IndexOf(Msg)>=0 then continue;
- s:='';
- str(Msg.MsgType,s);
- Fail('Unexpected analyzer message found ['+IntToStr(Msg.Id)+'] '+s+': ('+IntToStr(Msg.MsgNumber)+') {'+Msg.MsgText+'}');
- end;
- end;
- procedure TCustomTestPas2jsAnalyzer.CheckUnitUsed(const aFilename: string;
- Used: boolean);
- var
- aResolver: TTestEnginePasResolver;
- PAEl: TPAElement;
- begin
- aResolver:=FindModuleWithFilename(aFilename);
- AssertNotNull('unit not found "'+aFilename+'"',aResolver);
- AssertNotNull('unit module not found "'+aFilename+'"',aResolver.Module);
- PAEl:=Analyzer.FindElement(aResolver.Module);
- if PAEl<>nil then
- begin
- // unit is used
- if not Used then
- Fail('expected unit "'+aFilename+'" not used, but it is used');
- end
- else
- begin
- // unit is not used
- if Used then
- Fail('expected unit "'+aFilename+'" used, but it is not used');
- end;
- end;
- procedure TCustomTestPas2jsAnalyzer.CheckScopeReferences(
- const ScopeName: string; const RefNames: array of string);
- type
- TEntry = record
- Name: string;
- Access: TPSRefAccess;
- end;
- var
- Entries: array of TEntry;
- procedure CheckRefs(ScopeRefs: TPasScopeReferences; const Prefix: string);
- procedure DumpRefsAndFail(Refs: TFPList; const Msg: string);
- var
- i: Integer;
- Ref: TPasScopeReference;
- begin
- {$IFDEF VerbosePasAnalyzer}
- if Refs.Count=0 then
- writeln('DumpRefsAndFail ',Prefix,' NO REFS');
- {$ENDIF}
- for i:=0 to Refs.Count-1 do
- begin
- Ref:=TPasScopeReference(Refs[i]);
- if Ref=nil then break;
- {$IFDEF VerbosePasAnalyzer}
- writeln('DumpRefsAndFail ',Prefix,' ',i,' ',GetObjName(Ref.Element),' ',Ref.Access);
- {$ENDIF}
- end;
- Fail(Prefix+': '+Msg);
- end;
- var
- Refs: TFPList;
- j, i: Integer;
- o: TObject;
- Ref: TPasScopeReference;
- begin
- if ScopeRefs=nil then
- Refs:=TFPList.Create
- else
- Refs:=ScopeRefs.GetList;
- try
- // check that Refs only contains TPasProcScopeReference
- for i:=0 to Refs.Count-1 do
- begin
- o:=TObject(Refs[i]);
- if not (o is TPasScopeReference) then
- Fail(Prefix+': Refs['+IntToStr(i)+'] '+GetObjName(o));
- end;
- // check that all Entries are referenced
- for i:=0 to length(Entries)-1 do
- begin
- j:=Refs.Count-1;
- while (j>=0)
- and (CompareText(Entries[i].Name,TPasScopeReference(Refs[j]).Element.Name)<>0) do
- dec(j);
- if j<0 then
- DumpRefsAndFail(Refs,'Missing reference "'+Entries[i].Name+'"');
- Ref:=TPasScopeReference(Refs[j]);
- if (Entries[i].Access<>psraNone) and (Ref.Access<>Entries[i].Access) then
- DumpRefsAndFail(Refs,'Wrong reference access "'+Entries[i].Name+'",'
- +' expected '+dbgs(Entries[i].Access)+', but got '+dbgs(Ref.Access));
- end;
- // check that no other references are in Refs
- for i:=0 to Refs.Count-1 do
- begin
- Ref:=TPasScopeReference(Refs[i]);
- j:=length(Entries)-1;
- while (j>=0)
- and (CompareText(Ref.Element.Name,Entries[j].Name)<>0) do
- dec(j);
- if j<0 then
- DumpRefsAndFail(Refs,'Unneeded reference "'+GetObjName(Ref.Element)+'"');
- end;
- finally
- Refs.Free;
- end;
- end;
- function FindProc(Section: TPasSection): boolean;
- var
- i: Integer;
- El: TPasElement;
- Proc: TPasProcedure;
- Scope: TPasProcedureScope;
- begin
- for i:=0 to Section.Declarations.Count-1 do
- begin
- El:=TPasElement(Section.Declarations[i]);
- if CompareText(El.Name,ScopeName)<>0 then continue;
- if not (El is TPasProcedure) then
- Fail('El is not proc '+GetObjName(El));
- Proc:=TPasProcedure(El);
- Scope:=Proc.CustomData as TPasProcedureScope;
- if Scope.DeclarationProc<>nil then continue;
- // check references created by AnalyzeModule
- CheckRefs(Scope.References,'AnalyzeModule');
- exit(true);
- end;
- Result:=false;
- end;
- procedure CheckInitialFinalization(El: TPasImplBlock);
- var
- Scope: TPasInitialFinalizationScope;
- begin
- Scope:=El.CustomData as TPasInitialFinalizationScope;
- CheckRefs(Scope.References,'AnalyzeModule');
- end;
- var
- i: Integer;
- begin
- Entries:=nil;
- SetLength(Entries,High(RefNames)-low(RefNames)+1);
- for i:=low(RefNames) to high(RefNames) do
- begin
- Entries[i].Name:=RefNames[i];
- Entries[i].Access:=psraNone;
- end;
- if Module is TPasProgram then
- begin
- if CompareText(ScopeName,'begin')=0 then
- begin
- // check begin-block references created by AnalyzeModule
- CheckInitialFinalization(Module.InitializationSection);
- exit;
- end
- else if FindProc(TPasProgram(Module).ProgramSection) then
- exit;
- end
- else if Module is TPasLibrary then
- begin
- if CompareText(ScopeName,'begin')=0 then
- begin
- // check begin-block references created by AnalyzeModule
- CheckInitialFinalization(Module.InitializationSection);
- exit;
- end
- else if FindProc(TPasLibrary(Module).LibrarySection) then
- exit;
- end
- else if Module.ClassType=TPasModule then
- begin
- if CompareText(ScopeName,'initialization')=0 then
- begin
- // check initialization references created by AnalyzeModule
- CheckInitialFinalization(Module.InitializationSection);
- exit;
- end
- else if CompareText(ScopeName,'finalization')=0 then
- begin
- // check finalization references created by AnalyzeModule
- CheckInitialFinalization(Module.FinalizationSection);
- exit;
- end
- else if FindProc(Module.InterfaceSection) then
- exit
- else if FindProc(Module.ImplementationSection) then
- exit;
- end;
- Fail('missing proc '+ScopeName);
- end;
- function TCustomTestPas2jsAnalyzer.PAMessageCount: integer;
- begin
- Result:=FPAMessages.Count;
- end;
- Initialization
- RegisterTests([TTestPas2jsAnalyzer]);
- end.
|