fcl-passrc: useanalyzer: option to generate references of all used procs

git-svn-id: trunk@38355 -
This commit is contained in:
Mattias Gaertner 2018-02-26 17:32:28 +00:00
parent f623038da6
commit 02e57c36c2
3 changed files with 192 additions and 72 deletions

View File

@ -34,11 +34,11 @@ Working:
- Hint: 'Private const "%s" never used' - Hint: 'Private const "%s" never used'
- Hint: 'Private property "%s" never used' - Hint: 'Private property "%s" never used'
- Hint: 'Function result does not seem to be set' - Hint: 'Function result does not seem to be set'
- TPasArgument: compute the effective Access
- calls: use the effective Access of arguments
ToDo: ToDo:
- Add test: Call Override: e.g. A.Proc, mark only overrides of descendants of A - Add test: Call Override: e.g. A.Proc, mark only overrides of descendants of A
- TPasArgument: compute the effective Access
- calls: use the effective Access of arguments
} }
unit PasUseAnalyzer; unit PasUseAnalyzer;
@ -146,7 +146,8 @@ type
end; end;
TPasAnalyzerOption = ( TPasAnalyzerOption = (
paoOnlyExports // default: use all class members accessible from outside (protected, but not private) paoOnlyExports, // default: use all class members accessible from outside (protected, but not private)
paoProcReferences // collect TPasProcedureScope.References of top lvl proc implementations
); );
TPasAnalyzerOptions = set of TPasAnalyzerOption; TPasAnalyzerOptions = set of TPasAnalyzerOption;
@ -180,6 +181,7 @@ type
FUsedElements: TAVLTree; // tree of TPAElement sorted for Element FUsedElements: TAVLTree; // tree of TPAElement sorted for Element
FRefProcDecl: TPasProcedure; // if set, collect only what this proc references FRefProcDecl: TPasProcedure; // if set, collect only what this proc references
FRefProcScope: TPasProcedureScope; // the ProcScope of FRefProcDecl FRefProcScope: TPasProcedureScope; // the ProcScope of FRefProcDecl
procedure UseElType(El: TPasElement; aType: TPasType; Mode: TPAUseMode); inline;
function AddOverride(OverriddenEl, OverrideEl: TPasElement): boolean; function AddOverride(OverriddenEl, OverrideEl: TPasElement): boolean;
function FindOverrideNode(El: TPasElement): TAVLTreeNode; function FindOverrideNode(El: TPasElement): TAVLTreeNode;
function FindOverrideList(El: TPasElement): TPAOverrideList; function FindOverrideList(El: TPasElement): TPAOverrideList;
@ -188,6 +190,7 @@ type
protected protected
procedure RaiseInconsistency(const Id: int64; Msg: string); procedure RaiseInconsistency(const Id: int64; Msg: string);
procedure RaiseNotSupported(const Id: int64; El: TPasElement; const Msg: string = ''); procedure RaiseNotSupported(const Id: int64; El: TPasElement; const Msg: string = '');
function FindTopProcScope(El: TPasElement; Decl: boolean): TPasProcedureScope;
// mark used elements // mark used elements
function Add(El: TPasElement; CheckDuplicate: boolean = true; function Add(El: TPasElement; CheckDuplicate: boolean = true;
aClass: TPAElementClass = nil): TPAElement; aClass: TPAElementClass = nil): TPAElement;
@ -196,7 +199,8 @@ type
procedure CreateTree; virtual; procedure CreateTree; virtual;
function MarkElementAsUsed(El: TPasElement; aClass: TPAElementClass = nil): boolean; // true if new function MarkElementAsUsed(El: TPasElement; aClass: TPAElementClass = nil): boolean; // true if new
function ElementVisited(El: TPasElement; Mode: TPAUseMode): boolean; function ElementVisited(El: TPasElement; Mode: TPAUseMode): boolean;
function MarkProcRef(El: TPasElement; Access: TPSRefAccess): boolean; // true if outside FRefProcDecl function MarkSingleProcRef(El: TPasElement; Access: TPSRefAccess): boolean; // true if outside FRefProcDecl
procedure MarkScopeRef(Parent, El: TPasElement; Access: TPSRefAccess);
procedure UseElement(El: TPasElement; Access: TResolvedRefAccess; procedure UseElement(El: TPasElement; Access: TResolvedRefAccess;
UseFull: boolean); virtual; UseFull: boolean); virtual;
procedure UsePublished(El: TPasElement); virtual; procedure UsePublished(El: TPasElement); virtual;
@ -205,8 +209,8 @@ type
procedure UseImplBlock(Block: TPasImplBlock; Mark: boolean); virtual; procedure UseImplBlock(Block: TPasImplBlock; Mark: boolean); virtual;
procedure UseImplElement(El: TPasImplElement); virtual; procedure UseImplElement(El: TPasImplElement); virtual;
procedure UseExpr(El: TPasExpr); virtual; procedure UseExpr(El: TPasExpr); virtual;
procedure UseExprRef(Expr: TPasExpr; Access: TResolvedRefAccess; procedure UseExprRef(El: TPasElement; Expr: TPasExpr;
UseFull: boolean); virtual; Access: TResolvedRefAccess; UseFull: boolean); virtual;
procedure UseInheritedExpr(El: TInheritedExpr); virtual; procedure UseInheritedExpr(El: TInheritedExpr); virtual;
procedure UseProcedure(Proc: TPasProcedure); virtual; procedure UseProcedure(Proc: TPasProcedure); virtual;
procedure UseProcedureType(ProcType: TPasProcedureType; Mark: boolean); virtual; procedure UseProcedureType(ProcType: TPasProcedureType; Mark: boolean); virtual;
@ -418,6 +422,15 @@ begin
Result:=TPAElement(Node.Data); Result:=TPAElement(Node.Data);
end; end;
// inline
procedure TPasAnalyzer.UseElType(El: TPasElement; aType: TPasType;
Mode: TPAUseMode);
begin
if aType=nil then exit;
MarkScopeRef(El,aType,PAUseModeToPSRefAccess[Mode]);
UseType(aType,Mode);
end;
procedure TPasAnalyzer.SetOptions(AValue: TPasAnalyzerOptions); procedure TPasAnalyzer.SetOptions(AValue: TPasAnalyzerOptions);
begin begin
if FOptions=AValue then Exit; if FOptions=AValue then Exit;
@ -520,6 +533,24 @@ begin
raise E; raise E;
end; end;
function TPasAnalyzer.FindTopProcScope(El: TPasElement; Decl: boolean
): TPasProcedureScope;
begin
Result:=nil;
while El<>nil do
begin
if El is TPasProcedure then
Result:=El.CustomData as TPasProcedureScope;
El:=El.Parent;
end;
if Result=nil then exit;
if Decl then
begin
if Result.DeclarationProc<>nil then
Result:=Result.DeclarationProc.CustomData as TPasProcedureScope;
end;
end;
function TPasAnalyzer.Add(El: TPasElement; CheckDuplicate: boolean; function TPasAnalyzer.Add(El: TPasElement; CheckDuplicate: boolean;
aClass: TPAElementClass): TPAElement; aClass: TPAElementClass): TPAElement;
begin begin
@ -607,7 +638,7 @@ begin
FChecked[Mode].Add(El); FChecked[Mode].Add(El);
end; end;
function TPasAnalyzer.MarkProcRef(El: TPasElement; Access: TPSRefAccess function TPasAnalyzer.MarkSingleProcRef(El: TPasElement; Access: TPSRefAccess
): boolean; ): boolean;
var var
Parent: TPasElement; Parent: TPasElement;
@ -623,6 +654,27 @@ begin
Result:=true; Result:=true;
end; end;
procedure TPasAnalyzer.MarkScopeRef(Parent, El: TPasElement;
Access: TPSRefAccess);
var
ParentProcScope, ElProcScope: TPasProcedureScope;
begin
if El=nil then exit;
if El.Parent=Parent then exit; // same scope
if paoProcReferences in Options then
begin
ParentProcScope:=FindTopProcScope(Parent,true);
if ParentProcScope<>nil then
begin
ElProcScope:=FindTopProcScope(El,true);
if ElProcScope<>ParentProcScope then
begin
ParentProcScope.AddReference(El,Access);
end;
end;
end;
end;
procedure TPasAnalyzer.UseElement(El: TPasElement; Access: TResolvedRefAccess; procedure TPasAnalyzer.UseElement(El: TPasElement; Access: TResolvedRefAccess;
UseFull: boolean); UseFull: boolean);
var var
@ -660,6 +712,14 @@ end;
procedure TPasAnalyzer.UsePublished(El: TPasElement); procedure TPasAnalyzer.UsePublished(El: TPasElement);
// mark typeinfo, do not mark code // mark typeinfo, do not mark code
procedure UseSubEl(SubEl: TPasElement); inline;
begin
if SubEl=nil then exit;
MarkScopeRef(El,SubEl,psraTypeInfo);
UsePublished(SubEl);
end;
var var
C: TClass; C: TClass;
Members: TFPList; Members: TFPList;
@ -673,21 +733,21 @@ begin
writeln('TPasAnalyzer.UsePublished START ',GetObjName(El)); writeln('TPasAnalyzer.UsePublished START ',GetObjName(El));
{$ENDIF} {$ENDIF}
if ElementVisited(El,paumPublished) then exit; if ElementVisited(El,paumPublished) then exit;
if (FRefProcDecl<>nil) and MarkProcRef(El,psraTypeInfo) then exit; if (FRefProcDecl<>nil) and MarkSingleProcRef(El,psraTypeInfo) then exit;
C:=El.ClassType; C:=El.ClassType;
if C=TPasUnresolvedSymbolRef then if C=TPasUnresolvedSymbolRef then
else if (C=TPasVariable) or (C=TPasConst) then else if (C=TPasVariable) or (C=TPasConst) then
UsePublished(TPasVariable(El).VarType) UseSubEl(TPasVariable(El).VarType)
else if (C=TPasArgument) then else if (C=TPasArgument) then
UsePublished(TPasArgument(El).ArgType) UseSubEl(TPasArgument(El).ArgType)
else if C=TPasProperty then else if C=TPasProperty then
begin begin
// published property // published property
Prop:=TPasProperty(El); Prop:=TPasProperty(El);
for i:=0 to Prop.Args.Count-1 do for i:=0 to Prop.Args.Count-1 do
UsePublished(TPasArgument(Prop.Args[i]).ArgType); UseSubEl(TPasArgument(Prop.Args[i]).ArgType);
UsePublished(Prop.VarType); UseSubEl(Prop.VarType);
// Note: read, write and index don't need extra typeinfo // Note: read, write and index don't need extra typeinfo
// stored and defaultvalue are only used when published -> mark as used // stored and defaultvalue are only used when published -> mark as used
@ -695,23 +755,23 @@ begin
UseElement(Prop.DefaultExpr,rraRead,false); UseElement(Prop.DefaultExpr,rraRead,false);
end end
else if (C=TPasAliasType) or (C=TPasTypeAliasType) then else if (C=TPasAliasType) or (C=TPasTypeAliasType) then
UsePublished(TPasAliasType(El).DestType) UseSubEl(TPasAliasType(El).DestType)
else if C=TPasEnumType then else if C=TPasEnumType then
else if C=TPasSetType then else if C=TPasSetType then
UsePublished(TPasSetType(El).EnumType) UseSubEl(TPasSetType(El).EnumType)
else if C=TPasRangeType then else if C=TPasRangeType then
else if C=TPasArrayType then else if C=TPasArrayType then
begin begin
UsePublished(TPasArrayType(El).ElType); UseSubEl(TPasArrayType(El).ElType);
for i:=0 to length(TPasArrayType(El).Ranges)-1 do for i:=0 to length(TPasArrayType(El).Ranges)-1 do
begin begin
Member:=TPasArrayType(El).Ranges[i]; Member:=TPasArrayType(El).Ranges[i];
Resolver.ComputeElement(Member,MemberResolved,[rcConstant]); Resolver.ComputeElement(Member,MemberResolved,[rcConstant]);
UsePublished(MemberResolved.TypeEl); UseSubEl(MemberResolved.TypeEl);
end; end;
end end
else if C=TPasPointerType then else if C=TPasPointerType then
UsePublished(TPasPointerType(El).DestType) UseSubEl(TPasPointerType(El).DestType)
else if C=TPasClassType then else if C=TPasClassType then
else if C=TPasClassOfType then else if C=TPasClassOfType then
else if C=TPasRecordType then else if C=TPasRecordType then
@ -721,19 +781,19 @@ begin
for i:=0 to Members.Count-1 do for i:=0 to Members.Count-1 do
begin begin
Member:=TPasElement(Members[i]); Member:=TPasElement(Members[i]);
UsePublished(Member); UseSubEl(Member);
UseElement(Member,rraNone,true); UseElement(Member,rraNone,true);
end; end;
end end
else if C.InheritsFrom(TPasProcedure) then else if C.InheritsFrom(TPasProcedure) then
UsePublished(TPasProcedure(El).ProcType) UseSubEl(TPasProcedure(El).ProcType)
else if C.InheritsFrom(TPasProcedureType) then else if C.InheritsFrom(TPasProcedureType) then
begin begin
ProcType:=TPasProcedureType(El); ProcType:=TPasProcedureType(El);
for i:=0 to ProcType.Args.Count-1 do for i:=0 to ProcType.Args.Count-1 do
UsePublished(TPasArgument(ProcType.Args[i]).ArgType); UseSubEl(TPasArgument(ProcType.Args[i]).ArgType);
if El is TPasFunctionType then if El is TPasFunctionType then
UsePublished(TPasFunctionType(El).ResultEl.ResultType); UseSubEl(TPasFunctionType(El).ResultEl.ResultType);
end end
else else
begin begin
@ -746,20 +806,20 @@ end;
procedure TPasAnalyzer.UseModule(aModule: TPasModule; Mode: TPAUseMode); procedure TPasAnalyzer.UseModule(aModule: TPasModule; Mode: TPAUseMode);
procedure UseInitFinal(aSection: TPasImplBlock); procedure UseInitFinal(ImplBlock: TPasImplBlock);
begin begin
if IsImplBlockEmpty(aSection) then exit; if IsImplBlockEmpty(ImplBlock) then exit;
// this module has an initialization section -> mark module // this module has an initialization section -> mark module
if FindNode(aModule)=nil then if FindNode(aModule)=nil then
Add(aModule); Add(aModule);
UseImplBlock(aSection,true); UseImplBlock(ImplBlock,true);
end; end;
var var
ModScope: TPasModuleScope; ModScope: TPasModuleScope;
begin begin
if ElementVisited(aModule,Mode) then exit; if ElementVisited(aModule,Mode) then exit;
if (FRefProcDecl<>nil) and MarkProcRef(aModule,psraRead) then exit; if (FRefProcDecl<>nil) and MarkSingleProcRef(aModule,psraRead) then exit;
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('TPasAnalyzer.UseModule ',GetElModName(aModule),' Mode=',Mode); writeln('TPasAnalyzer.UseModule ',GetElModName(aModule),' Mode=',Mode);
@ -774,6 +834,7 @@ begin
begin begin
// unit // unit
UseSection(aModule.InterfaceSection,Mode); UseSection(aModule.InterfaceSection,Mode);
// Note: implementation can not be used directly from outside
end; end;
end; end;
UseInitFinal(aModule.InitializationSection); UseInitFinal(aModule.InitializationSection);
@ -845,6 +906,7 @@ begin
writeln('TPasAnalyzer.UseSection ',Section.ClassName,' Decl=',GetElModName(Decl),' Mode=',Mode); writeln('TPasAnalyzer.UseSection ',Section.ClassName,' Decl=',GetElModName(Decl),' Mode=',Mode);
{$ENDIF} {$ENDIF}
C:=Decl.ClassType; C:=Decl.ClassType;
// Note: no MarkScopeRef needed, because all Decl are in the same scope
if C.InheritsFrom(TPasProcedure) then if C.InheritsFrom(TPasProcedure) then
begin begin
if OnlyExports and ([pmExport,pmPublic]*TPasProcedure(Decl).Modifiers=[]) then if OnlyExports and ([pmExport,pmPublic]*TPasProcedure(Decl).Modifiers=[]) then
@ -946,8 +1008,11 @@ begin
UseExpr(ForLoop.StartExpr); UseExpr(ForLoop.StartExpr);
UseExpr(ForLoop.EndExpr); UseExpr(ForLoop.EndExpr);
ForScope:=ForLoop.CustomData as TPasForLoopScope; ForScope:=ForLoop.CustomData as TPasForLoopScope;
MarkScopeRef(ForLoop,ForScope.GetEnumerator,psraRead);
UseProcedure(ForScope.GetEnumerator); UseProcedure(ForScope.GetEnumerator);
MarkScopeRef(ForLoop,ForScope.MoveNext,psraRead);
UseProcedure(ForScope.MoveNext); UseProcedure(ForScope.MoveNext);
MarkScopeRef(ForLoop,ForScope.Current,psraRead);
UseVariable(ForScope.Current,rraRead,false); UseVariable(ForScope.Current,rraRead,false);
UseImplElement(ForLoop.Body); UseImplElement(ForLoop.Body);
end end
@ -983,7 +1048,8 @@ begin
else if C=TPasImplExceptOn then else if C=TPasImplExceptOn then
begin begin
// except-on // except-on
UseType(TPasImplExceptOn(El).TypeEl,paumElement); // Note: VarEl is marked when actually used
UseElType(El,TPasImplExceptOn(El).TypeEl,paumElement);
UseImplElement(TPasImplExceptOn(El).Body); UseImplElement(TPasImplExceptOn(El).Body);
end end
else if C=TPasImplRaise then else if C=TPasImplRaise then
@ -1028,6 +1094,8 @@ var
ParamResolved: TPasResolverResult; ParamResolved: TPasResolverResult;
Decl: TPasElement; Decl: TPasElement;
ModScope: TPasModuleScope; ModScope: TPasModuleScope;
Access: TResolvedRefAccess;
SubEl: TPasElement;
begin begin
if El=nil then exit; if El=nil then exit;
// Note: expression itself is not marked, but it can reference identifiers // Note: expression itself is not marked, but it can reference identifiers
@ -1038,7 +1106,9 @@ begin
// this is a reference -> mark target // this is a reference -> mark target
Ref:=TResolvedReference(El.CustomData); Ref:=TResolvedReference(El.CustomData);
Decl:=Ref.Declaration; Decl:=Ref.Declaration;
UseElement(Decl,Ref.Access,false); Access:=Ref.Access;
MarkScopeRef(El,Decl,ResolvedToPSRefAccess[Access]);
UseElement(Decl,Access,false);
if Resolver.IsNameExpr(El) then if Resolver.IsNameExpr(El) then
begin begin
@ -1047,7 +1117,7 @@ begin
if Ref.WithExprScope.Scope is TPasRecordScope then if Ref.WithExprScope.Scope is TPasRecordScope then
begin begin
// a record member was accessed -> access the record too // a record member was accessed -> access the record too
UseExprRef(Ref.WithExprScope.Expr,Ref.Access,false); UseExprRef(El,Ref.WithExprScope.Expr,Access,false);
exit; exit;
end; end;
end; end;
@ -1058,8 +1128,8 @@ begin
if ((Decl.Parent is TPasRecordType) if ((Decl.Parent is TPasRecordType)
or (Decl.Parent is TPasVariant)) then or (Decl.Parent is TPasVariant)) then
begin begin
// a record member was accessed -> access the record too // a record member was accessed -> access the record with same Access
UseExprRef(TBinaryExpr(El.Parent).left,Ref.Access,false); UseExprRef(El.Parent,TBinaryExpr(El.Parent).left,Access,false);
end; end;
end; end;
end; end;
@ -1073,20 +1143,32 @@ begin
bfTypeInfo: bfTypeInfo:
begin begin
Params:=(El.Parent as TParamsExpr).Params; Params:=(El.Parent as TParamsExpr).Params;
if length(Params)<>1 then
RaiseNotSupported(20180226144217,El.Parent);
Resolver.ComputeElement(Params[0],ParamResolved,[rcNoImplicitProc]); Resolver.ComputeElement(Params[0],ParamResolved,[rcNoImplicitProc]);
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('TPasAnalyzer.UseExpr typeinfo ',GetResolverResultDbg(ParamResolved)); writeln('TPasAnalyzer.UseExpr typeinfo ',GetResolverResultDbg(ParamResolved));
{$ENDIF} {$ENDIF}
if ParamResolved.IdentEl is TPasFunction then if ParamResolved.IdentEl is TPasFunction then
UsePublished(TPasFunction(ParamResolved.IdentEl).FuncType.ResultEl.ResultType) begin
SubEl:=TPasFunction(ParamResolved.IdentEl).FuncType.ResultEl.ResultType;
MarkScopeRef(El,SubEl,psraTypeInfo);
UsePublished(SubEl);
end
else else
UsePublished(ParamResolved.IdentEl); begin
SubEl:=ParamResolved.IdentEl;
MarkScopeRef(El,SubEl,psraTypeInfo);
UsePublished(SubEl);
end;
// the parameter is not used otherwise
exit;
end; end;
bfAssert: bfAssert:
begin begin
ModScope:=Resolver.RootElement.CustomData as TPasModuleScope; ModScope:=Resolver.RootElement.CustomData as TPasModuleScope;
if ModScope.AssertClass<>nil then if ModScope.AssertClass<>nil then
UseType(ModScope.AssertClass,paumElement); UseElType(El,ModScope.AssertClass,paumElement);
end; end;
end; end;
@ -1128,8 +1210,8 @@ begin
RaiseNotSupported(20170307085444,El); RaiseNotSupported(20170307085444,El);
end; end;
procedure TPasAnalyzer.UseExprRef(Expr: TPasExpr; Access: TResolvedRefAccess; procedure TPasAnalyzer.UseExprRef(El: TPasElement; Expr: TPasExpr;
UseFull: boolean); Access: TResolvedRefAccess; UseFull: boolean);
var var
Ref: TResolvedReference; Ref: TResolvedReference;
C: TClass; C: TClass;
@ -1137,18 +1219,12 @@ var
Params: TParamsExpr; Params: TParamsExpr;
ValueResolved: TPasResolverResult; ValueResolved: TPasResolverResult;
begin begin
if (Expr.CustomData is TResolvedReference) then
begin
Ref:=TResolvedReference(Expr.CustomData);
UseElement(Ref.Declaration,Access,UseFull);
end;
C:=Expr.ClassType; C:=Expr.ClassType;
if C=TBinaryExpr then if C=TBinaryExpr then
begin begin
Bin:=TBinaryExpr(Expr); Bin:=TBinaryExpr(Expr);
if Bin.OpCode in [eopSubIdent,eopNone] then if Bin.OpCode in [eopSubIdent,eopNone] then
UseExprRef(Bin.right,Access,UseFull); UseExprRef(El,Bin.right,Access,UseFull);
end end
else if C=TParamsExpr then else if C=TParamsExpr then
begin begin
@ -1156,14 +1232,14 @@ begin
case Params.Kind of case Params.Kind of
pekFuncParams: pekFuncParams:
if Resolver.IsTypeCast(Params) then if Resolver.IsTypeCast(Params) then
UseExprRef(Params.Params[0],Access,UseFull) UseExprRef(El,Params.Params[0],Access,UseFull)
else else
UseExprRef(Params.Value,Access,UseFull); UseExprRef(El,Params.Value,Access,UseFull);
pekArrayParams: pekArrayParams:
begin begin
Resolver.ComputeElement(Params.Value,ValueResolved,[]); Resolver.ComputeElement(Params.Value,ValueResolved,[]);
if not Resolver.IsDynArray(ValueResolved.TypeEl) then if not Resolver.IsDynArray(ValueResolved.TypeEl) then
UseExprRef(Params.Value,Access,UseFull); UseExprRef(El,Params.Value,Access,UseFull);
end; end;
pekSet: ; pekSet: ;
else else
@ -1171,9 +1247,16 @@ begin
end; end;
end end
else if (C=TSelfExpr) or ((C=TPrimitiveExpr) and (TPrimitiveExpr(Expr).Kind=pekIdent)) then else if (C=TSelfExpr) or ((C=TPrimitiveExpr) and (TPrimitiveExpr(Expr).Kind=pekIdent)) then
// ok begin
if (Expr.CustomData is TResolvedReference) then
begin
Ref:=TResolvedReference(Expr.CustomData);
MarkScopeRef(El,Ref.Declaration,ResolvedToPSRefAccess[Access]);
UseElement(Ref.Declaration,Access,UseFull);
end;
end
else if (Access=rraRead) else if (Access=rraRead)
and ((C=TPrimitiveExpr) and ((C=TPrimitiveExpr) // Kind<>pekIdent
or (C=TNilExpr) or (C=TNilExpr)
or (C=TBoolConstExpr) or (C=TBoolConstExpr)
or (C=TUnaryExpr)) then or (C=TUnaryExpr)) then
@ -1254,7 +1337,7 @@ begin
exit; // skip implementation, Note:PasResolver always refers the declaration exit; // skip implementation, Note:PasResolver always refers the declaration
if not MarkElementAsUsed(Proc) then exit; if not MarkElementAsUsed(Proc) then exit;
if (FRefProcDecl<>nil) and MarkProcRef(Proc,psraRead) then exit; if (FRefProcDecl<>nil) and MarkSingleProcRef(Proc,psraRead) then exit;
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('TPasAnalyzer.UseProcedure ',GetElModName(Proc)); writeln('TPasAnalyzer.UseProcedure ',GetElModName(Proc));
{$ENDIF} {$ENDIF}
@ -1270,7 +1353,10 @@ begin
if FRefProcDecl=nil then if FRefProcDecl=nil then
begin begin
if Proc.IsOverride and (ProcScope.OverriddenProc<>nil) then if Proc.IsOverride and (ProcScope.OverriddenProc<>nil) then
begin
MarkScopeRef(Proc,ProcScope.OverriddenProc,psraRead);
AddOverride(ProcScope.OverriddenProc,Proc); AddOverride(ProcScope.OverriddenProc,Proc);
end;
// mark overrides // mark overrides
if [pmOverride,pmVirtual]*Proc.Modifiers<>[] then if [pmOverride,pmVirtual]*Proc.Modifiers<>[] then
@ -1294,11 +1380,11 @@ begin
Arg:=TPasArgument(ProcType.Args[i]); Arg:=TPasArgument(ProcType.Args[i]);
// Note: the arguments themselves are marked when used in code // Note: the arguments themselves are marked when used in code
// mark argument type and default value // mark argument type and default value
UseType(Arg.ArgType,paumElement); UseElType(ProcType,Arg.ArgType,paumElement);
UseExpr(Arg.ValueExpr); UseExpr(Arg.ValueExpr);
end; end;
if ProcType is TPasFunctionType then if ProcType is TPasFunctionType then
UseType(TPasFunctionType(ProcType).ResultEl.ResultType,paumElement); UseElType(ProcType,TPasFunctionType(ProcType).ResultEl.ResultType,paumElement);
end; end;
procedure TPasAnalyzer.UseType(El: TPasType; Mode: TPAUseMode); procedure TPasAnalyzer.UseType(El: TPasType; Mode: TPAUseMode);
@ -1307,7 +1393,7 @@ var
i: Integer; i: Integer;
begin begin
if El=nil then exit; if El=nil then exit;
if (FRefProcDecl<>nil) and MarkProcRef(El,PAUseModeToPSRefAccess[Mode]) then exit; if (FRefProcDecl<>nil) and MarkSingleProcRef(El,PAUseModeToPSRefAccess[Mode]) then exit;
C:=El.ClassType; C:=El.ClassType;
if Mode=paumAllExports then if Mode=paumAllExports then
@ -1337,14 +1423,14 @@ begin
or (C=TPasClassOfType) then or (C=TPasClassOfType) then
begin begin
if not MarkElementAsUsed(El) then exit; if not MarkElementAsUsed(El) then exit;
UseType(TPasAliasType(El).DestType,Mode); UseElType(El,TPasAliasType(El).DestType,Mode);
end end
else if C=TPasArrayType then else if C=TPasArrayType then
begin begin
if not MarkElementAsUsed(El) then exit; if not MarkElementAsUsed(El) then exit;
for i:=0 to length(TPasArrayType(El).Ranges)-1 do for i:=0 to length(TPasArrayType(El).Ranges)-1 do
UseExpr(TPasArrayType(El).Ranges[i]); UseExpr(TPasArrayType(El).Ranges[i]);
UseType(TPasArrayType(El).ElType,Mode); UseElType(El,TPasArrayType(El).ElType,Mode);
end end
else if C=TPasRecordType then else if C=TPasRecordType then
UseRecordType(TPasRecordType(El),Mode) UseRecordType(TPasRecordType(El),Mode)
@ -1359,7 +1445,7 @@ begin
else if C=TPasPointerType then else if C=TPasPointerType then
begin begin
if not MarkElementAsUsed(El) then exit; if not MarkElementAsUsed(El) then exit;
UseType(TPasPointerType(El).DestType,Mode); UseElType(El,TPasPointerType(El).DestType,Mode);
end end
else if C=TPasRangeType then else if C=TPasRangeType then
begin begin
@ -1369,7 +1455,7 @@ begin
else if C=TPasSetType then else if C=TPasSetType then
begin begin
if not MarkElementAsUsed(El) then exit; if not MarkElementAsUsed(El) then exit;
UseType(TPasSetType(El).EnumType,Mode); UseElType(El,TPasSetType(El).EnumType,Mode);
end end
else if C.InheritsFrom(TPasProcedureType) then else if C.InheritsFrom(TPasProcedureType) then
UseProcedureType(TPasProcedureType(El),true) UseProcedureType(TPasProcedureType(El),true)
@ -1434,11 +1520,11 @@ begin
ClassScope:=El.CustomData as TPasClassScope; ClassScope:=El.CustomData as TPasClassScope;
if FirstTime then if FirstTime then
begin begin
UseType(ClassScope.DirectAncestor,paumElement); UseElType(El,ClassScope.DirectAncestor,paumElement);
UseType(El.HelperForType,paumElement); UseElType(El,El.HelperForType,paumElement);
UseExpr(El.GUIDExpr); UseExpr(El.GUIDExpr);
for i:=0 to El.Interfaces.Count-1 do for i:=0 to El.Interfaces.Count-1 do
UseType(TPasType(El.Interfaces[i]),paumElement); UseElType(El,TPasType(El.Interfaces[i]),paumElement);
end; end;
// members // members
AllPublished:=(Mode<>paumAllExports); AllPublished:=(Mode<>paumAllExports);
@ -1454,7 +1540,7 @@ begin
AddOverride(ProcScope.OverriddenProc,Member); AddOverride(ProcScope.OverriddenProc,Member);
if ScopeModule<>nil then if ScopeModule<>nil then
begin begin
// when analyzingf a single module, all overrides are assumed to be called // when analyzing a single module, all overrides are assumed to be called
UseElement(Member,rraNone,true); UseElement(Member,rraNone,true);
continue; continue;
end; end;
@ -1514,7 +1600,7 @@ begin
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('TPasAnalyzer.UseVariable ',GetElModName(El),' ',Access,' Full=',UseFull); writeln('TPasAnalyzer.UseVariable ',GetElModName(El),' ',Access,' Full=',UseFull);
{$ENDIF} {$ENDIF}
if (FRefProcDecl<>nil) and MarkProcRef(El,ResolvedToPSRefAccess[Access]) then exit; if (FRefProcDecl<>nil) and MarkSingleProcRef(El,ResolvedToPSRefAccess[Access]) then exit;
if El.ClassType=TPasProperty then if El.ClassType=TPasProperty then
Prop:=TPasProperty(El) Prop:=TPasProperty(El)
@ -1566,7 +1652,7 @@ begin
Usage.Access:=paiaWrite; Usage.Access:=paiaWrite;
UpdateVarAccess(IsRead,IsWrite); UpdateVarAccess(IsRead,IsWrite);
// then use recursively // then use recursively
UseType(El.VarType,paumElement); UseElType(El,El.VarType,paumElement);
UseExpr(El.Expr); UseExpr(El.Expr);
UseExpr(El.LibraryName); UseExpr(El.LibraryName);
UseExpr(El.ExportName); UseExpr(El.ExportName);
@ -1574,7 +1660,7 @@ begin
if Prop<>nil then if Prop<>nil then
begin begin
for i:=0 to Prop.Args.Count-1 do for i:=0 to Prop.Args.Count-1 do
UseType(TPasArgument(Prop.Args[i]).ArgType,paumElement); UseElType(Prop,TPasArgument(Prop.Args[i]).ArgType,paumElement);
UseExpr(Prop.IndexExpr); UseExpr(Prop.IndexExpr);
UseExpr(Prop.ImplementsFunc); UseExpr(Prop.ImplementsFunc);
// ToDo: UseExpr(Prop.DispIDExpr); // ToDo: UseExpr(Prop.DispIDExpr);
@ -2186,6 +2272,11 @@ end;
procedure TPasAnalyzer.EmitMessage(Msg: TPAMessage); procedure TPasAnalyzer.EmitMessage(Msg: TPAMessage);
begin begin
if FRefProcDecl<>nil then exit; if FRefProcDecl<>nil then exit;
if not Assigned(OnMessage) then
begin
Msg.Release;
exit;
end;
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('TPasAnalyzer.EmitMessage [',Msg.Id,'] ',Msg.MsgType,': (',Msg.MsgNumber,') ',Msg.MsgText); writeln('TPasAnalyzer.EmitMessage [',Msg.Id,'] ',Msg.MsgType,': (',Msg.MsgNumber,') ',Msg.MsgText);
{$ENDIF} {$ENDIF}

View File

@ -21,6 +21,7 @@ type
FAnalyzer: TPasAnalyzer; FAnalyzer: TPasAnalyzer;
FPAMessages: TFPList; // list of TPAMessage FPAMessages: TFPList; // list of TPAMessage
FPAGoodMessages: TFPList; FPAGoodMessages: TFPList;
FProcAnalyzer: TPasAnalyzer;
function GetPAMessages(Index: integer): TPAMessage; function GetPAMessages(Index: integer): TPAMessage;
procedure OnAnalyzerMessage(Sender: TObject; Msg: TPAMessage); procedure OnAnalyzerMessage(Sender: TObject; Msg: TPAMessage);
protected protected
@ -39,6 +40,7 @@ type
const RefNames: array of string); const RefNames: array of string);
public public
property Analyzer: TPasAnalyzer read FAnalyzer; property Analyzer: TPasAnalyzer read FAnalyzer;
property ProcAnalyzer: TPasAnalyzer read FProcAnalyzer;
function PAMessageCount: integer; function PAMessageCount: integer;
property PAMessages[Index: integer]: TPAMessage read GetPAMessages; property PAMessages[Index: integer]: TPAMessage read GetPAMessages;
end; end;
@ -185,6 +187,7 @@ begin
TPAMessage(FPAMessages[i]).Release; TPAMessage(FPAMessages[i]).Release;
FreeAndNil(FPAMessages); FreeAndNil(FPAMessages);
FreeAndNil(FAnalyzer); FreeAndNil(FAnalyzer);
FreeAndNil(FProcAnalyzer);
inherited TearDown; inherited TearDown;
end; end;
@ -353,7 +356,7 @@ type
var var
Entries: array of TEntry; Entries: array of TEntry;
procedure CheckRefs(Scope: TPasProcedureScope); procedure CheckRefs(Scope: TPasProcedureScope; const Prefix: string);
procedure DumpRefsAndFail(Refs: TFPList; const Msg: string); procedure DumpRefsAndFail(Refs: TFPList; const Msg: string);
var var
@ -365,10 +368,10 @@ var
Ref:=TPasProcScopeReference(Refs[i]); Ref:=TPasProcScopeReference(Refs[i]);
if Ref=nil then break; if Ref=nil then break;
{$IFDEF VerbosePasAnalyzer} {$IFDEF VerbosePasAnalyzer}
writeln('DumpRefsAndFail ',i,' ',GetObjName(Ref.Element),' ',Ref.Access); writeln('DumpRefsAndFail ',Prefix,' ',i,' ',GetObjName(Ref.Element),' ',Ref.Access);
{$ENDIF} {$ENDIF}
end; end;
Fail(Msg); Fail(Prefix+': '+Msg);
end; end;
var var
@ -384,7 +387,7 @@ var
begin begin
o:=TObject(Refs[i]); o:=TObject(Refs[i]);
if not (o is TPasProcScopeReference) then if not (o is TPasProcScopeReference) then
Fail('Refs['+IntToStr(i)+'] '+GetObjName(o)); Fail(Prefix+': Refs['+IntToStr(i)+'] '+GetObjName(o));
end; end;
// check that all Entries are referenced // check that all Entries are referenced
for i:=0 to length(Entries)-1 do for i:=0 to length(Entries)-1 do
@ -422,6 +425,7 @@ var
El: TPasElement; El: TPasElement;
Proc: TPasProcedure; Proc: TPasProcedure;
Scope: TPasProcedureScope; Scope: TPasProcedureScope;
ProcAnalyzer: TPasAnalyzer;
begin begin
for i:=0 to Section.Declarations.Count-1 do for i:=0 to Section.Declarations.Count-1 do
begin begin
@ -432,9 +436,21 @@ var
Proc:=TPasProcedure(El); Proc:=TPasProcedure(El);
Scope:=Proc.CustomData as TPasProcedureScope; Scope:=Proc.CustomData as TPasProcedureScope;
if Scope.DeclarationProc<>nil then continue; if Scope.DeclarationProc<>nil then continue;
Analyzer.Clear;
Analyzer.AnalyzeProcRefs(Proc); // check references created by AnalyzeModule
CheckRefs(Scope); CheckRefs(Scope,'AnalyzeModule');
// check references created by AnalyzeProcRefs
Scope.ClearReferences;
if FProcAnalyzer=nil then
begin
ProcAnalyzer:=TPasAnalyzer.Create;
ProcAnalyzer.Resolver:=ResolverEngine;
end;
ProcAnalyzer.Clear;
ProcAnalyzer.AnalyzeProcRefs(Proc);
CheckRefs(Scope,'AnalyzeProcRefs');
exit(true); exit(true);
end; end;
Result:=false; Result:=false;
@ -443,8 +459,6 @@ var
var var
i: Integer; i: Integer;
begin begin
ParseUnit;
SetLength(Entries,High(RefNames)-low(RefNames)+1); SetLength(Entries,High(RefNames)-low(RefNames)+1);
for i:=low(RefNames) to high(RefNames) do for i:=low(RefNames) to high(RefNames) do
begin begin
@ -1750,6 +1764,7 @@ begin
'end;', 'end;',
'begin', 'begin',
' DoIt(nil);']); ' DoIt(nil);']);
AnalyzeProgram;
CheckUseAnalyzerUnexpectedHints; CheckUseAnalyzerUnexpectedHints;
end; end;
@ -2252,6 +2267,8 @@ begin
' b:=i;', ' b:=i;',
'end;', 'end;',
'']); '']);
Analyzer.Options:=Analyzer.Options+[paoProcReferences];
AnalyzeUnit;
CheckUnitProcedureReferences('DoIt',['i','tintcolor']); CheckUnitProcedureReferences('DoIt',['i','tintcolor']);
end; end;

View File

@ -24,7 +24,7 @@ interface
uses uses
Classes, SysUtils, fpcunit, testregistry, Classes, SysUtils, fpcunit, testregistry,
PasTree, PScanner, PasResolver, PasResolveEval, PParser, PasTree, PScanner, PasResolver, PasResolveEval, PParser, PasUseAnalyzer,
FPPas2Js, Pas2JsFiler, FPPas2Js, Pas2JsFiler,
tcmodules; tcmodules;
@ -34,6 +34,7 @@ type
TCustomTestPrecompile = Class(TCustomTestModule) TCustomTestPrecompile = Class(TCustomTestModule)
private private
FAnalyzer: TPasAnalyzer;
FInitialFlags: TPJUInitialFlags; FInitialFlags: TPJUInitialFlags;
FPJUReader: TPJUReader; FPJUReader: TPJUReader;
FPJUWriter: TPJUWriter; FPJUWriter: TPJUWriter;
@ -42,6 +43,7 @@ type
protected protected
procedure SetUp; override; procedure SetUp; override;
procedure TearDown; override; procedure TearDown; override;
procedure ConvertModule; override;
procedure WriteReadUnit; virtual; procedure WriteReadUnit; virtual;
procedure StartParsing; override; procedure StartParsing; override;
function CheckRestoredObject(const Path: string; Orig, Rest: TObject): boolean; virtual; function CheckRestoredObject(const Path: string; Orig, Rest: TObject): boolean; virtual;
@ -106,6 +108,7 @@ type
procedure CheckRestoredProcedure(const Path: string; Orig, Rest: TPasProcedure); virtual; procedure CheckRestoredProcedure(const Path: string; Orig, Rest: TPasProcedure); virtual;
procedure CheckRestoredOperator(const Path: string; Orig, Rest: TPasOperator); virtual; procedure CheckRestoredOperator(const Path: string; Orig, Rest: TPasOperator); virtual;
public public
property Analyzer: TPasAnalyzer read FAnalyzer;
property PJUWriter: TPJUWriter read FPJUWriter write FPJUWriter; property PJUWriter: TPJUWriter read FPJUWriter write FPJUWriter;
property PJUReader: TPJUReader read FPJUReader write FPJUReader; property PJUReader: TPJUReader read FPJUReader write FPJUReader;
property InitialFlags: TPJUInitialFlags read FInitialFlags; property InitialFlags: TPJUInitialFlags read FInitialFlags;
@ -165,16 +168,25 @@ procedure TCustomTestPrecompile.SetUp;
begin begin
inherited SetUp; inherited SetUp;
FInitialFlags:=TPJUInitialFlags.Create; FInitialFlags:=TPJUInitialFlags.Create;
FAnalyzer:=TPasAnalyzer.Create;
Analyzer.Resolver:=Engine;
end; end;
procedure TCustomTestPrecompile.TearDown; procedure TCustomTestPrecompile.TearDown;
begin begin
FreeAndNil(FAnalyzer);
FreeAndNil(FPJUWriter); FreeAndNil(FPJUWriter);
FreeAndNil(FPJUReader); FreeAndNil(FPJUReader);
FreeAndNil(FInitialFlags); FreeAndNil(FInitialFlags);
inherited TearDown; inherited TearDown;
end; end;
procedure TCustomTestPrecompile.ConvertModule;
begin
Analyzer.AnalyzeModule(Module);
inherited ConvertModule;
end;
procedure TCustomTestPrecompile.WriteReadUnit; procedure TCustomTestPrecompile.WriteReadUnit;
var var
ms: TMemoryStream; ms: TMemoryStream;