pasuseanalyzer.pas 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979
  1. {
  2. This file is part of the Free Component Library
  3. Pascal parse tree classes
  4. Copyright (c) 2017 Mattias Gaertner, [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {
  12. Abstract:
  13. After running TPasResolver, run this to
  14. - create a list of used declararion, either in a module or a whole program.
  15. - emit hints about unused declarations
  16. - and warnings about uninitialized variables.
  17. Working:
  18. - mark used elements of a module, starting from all accessible elements
  19. - Hint: 'Unit "%s" not used in %s'
  20. - Hint: 'Parameter "%s" not used'
  21. - Hint: 'Local variable "%s" not used'
  22. - Hint: 'Value parameter "%s" is assigned but never used'
  23. - Hint: 'Local variable "%s" is assigned but never used'
  24. - Hint: 'Local %s "%s" not used'
  25. - Hint: 'Private field "%s" is never used'
  26. - Hint: 'Private field "%s" is assigned but never used'
  27. - Hint: 'Private method "%s" is never used'
  28. - Hint: 'Private type "%s" never used'
  29. - Hint: 'Private const "%s" never used'
  30. - Hint: 'Private property "%s" never used'
  31. - Hint: 'Function result does not seem to be set'
  32. ToDo:
  33. - record members
  34. - class members
  35. - Improve Call Override: e.g. A.Proc, mark only overrides of descendants of A
  36. - TPasArgument: compute the effective Access
  37. - calls: use the effective Access of arguments
  38. }
  39. unit PasUseAnalyzer;
  40. {$mode objfpc}{$H+}{$inline on}
  41. interface
  42. uses
  43. Classes, SysUtils, AVL_Tree, PasTree, PScanner,
  44. {$IFDEF VerbosePasAnalyzer}
  45. PasResolveEval,
  46. {$ENDIF}
  47. PasResolver;
  48. const
  49. nPAUnitNotUsed = 5023;
  50. sPAUnitNotUsed = 'Unit "%s" not used in %s';
  51. nPAParameterNotUsed = 5024;
  52. sPAParameterNotUsed = 'Parameter "%s" not used';
  53. nPALocalVariableNotUsed = 5025;
  54. sPALocalVariableNotUsed = 'Local variable "%s" not used';
  55. nPAValueParameterIsAssignedButNeverUsed = 5026;
  56. sPAValueParameterIsAssignedButNeverUsed = 'Value parameter "%s" is assigned but never used';
  57. nPALocalVariableIsAssignedButNeverUsed = 5027;
  58. sPALocalVariableIsAssignedButNeverUsed = 'Local variable "%s" is assigned but never used';
  59. nPALocalXYNotUsed = 5028;
  60. sPALocalXYNotUsed = 'Local %s "%s" not used';
  61. nPAPrivateFieldIsNeverUsed = 5029;
  62. sPAPrivateFieldIsNeverUsed = 'Private field "%s" is never used';
  63. nPAPrivateFieldIsAssignedButNeverUsed = 5030;
  64. sPAPrivateFieldIsAssignedButNeverUsed = 'Private field "%s" is assigned but never used';
  65. nPAPrivateMethodIsNeverUsed = 5031;
  66. sPAPrivateMethodIsNeverUsed = 'Private method "%s" is never used';
  67. nPAFunctionResultDoesNotSeemToBeSet = 5033;
  68. sPAFunctionResultDoesNotSeemToBeSet = 'Function result does not seem to be set';
  69. nPAPrivateTypeXNeverUsed = 5071;
  70. sPAPrivateTypeXNeverUsed = 'Private type "%s" never used';
  71. nPAPrivateConstXNeverUsed = 5072;
  72. sPAPrivateConstXNeverUsed = 'Private const "%s" never used';
  73. nPAPrivatePropertyXNeverUsed = 5073;
  74. sPAPrivatePropertyXNeverUsed = 'Private property "%s" never used';
  75. //nPAUnreachableCode = 6018;
  76. //sPAUnreachableCode = 'unreachable code';
  77. type
  78. EPasAnalyzer = class(EPasResolve);
  79. { TPAMessage }
  80. TPAMessage = class
  81. private
  82. FRefCount: integer;
  83. public
  84. Id: int64;
  85. MsgType: TMessageType;
  86. MsgNumber: integer;
  87. MsgText: string;
  88. MsgPattern: String;
  89. Args: TMessageArgs;
  90. PosEl: TPasElement;
  91. Filename: string;
  92. Row, Col: integer;
  93. constructor Create;
  94. procedure AddRef;
  95. procedure Release;
  96. property RefCount: integer read FRefCount;
  97. end;
  98. TPAMessageEvent = procedure(Sender: TObject; Msg: TPAMessage) of object;
  99. TPAIdentifierAccess = (
  100. paiaNone,
  101. paiaRead,
  102. paiaWrite,
  103. paiaReadWrite,
  104. paiaWriteRead
  105. );
  106. { TPAElement }
  107. TPAElement = class
  108. private
  109. FElement: TPasElement;
  110. procedure SetElement(AValue: TPasElement);
  111. public
  112. Access: TPAIdentifierAccess;
  113. destructor Destroy; override;
  114. property Element: TPasElement read FElement write SetElement;
  115. end;
  116. TPAElementClass = class of TPAElement;
  117. { TPAOverrideList }
  118. TPAOverrideList = class
  119. private
  120. FElement: TPasElement;
  121. FOverrides: TFPList; // list of TPasElement
  122. function GetOverrides(Index: integer): TPasElement; inline;
  123. procedure SetElement(AValue: TPasElement);
  124. public
  125. constructor Create;
  126. destructor Destroy; override;
  127. procedure Add(OverrideEl: TPasElement);
  128. property Element: TPasElement read FElement write SetElement;
  129. function Count: integer;
  130. function IndexOf(OverrideEl: TPasElement): integer; inline;
  131. property Overrides[Index: integer]: TPasElement read GetOverrides; default;
  132. end;
  133. TPasAnalyzerOption = (
  134. paoOnlyExports // default: use all class members accessible from outside (protected, but not private)
  135. );
  136. TPasAnalyzerOptions = set of TPasAnalyzerOption;
  137. TPAUseMode = (
  138. paumElement, // Mark element. Do not descend into children.
  139. paumAllPublic, // Mark element and descend into children and mark public identifiers
  140. paumAllExports, // Do not mark element. Descend into children and mark exports.
  141. paumPublished // Mark element and its type and descend into children and mark published identifiers
  142. );
  143. TPAUseModes = set of TPAUseMode;
  144. { TPasAnalyzer }
  145. TPasAnalyzer = class
  146. private
  147. FChecked: array[TPAUseMode] of TAVLTree; // tree of TElement
  148. FOnMessage: TPAMessageEvent;
  149. FOptions: TPasAnalyzerOptions;
  150. FOverrideLists: TAVLTree; // tree of TPAOverrideList sorted for Element
  151. FResolver: TPasResolver;
  152. FScopeModule: TPasModule;
  153. FUsedElements: TAVLTree; // tree of TPAElement sorted for Element
  154. function AddOverride(OverriddenEl, OverrideEl: TPasElement): boolean;
  155. function FindOverrideNode(El: TPasElement): TAVLTreeNode;
  156. function FindOverrideList(El: TPasElement): TPAOverrideList;
  157. procedure SetOptions(AValue: TPasAnalyzerOptions);
  158. procedure UpdateAccess(IsWrite: Boolean; IsRead: Boolean; Usage: TPAElement);
  159. protected
  160. procedure RaiseInconsistency(const Id: int64; Msg: string);
  161. procedure RaiseNotSupported(const Id: int64; El: TPasElement; const Msg: string = '');
  162. // mark used elements
  163. function Add(El: TPasElement; CheckDuplicate: boolean = true;
  164. aClass: TPAElementClass = nil): TPAElement;
  165. function FindNode(El: TPasElement): TAVLTreeNode; inline;
  166. function FindPAElement(El: TPasElement): TPAElement; inline;
  167. procedure CreateTree; virtual;
  168. function MarkElementAsUsed(El: TPasElement; aClass: TPAElementClass = nil): boolean; // true if new
  169. function ElementVisited(El: TPasElement; Mode: TPAUseMode): boolean;
  170. procedure UseElement(El: TPasElement; Access: TResolvedRefAccess;
  171. UseFull: boolean); virtual;
  172. procedure UsePublished(El: TPasElement); virtual;
  173. procedure UseModule(aModule: TPasModule; Mode: TPAUseMode); virtual;
  174. procedure UseSection(Section: TPasSection; Mode: TPAUseMode); virtual;
  175. procedure UseImplBlock(Block: TPasImplBlock; Mark: boolean); virtual;
  176. procedure UseImplElement(El: TPasImplElement); virtual;
  177. procedure UseExpr(El: TPasExpr); virtual;
  178. procedure UseExprRef(Expr: TPasExpr; Access: TResolvedRefAccess;
  179. UseFull: boolean); virtual;
  180. procedure UseProcedure(Proc: TPasProcedure); virtual;
  181. procedure UseProcedureType(ProcType: TPasProcedureType; Mark: boolean); virtual;
  182. procedure UseType(El: TPasType; Mode: TPAUseMode); virtual;
  183. procedure UseRecordType(El: TPasRecordType; Mode: TPAUseMode); virtual;
  184. procedure UseClassType(El: TPasClassType; Mode: TPAUseMode); virtual;
  185. procedure UseVariable(El: TPasVariable; Access: TResolvedRefAccess;
  186. UseFull: boolean); virtual;
  187. procedure UseArgument(El: TPasArgument; Access: TResolvedRefAccess); virtual;
  188. procedure UseResultElement(El: TPasResultElement; Access: TResolvedRefAccess); virtual;
  189. // create hints for a unit, program or library
  190. procedure EmitElementHints(El: TPasElement); virtual;
  191. procedure EmitSectionHints(Section: TPasSection); virtual;
  192. procedure EmitDeclarationsHints(El: TPasDeclarations); virtual;
  193. procedure EmitTypeHints(El: TPasType); virtual;
  194. procedure EmitVariableHints(El: TPasVariable); virtual;
  195. procedure EmitProcedureHints(El: TPasProcedure); virtual;
  196. public
  197. constructor Create;
  198. destructor Destroy; override;
  199. procedure Clear;
  200. procedure AnalyzeModule(aModule: TPasModule);
  201. procedure AnalyzeWholeProgram(aStartModule: TPasProgram);
  202. procedure EmitModuleHints(aModule: TPasModule); virtual;
  203. function FindElement(El: TPasElement): TPAElement;
  204. // utility
  205. function IsUsed(El: TPasElement): boolean; // valid after calling Analyze*
  206. function IsTypeInfoUsed(El: TPasElement): boolean; // valid after calling Analyze*
  207. function IsModuleInternal(El: TPasElement): boolean;
  208. function IsExport(El: TPasElement): boolean;
  209. function IsIdentifier(El: TPasElement): boolean;
  210. function IsImplBlockEmpty(El: TPasImplBlock): boolean;
  211. procedure EmitMessage(const Id: int64; const MsgType: TMessageType;
  212. MsgNumber: integer; Fmt: String; const Args: array of const; PosEl: TPasElement);
  213. procedure EmitMessage(Msg: TPAMessage);
  214. property OnMessage: TPAMessageEvent read FOnMessage write FOnMessage;
  215. property Options: TPasAnalyzerOptions read FOptions write SetOptions;
  216. property Resolver: TPasResolver read FResolver write FResolver;
  217. property ScopeModule: TPasModule read FScopeModule write FScopeModule;
  218. end;
  219. function ComparePAElements(Identifier1, Identifier2: Pointer): integer;
  220. function CompareElementWithPAElement(El, Id: Pointer): integer;
  221. function ComparePAOverrideLists(List1, List2: Pointer): integer;
  222. function CompareElementWithPAOverrideList(El, List: Pointer): integer;
  223. function GetElModName(El: TPasElement): string;
  224. implementation
  225. function ComparePointer(Data1, Data2: Pointer): integer;
  226. begin
  227. if Data1>Data2 then Result:=-1
  228. else if Data1<Data2 then Result:=1
  229. else Result:=0;
  230. end;
  231. function ComparePAElements(Identifier1, Identifier2: Pointer): integer;
  232. var
  233. Item1: TPAElement absolute Identifier1;
  234. Item2: TPAElement absolute Identifier2;
  235. begin
  236. Result:=ComparePointer(Item1.Element,Item2.Element);
  237. end;
  238. function CompareElementWithPAElement(El, Id: Pointer): integer;
  239. var
  240. Identifier: TPAElement absolute Id;
  241. begin
  242. Result:=ComparePointer(El,Identifier.Element);
  243. end;
  244. function ComparePAOverrideLists(List1, List2: Pointer): integer;
  245. var
  246. Item1: TPAOverrideList absolute List1;
  247. Item2: TPAOverrideList absolute List2;
  248. begin
  249. Result:=ComparePointer(Item1.Element,Item2.Element);
  250. end;
  251. function CompareElementWithPAOverrideList(El, List: Pointer): integer;
  252. var
  253. OvList: TPAOverrideList absolute List;
  254. begin
  255. Result:=ComparePointer(El,OvList.Element);
  256. end;
  257. function GetElModName(El: TPasElement): string;
  258. var
  259. aModule: TPasModule;
  260. begin
  261. if El=nil then exit('nil');
  262. Result:=El.Name+':'+El.ClassName;
  263. aModule:=El.GetModule;
  264. if aModule=El then exit;
  265. if aModule=nil then
  266. Result:='NilModule.'+Result
  267. else
  268. Result:=aModule.Name+'.'+Result;
  269. end;
  270. { TPAMessage }
  271. constructor TPAMessage.Create;
  272. begin
  273. FRefCount:=1;
  274. end;
  275. procedure TPAMessage.AddRef;
  276. begin
  277. inc(FRefCount);
  278. end;
  279. procedure TPAMessage.Release;
  280. begin
  281. if FRefCount=0 then
  282. raise Exception.Create('');
  283. dec(FRefCount);
  284. if FRefCount=0 then
  285. Free;
  286. end;
  287. { TPAOverrideList }
  288. // inline
  289. function TPAOverrideList.GetOverrides(Index: integer): TPasElement;
  290. begin
  291. Result:=TPasElement(FOverrides[Index]);
  292. end;
  293. // inline
  294. function TPAOverrideList.IndexOf(OverrideEl: TPasElement): integer;
  295. begin
  296. Result:=FOverrides.IndexOf(OverrideEl);
  297. end;
  298. procedure TPAOverrideList.SetElement(AValue: TPasElement);
  299. begin
  300. if FElement=AValue then Exit;
  301. if FElement<>nil then
  302. FElement.Release;
  303. FElement:=AValue;
  304. if FElement<>nil then
  305. FElement.AddRef;
  306. end;
  307. constructor TPAOverrideList.Create;
  308. begin
  309. FOverrides:=TFPList.Create;
  310. end;
  311. destructor TPAOverrideList.Destroy;
  312. var
  313. i: Integer;
  314. begin
  315. for i:=0 to FOverrides.Count-1 do
  316. TPasElement(FOverrides[i]).Release;
  317. FreeAndNil(FOverrides);
  318. inherited Destroy;
  319. end;
  320. procedure TPAOverrideList.Add(OverrideEl: TPasElement);
  321. begin
  322. FOverrides.Add(OverrideEl);
  323. OverrideEl.AddRef;
  324. end;
  325. function TPAOverrideList.Count: integer;
  326. begin
  327. Result:=FOverrides.Count;
  328. end;
  329. { TPAElement }
  330. procedure TPAElement.SetElement(AValue: TPasElement);
  331. begin
  332. if FElement=AValue then Exit;
  333. if FElement<>nil then
  334. FElement.Release;
  335. FElement:=AValue;
  336. if FElement<>nil then
  337. FElement.AddRef;
  338. end;
  339. destructor TPAElement.Destroy;
  340. begin
  341. Element:=nil;
  342. inherited Destroy;
  343. end;
  344. { TPasAnalyzer }
  345. // inline
  346. function TPasAnalyzer.FindNode(El: TPasElement): TAVLTreeNode;
  347. begin
  348. Result:=FUsedElements.FindKey(El,@CompareElementWithPAElement);
  349. end;
  350. // inline
  351. function TPasAnalyzer.FindPAElement(El: TPasElement): TPAElement;
  352. var
  353. Node: TAVLTreeNode;
  354. begin
  355. Node:=FindNode(El);
  356. if Node=nil then
  357. Result:=nil
  358. else
  359. Result:=TPAElement(Node.Data);
  360. end;
  361. procedure TPasAnalyzer.SetOptions(AValue: TPasAnalyzerOptions);
  362. begin
  363. if FOptions=AValue then Exit;
  364. FOptions:=AValue;
  365. end;
  366. function TPasAnalyzer.FindOverrideNode(El: TPasElement): TAVLTreeNode;
  367. begin
  368. Result:=FOverrideLists.FindKey(El,@CompareElementWithPAOverrideList);
  369. end;
  370. function TPasAnalyzer.FindOverrideList(El: TPasElement): TPAOverrideList;
  371. var
  372. Node: TAVLTreeNode;
  373. begin
  374. Node:=FindOverrideNode(El);
  375. if Node=nil then
  376. Result:=nil
  377. else
  378. Result:=TPAOverrideList(Node.Data);
  379. end;
  380. function TPasAnalyzer.AddOverride(OverriddenEl, OverrideEl: TPasElement): boolean;
  381. // OverrideEl overrides OverriddenEl
  382. // returns true if new override
  383. var
  384. Node: TAVLTreeNode;
  385. Item: TPAOverrideList;
  386. OverriddenPAEl: TPAElement;
  387. begin
  388. {$IFDEF VerbosePasAnalyzer}
  389. writeln('TPasAnalyzer.AddOverride OverriddenEl=',GetElModName(OverriddenEl),' OverrideEl=',GetElModName(OverrideEl));
  390. {$ENDIF}
  391. Node:=FindOverrideNode(OverriddenEl);
  392. if Node=nil then
  393. begin
  394. Item:=TPAOverrideList.Create;
  395. Item.Element:=OverriddenEl;
  396. FOverrideLists.Add(Item);
  397. end
  398. else
  399. begin
  400. Item:=TPAOverrideList(Node.Data);
  401. if Item.IndexOf(OverrideEl)>=0 then
  402. exit(false);
  403. end;
  404. // new override
  405. Item.Add(OverrideEl);
  406. Result:=true;
  407. OverriddenPAEl:=FindPAElement(OverriddenEl);
  408. if OverriddenPAEl<>nil then
  409. UseElement(OverrideEl,rraNone,true);
  410. end;
  411. procedure TPasAnalyzer.UpdateAccess(IsWrite: Boolean; IsRead: Boolean;
  412. Usage: TPAElement);
  413. begin
  414. if IsRead then
  415. case Usage.Access of
  416. paiaNone: Usage.Access:=paiaRead;
  417. paiaRead: ;
  418. paiaWrite: Usage.Access:=paiaWriteRead;
  419. paiaReadWrite: ;
  420. paiaWriteRead: ;
  421. else RaiseInconsistency(20170311183122, '');
  422. end;
  423. if IsWrite then
  424. case Usage.Access of
  425. paiaNone: Usage.Access:=paiaWrite;
  426. paiaRead: Usage.Access:=paiaReadWrite;
  427. paiaWrite: ;
  428. paiaReadWrite: ;
  429. paiaWriteRead: ;
  430. else RaiseInconsistency(20170311183127, '');
  431. end;
  432. end;
  433. procedure TPasAnalyzer.RaiseInconsistency(const Id: int64; Msg: string);
  434. begin
  435. raise EPasAnalyzer.Create('['+IntToStr(Id)+']: '+Msg);
  436. end;
  437. procedure TPasAnalyzer.RaiseNotSupported(const Id: int64; El: TPasElement;
  438. const Msg: string);
  439. var
  440. s: String;
  441. E: EPasAnalyzer;
  442. begin
  443. s:='['+IntToStr(Id)+']: Element='+GetElModName(El);
  444. if Msg<>'' then S:=S+' '+Msg;
  445. E:=EPasAnalyzer.Create(s);
  446. E.PasElement:=El;
  447. {$IFDEF VerbosePasAnalyzer}
  448. writeln('TPasAnalyzer.RaiseNotSupported ',E.Message);
  449. {$ENDIF}
  450. raise E;
  451. end;
  452. function TPasAnalyzer.Add(El: TPasElement; CheckDuplicate: boolean;
  453. aClass: TPAElementClass): TPAElement;
  454. begin
  455. if El=nil then
  456. RaiseInconsistency(20170308093407,'');
  457. {$IFDEF VerbosePasAnalyzer}
  458. writeln('TPasAnalyzer.Add ',GetElModName(El),' New=',FindNode(El)=nil);
  459. {$ENDIF}
  460. if CheckDuplicate and (FindNode(El)<>nil) then
  461. RaiseInconsistency(20170304201318,'');
  462. if aClass=nil then
  463. aClass:=TPAElement;
  464. Result:=aClass.Create;
  465. Result.Element:=El;
  466. FUsedElements.Add(Result);
  467. end;
  468. procedure TPasAnalyzer.CreateTree;
  469. begin
  470. FUsedElements:=TAVLTree.Create(@ComparePAElements);
  471. end;
  472. function TPasAnalyzer.MarkElementAsUsed(El: TPasElement; aClass: TPAElementClass
  473. ): boolean;
  474. function MarkModule(CurModule: TPasModule): boolean;
  475. begin
  476. if FindNode(CurModule)<>nil then
  477. exit(false);
  478. {$IFDEF VerbosePasAnalyzer}
  479. writeln('TPasAnalyzer.MarkElement.MarkModule mark "',GetElModName(CurModule),'"');
  480. {$ENDIF}
  481. Add(CurModule);
  482. Result:=true;
  483. end;
  484. var
  485. CurModule: TPasModule;
  486. begin
  487. if El=nil then exit(false);
  488. CurModule:=El.GetModule;
  489. if CurModule=nil then
  490. begin
  491. if El.ClassType=TPasUnresolvedSymbolRef then
  492. exit(false);
  493. {$IFDEF VerbosePasAnalyzer}
  494. writeln('TPasAnalyzer.MarkElement GetModule failed for El=',GetElModName(El),' El.Parent=',GetElModName(El.Parent));
  495. {$ENDIF}
  496. RaiseInconsistency(20170308093540,GetElModName(El));
  497. end;
  498. if (ScopeModule<>nil) then
  499. begin
  500. // single module analysis
  501. if (CurModule<>ScopeModule) then
  502. begin
  503. // element from another unit
  504. // -> mark unit as used and do not descend deeper
  505. MarkModule(CurModule);
  506. exit(false);
  507. end;
  508. end;
  509. // mark element
  510. if FindNode(El)<>nil then exit(false);
  511. Add(El,false,aClass);
  512. Result:=true;
  513. if ScopeModule=nil then
  514. begin
  515. // whole program analysis
  516. if IsIdentifier(El) then
  517. // an identifier of this unit is used -> mark unit
  518. if MarkModule(CurModule) then
  519. UseModule(CurModule,paumElement);
  520. end;
  521. end;
  522. function TPasAnalyzer.ElementVisited(El: TPasElement; Mode: TPAUseMode
  523. ): boolean;
  524. begin
  525. if El=nil then
  526. exit(true);
  527. if FChecked[Mode].Find(El)<>nil then exit(true);
  528. Result:=false;
  529. FChecked[Mode].Add(El);
  530. end;
  531. procedure TPasAnalyzer.UseElement(El: TPasElement; Access: TResolvedRefAccess;
  532. UseFull: boolean);
  533. var
  534. C: TClass;
  535. begin
  536. if El=nil then exit;
  537. C:=El.ClassType;
  538. if C.InheritsFrom(TPasType) then
  539. UseType(TPasType(El),paumElement)
  540. else if C.InheritsFrom(TPasVariable) then
  541. UseVariable(TPasVariable(El),Access,UseFull)
  542. else if C=TPasArgument then
  543. UseArgument(TPasArgument(El),Access)
  544. else if C=TPasResultElement then
  545. UseResultElement(TPasResultElement(El),Access)
  546. else if C.InheritsFrom(TPasProcedure) then
  547. UseProcedure(TPasProcedure(El))
  548. else if C.InheritsFrom(TPasExpr) then
  549. UseExpr(TPasExpr(El))
  550. else if C=TPasEnumValue then
  551. begin
  552. repeat
  553. MarkElementAsUsed(El);
  554. El:=El.Parent;
  555. until not (El is TPasType);
  556. end
  557. else if (C.InheritsFrom(TPasModule)) or (C=TPasUsesUnit) then
  558. // e.g. unitname.identifier -> the module is used by the identifier
  559. else
  560. RaiseNotSupported(20170307090947,El);
  561. end;
  562. procedure TPasAnalyzer.UsePublished(El: TPasElement);
  563. // mark typeinfo, do not mark code
  564. var
  565. C: TClass;
  566. Members: TFPList;
  567. i: Integer;
  568. Member: TPasElement;
  569. MemberResolved: TPasResolverResult;
  570. Prop: TPasProperty;
  571. ProcType: TPasProcedureType;
  572. begin
  573. {$IFDEF VerbosePasAnalyzer}
  574. writeln('TPasAnalyzer.UsePublished START ',GetObjName(El));
  575. {$ENDIF}
  576. if ElementVisited(El,paumPublished) then exit;
  577. C:=El.ClassType;
  578. if C=TPasUnresolvedSymbolRef then
  579. else if (C=TPasVariable) or (C=TPasConst) then
  580. UsePublished(TPasVariable(El).VarType)
  581. else if (C=TPasArgument) then
  582. UsePublished(TPasArgument(El).ArgType)
  583. else if C=TPasProperty then
  584. begin
  585. // published property
  586. Prop:=TPasProperty(El);
  587. for i:=0 to Prop.Args.Count-1 do
  588. UsePublished(TPasArgument(Prop.Args[i]).ArgType);
  589. UsePublished(Prop.VarType);
  590. // Note: read, write and index don't need extra typeinfo
  591. // stored and defaultvalue are only used when published -> mark as used
  592. UseElement(Prop.StoredAccessor,rraRead,false);
  593. UseElement(Prop.DefaultExpr,rraRead,false);
  594. end
  595. else if (C=TPasAliasType) or (C=TPasTypeAliasType) then
  596. UsePublished(TPasAliasType(El).DestType)
  597. else if C=TPasEnumType then
  598. else if C=TPasSetType then
  599. UsePublished(TPasSetType(El).EnumType)
  600. else if C=TPasArrayType then
  601. begin
  602. UsePublished(TPasArrayType(El).ElType);
  603. for i:=0 to length(TPasArrayType(El).Ranges)-1 do
  604. begin
  605. Member:=TPasArrayType(El).Ranges[i];
  606. Resolver.ComputeElement(Member,MemberResolved,[rcConstant]);
  607. UsePublished(MemberResolved.TypeEl);
  608. end;
  609. end
  610. else if C=TPasPointerType then
  611. UsePublished(TPasPointerType(El).DestType)
  612. else if C=TPasClassType then
  613. else if C=TPasClassOfType then
  614. else if C=TPasRecordType then
  615. begin
  616. // published record: use all members
  617. Members:=TPasRecordType(El).Members;
  618. for i:=0 to Members.Count-1 do
  619. begin
  620. Member:=TPasElement(Members[i]);
  621. UsePublished(Member);
  622. UseElement(Member,rraNone,true);
  623. end;
  624. end
  625. else if C.InheritsFrom(TPasProcedure) then
  626. UsePublished(TPasProcedure(El).ProcType)
  627. else if C.InheritsFrom(TPasProcedureType) then
  628. begin
  629. ProcType:=TPasProcedureType(El);
  630. for i:=0 to ProcType.Args.Count-1 do
  631. UsePublished(TPasArgument(ProcType.Args[i]).ArgType);
  632. if El is TPasFunctionType then
  633. UsePublished(TPasFunctionType(El).ResultEl.ResultType);
  634. end
  635. else
  636. RaiseNotSupported(20170414153904,El);
  637. end;
  638. procedure TPasAnalyzer.UseModule(aModule: TPasModule; Mode: TPAUseMode);
  639. procedure UseInitFinal(aSection: TPasImplBlock);
  640. begin
  641. if IsImplBlockEmpty(aSection) then exit;
  642. // this module has an initialization section -> mark module
  643. if FindNode(aModule)=nil then
  644. Add(aModule);
  645. UseImplBlock(aSection,true);
  646. end;
  647. begin
  648. if ElementVisited(aModule,Mode) then exit;
  649. {$IFDEF VerbosePasAnalyzer}
  650. writeln('TPasAnalyzer.UseModule ',GetElModName(aModule),' Mode=',Mode);
  651. {$ENDIF}
  652. if Mode in [paumAllExports,paumAllPublic] then
  653. begin
  654. if aModule is TPasProgram then
  655. UseSection(TPasProgram(aModule).ProgramSection,Mode)
  656. else if aModule is TPasLibrary then
  657. UseSection(TPasLibrary(aModule).LibrarySection,Mode)
  658. else
  659. begin
  660. // unit
  661. UseSection(aModule.InterfaceSection,Mode);
  662. end;
  663. end;
  664. UseInitFinal(aModule.InitializationSection);
  665. UseInitFinal(aModule.FinalizationSection);
  666. if Mode=paumElement then
  667. // e.g. a reference: unitname.identifier
  668. if FindNode(aModule)=nil then
  669. Add(aModule);
  670. end;
  671. procedure TPasAnalyzer.UseSection(Section: TPasSection; Mode: TPAUseMode);
  672. // called by UseModule
  673. var
  674. i: Integer;
  675. UsedModule: TPasModule;
  676. Decl: TPasElement;
  677. OnlyExports: Boolean;
  678. UsesClause: TPasUsesClause;
  679. begin
  680. // Section is TProgramSection, TLibrarySection, TInterfaceSection, TImplementationSection
  681. if Mode=paumElement then
  682. RaiseInconsistency(20170317172721,'');
  683. if ElementVisited(Section,Mode) then exit;
  684. OnlyExports:=Mode=paumAllExports;
  685. if Mode=paumAllPublic then
  686. MarkElementAsUsed(Section);
  687. {$IFDEF VerbosePasAnalyzer}
  688. writeln('TPasAnalyzer.UseSection ',GetElModName(Section),' Mode=',Mode);
  689. {$ENDIF}
  690. // used units
  691. UsesClause:=Section.UsesClause;
  692. for i:=0 to length(UsesClause)-1 do
  693. begin
  694. if UsesClause[i].Module is TPasModule then
  695. begin
  696. UsedModule:=TPasModule(UsesClause[i].Module);
  697. if ScopeModule=nil then
  698. // whole program analysis
  699. UseModule(UsedModule,paumAllExports)
  700. else
  701. begin
  702. // unit analysis
  703. if IsImplBlockEmpty(UsedModule.InitializationSection)
  704. and IsImplBlockEmpty(UsedModule.FinalizationSection) then
  705. continue;
  706. if FindNode(UsedModule)=nil then
  707. Add(UsedModule);
  708. UseImplBlock(UsedModule.InitializationSection,true);
  709. UseImplBlock(UsedModule.FinalizationSection,true);
  710. end;
  711. end;
  712. end;
  713. // section declarations
  714. for i:=0 to Section.Declarations.Count-1 do
  715. begin
  716. Decl:=TPasElement(Section.Declarations[i]);
  717. {$IFDEF VerbosePasAnalyzer}
  718. writeln('TPasAnalyzer.UseSection ',Section.ClassName,' Decl=',GetElModName(Decl),' Mode=',Mode);
  719. {$ENDIF}
  720. if Decl is TPasProcedure then
  721. begin
  722. if OnlyExports and ([pmExport,pmPublic]*TPasProcedure(Decl).Modifiers=[]) then
  723. continue;
  724. UseProcedure(TPasProcedure(Decl))
  725. end
  726. else if Decl is TPasType then
  727. UseType(TPasType(Decl),Mode)
  728. else if Decl is TPasVariable then
  729. begin
  730. if OnlyExports and ([vmExport,vmPublic]*TPasVariable(Decl).VarModifiers=[]) then
  731. continue;
  732. UseVariable(TPasVariable(Decl),rraNone,true);
  733. end
  734. else
  735. RaiseNotSupported(20170306165213,Decl);
  736. end;
  737. end;
  738. procedure TPasAnalyzer.UseImplBlock(Block: TPasImplBlock; Mark: boolean);
  739. var
  740. i: Integer;
  741. El: TPasElement;
  742. begin
  743. if Block=nil then exit;
  744. if Mark and not MarkElementAsUsed(Block) then exit;
  745. {$IFDEF VerbosePasAnalyzer}
  746. writeln('TPasAnalyzer.UseImplBlock ',GetElModName(Block),' Elements=',Block.Elements.Count);
  747. {$ENDIF}
  748. for i:=0 to Block.Elements.Count-1 do
  749. begin
  750. El:=TPasElement(Block.Elements[i]);
  751. if El is TPasImplElement then
  752. UseImplElement(TPasImplElement(El))
  753. else
  754. RaiseNotSupported(20170306195110,El);
  755. end;
  756. end;
  757. procedure TPasAnalyzer.UseImplElement(El: TPasImplElement);
  758. var
  759. C: TClass;
  760. ForLoop: TPasImplForLoop;
  761. CaseOf: TPasImplCaseOf;
  762. i, j: Integer;
  763. CaseSt: TPasImplCaseStatement;
  764. WithDo: TPasImplWithDo;
  765. SubEl, ParentEl: TPasElement;
  766. begin
  767. // do not mark
  768. if El=nil then exit;
  769. C:=El.ClassType;
  770. if C=TPasImplBlock then
  771. // impl block
  772. UseImplBlock(TPasImplBlock(El),false)
  773. else if C=TPasImplSimple then
  774. // simple expression
  775. UseExpr(TPasImplSimple(El).expr)
  776. else if C=TPasImplAssign then
  777. // a:=b
  778. begin
  779. UseExpr(TPasImplAssign(El).left);
  780. UseExpr(TPasImplAssign(El).right);
  781. end
  782. else if C=TPasImplAsmStatement then
  783. // asm..end
  784. else if C=TPasImplBeginBlock then
  785. // begin..end
  786. UseImplBlock(TPasImplBeginBlock(El),false)
  787. else if C=TPasImplCaseOf then
  788. begin
  789. // case-of
  790. CaseOf:=TPasImplCaseOf(El);
  791. UseExpr(CaseOf.CaseExpr);
  792. for i:=0 to CaseOf.Elements.Count-1 do
  793. begin
  794. SubEl:=TPasElement(CaseOf.Elements[i]);
  795. if SubEl.ClassType=TPasImplCaseStatement then
  796. begin
  797. CaseSt:=TPasImplCaseStatement(SubEl);
  798. for j:=0 to CaseSt.Expressions.Count-1 do
  799. UseExpr(TObject(CaseSt.Expressions[j]) as TPasExpr);
  800. UseImplElement(CaseSt.Body);
  801. end
  802. else if SubEl.ClassType=TPasImplCaseElse then
  803. UseImplBlock(TPasImplCaseElse(SubEl),false)
  804. else
  805. RaiseNotSupported(20170307195329,SubEl);
  806. end;
  807. end
  808. else if C=TPasImplForLoop then
  809. begin
  810. // for-loop
  811. ForLoop:=TPasImplForLoop(El);
  812. UseExpr(ForLoop.VariableName);
  813. UseExpr(ForLoop.StartExpr);
  814. UseExpr(ForLoop.EndExpr);
  815. UseImplElement(ForLoop.Body);
  816. end
  817. else if C=TPasImplIfElse then
  818. begin
  819. // if-then-else
  820. UseExpr(TPasImplIfElse(El).ConditionExpr);
  821. UseImplElement(TPasImplIfElse(El).IfBranch);
  822. UseImplElement(TPasImplIfElse(El).ElseBranch);
  823. end
  824. else if C=TPasImplLabelMark then
  825. // label mark
  826. else if C=TPasImplRepeatUntil then
  827. begin
  828. // repeat-until
  829. UseImplBlock(TPasImplRepeatUntil(El),false);
  830. UseExpr(TPasImplRepeatUntil(El).ConditionExpr);
  831. end
  832. else if C=TPasImplWhileDo then
  833. begin
  834. // while-do
  835. UseExpr(TPasImplWhileDo(El).ConditionExpr);
  836. UseImplBlock(TPasImplWhileDo(El),false);
  837. end
  838. else if C=TPasImplWithDo then
  839. begin
  840. // with-do
  841. WithDo:=TPasImplWithDo(El);
  842. for i:=0 to WithDo.Expressions.Count-1 do
  843. UseExpr(TObject(WithDo.Expressions[i]) as TPasExpr);
  844. UseImplBlock(WithDo,false);
  845. end
  846. else if C=TPasImplExceptOn then
  847. begin
  848. // except-on
  849. UseType(TPasImplExceptOn(El).TypeEl,paumElement);
  850. UseImplElement(TPasImplExceptOn(El).Body);
  851. end
  852. else if C=TPasImplRaise then
  853. begin
  854. // raise
  855. if TPasImplRaise(El).ExceptObject<>nil then
  856. UseExpr(TPasImplRaise(El).ExceptObject)
  857. else
  858. begin
  859. // raise; -> mark On E:
  860. ParentEl:=El.Parent;
  861. while ParentEl<>nil do
  862. begin
  863. if ParentEl is TPasImplExceptOn then
  864. begin
  865. UseVariable(TPasVariable(TPasImplExceptOn(ParentEl).VarEl),rraRead,false);
  866. break;
  867. end;
  868. ParentEl:=ParentEl.Parent;
  869. end;
  870. end;
  871. UseExpr(TPasImplRaise(El).ExceptAddr);
  872. end
  873. else if C=TPasImplTry then
  874. begin
  875. // try..finally/except..else..end
  876. UseImplBlock(TPasImplTry(El),false);
  877. UseImplBlock(TPasImplTry(El).FinallyExcept,false);
  878. UseImplBlock(TPasImplTry(El).ElseBranch,false);
  879. end
  880. else
  881. RaiseNotSupported(20170307162715,El);
  882. end;
  883. procedure TPasAnalyzer.UseExpr(El: TPasExpr);
  884. var
  885. Ref: TResolvedReference;
  886. C: TClass;
  887. Params: TPasExprArray;
  888. i: Integer;
  889. BuiltInProc: TResElDataBuiltInProc;
  890. ParamResolved: TPasResolverResult;
  891. Decl: TPasElement;
  892. begin
  893. if El=nil then exit;
  894. // expressions are not marked
  895. Ref:=nil;
  896. if El.CustomData is TResolvedReference then
  897. begin
  898. // this is a reference -> mark target
  899. Ref:=TResolvedReference(El.CustomData);
  900. Decl:=Ref.Declaration;
  901. UseElement(Decl,Ref.Access,false);
  902. if Resolver.IsNameExpr(El) then
  903. begin
  904. if Ref.WithExprScope<>nil then
  905. begin
  906. if Ref.WithExprScope.Scope is TPasRecordScope then
  907. begin
  908. // a record member was accessed -> access the record too
  909. UseExprRef(Ref.WithExprScope.Expr,Ref.Access,false);
  910. exit;
  911. end;
  912. end;
  913. if (Decl is TPasVariable)
  914. and (El.Parent is TBinaryExpr)
  915. and (TBinaryExpr(El.Parent).right=El) then
  916. begin
  917. if ((Decl.Parent is TPasRecordType)
  918. or (Decl.Parent is TPasVariant)) then
  919. begin
  920. // a record member was accessed -> access the record too
  921. UseExprRef(TBinaryExpr(El.Parent).left,Ref.Access,false);
  922. end;
  923. end;
  924. end;
  925. if Decl is TPasUnresolvedSymbolRef then
  926. begin
  927. if Decl.CustomData is TResElDataBuiltInProc then
  928. begin
  929. BuiltInProc:=TResElDataBuiltInProc(Decl.CustomData);
  930. if BuiltInProc.BuiltIn=bfTypeInfo then
  931. begin
  932. Params:=(El.Parent as TParamsExpr).Params;
  933. Resolver.ComputeElement(Params[0],ParamResolved,[rcNoImplicitProc]);
  934. {$IFDEF VerbosePasAnalyzer}
  935. writeln('TPasAnalyzer.UseExpr typeinfo ',GetResolverResultDbg(ParamResolved));
  936. {$ENDIF}
  937. if ParamResolved.IdentEl is TPasFunction then
  938. UsePublished(TPasFunction(ParamResolved.IdentEl).FuncType.ResultEl.ResultType)
  939. else
  940. UsePublished(ParamResolved.IdentEl);
  941. end;
  942. end;
  943. end;
  944. end;
  945. UseExpr(El.format1);
  946. UseExpr(El.format2);
  947. C:=El.ClassType;
  948. if (C=TPrimitiveExpr)
  949. or (C=TSelfExpr)
  950. or (C=TBoolConstExpr)
  951. or (C=TInheritedExpr)
  952. or (C=TNilExpr) then
  953. else if C=TBinaryExpr then
  954. begin
  955. UseExpr(TBinaryExpr(El).left);
  956. UseExpr(TBinaryExpr(El).right);
  957. end
  958. else if C=TUnaryExpr then
  959. UseExpr(TUnaryExpr(El).Operand)
  960. else if C=TParamsExpr then
  961. begin
  962. UseExpr(TParamsExpr(El).Value);
  963. Params:=TParamsExpr(El).Params;
  964. for i:=0 to length(Params)-1 do
  965. UseExpr(Params[i]);
  966. end
  967. else if C=TArrayValues then
  968. begin
  969. Params:=TArrayValues(El).Values;
  970. for i:=0 to length(Params)-1 do
  971. UseExpr(Params[i]);
  972. end
  973. else
  974. RaiseNotSupported(20170307085444,El);
  975. end;
  976. procedure TPasAnalyzer.UseExprRef(Expr: TPasExpr; Access: TResolvedRefAccess;
  977. UseFull: boolean);
  978. var
  979. Ref: TResolvedReference;
  980. C: TClass;
  981. Bin: TBinaryExpr;
  982. Params: TParamsExpr;
  983. ValueResolved: TPasResolverResult;
  984. begin
  985. if (Expr.CustomData is TResolvedReference) then
  986. begin
  987. Ref:=TResolvedReference(Expr.CustomData);
  988. UseElement(Ref.Declaration,Access,UseFull);
  989. end;
  990. C:=Expr.ClassType;
  991. if C=TBinaryExpr then
  992. begin
  993. Bin:=TBinaryExpr(Expr);
  994. if Bin.OpCode in [eopSubIdent,eopNone] then
  995. UseExprRef(Bin.right,Access,UseFull);
  996. end
  997. else if C=TParamsExpr then
  998. begin
  999. Params:=TParamsExpr(Expr);
  1000. case Params.Kind of
  1001. pekFuncParams:
  1002. if Resolver.IsTypeCast(Params) then
  1003. UseExprRef(Params.Params[0],Access,UseFull)
  1004. else
  1005. UseExprRef(Params.Value,Access,UseFull);
  1006. pekArrayParams:
  1007. begin
  1008. Resolver.ComputeElement(Params.Value,ValueResolved,[]);
  1009. if not Resolver.IsDynArray(ValueResolved.TypeEl) then
  1010. UseExprRef(Params.Value,Access,UseFull);
  1011. end;
  1012. pekSet: ;
  1013. else
  1014. RaiseNotSupported(20170403173817,Params);
  1015. end;
  1016. end
  1017. else if (C=TSelfExpr) or ((C=TPrimitiveExpr) and (TPrimitiveExpr(Expr).Kind=pekIdent)) then
  1018. // ok
  1019. else if (Access=rraRead)
  1020. and ((C=TPrimitiveExpr)
  1021. or (C=TNilExpr)
  1022. or (C=TBoolConstExpr)
  1023. or (C=TUnaryExpr)) then
  1024. // ok
  1025. else
  1026. begin
  1027. {$IFDEF VerbosePasResolver}
  1028. writeln('TPasResolver.UseExprRef Expr=',GetObjName(Expr),' Access=',Access,' Declaration="',Expr.GetDeclaration(false),'"');
  1029. {$ENDIF}
  1030. RaiseNotSupported(20170306102158,Expr);
  1031. end;
  1032. end;
  1033. procedure TPasAnalyzer.UseProcedure(Proc: TPasProcedure);
  1034. procedure UseOverrides(CurProc: TPasProcedure);
  1035. var
  1036. OverrideList: TPAOverrideList;
  1037. i: Integer;
  1038. OverrideProc: TPasProcedure;
  1039. begin
  1040. OverrideList:=FindOverrideList(CurProc);
  1041. if OverrideList=nil then exit;
  1042. // Note: while traversing the OverrideList it may grow
  1043. i:=0;
  1044. while i<OverrideList.Count do
  1045. begin
  1046. OverrideProc:=TObject(OverrideList.Overrides[i]) as TPasProcedure;
  1047. UseProcedure(OverrideProc);
  1048. inc(i);
  1049. end;
  1050. end;
  1051. var
  1052. ProcScope: TPasProcedureScope;
  1053. ImplProc: TPasProcedure;
  1054. begin
  1055. // use declaration, not implementation
  1056. ProcScope:=Proc.CustomData as TPasProcedureScope;
  1057. if ProcScope.DeclarationProc<>nil then
  1058. exit; // skip implementation, Note:PasResolver always refers the declaration
  1059. if not MarkElementAsUsed(Proc) then exit;
  1060. {$IFDEF VerbosePasAnalyzer}
  1061. writeln('TPasAnalyzer.UseProcedure ',GetElModName(Proc));
  1062. {$ENDIF}
  1063. UseProcedureType(Proc.ProcType,false);
  1064. ImplProc:=Proc;
  1065. if ProcScope.ImplProc<>nil then
  1066. ImplProc:=ProcScope.ImplProc;
  1067. if ImplProc.Body<>nil then
  1068. UseImplBlock(ImplProc.Body.Body,false);
  1069. if Proc.IsOverride and (ProcScope.OverriddenProc<>nil) then
  1070. AddOverride(ProcScope.OverriddenProc,Proc);
  1071. // mark overrides
  1072. if [pmOverride,pmVirtual]*Proc.Modifiers<>[] then
  1073. UseOverrides(Proc);
  1074. end;
  1075. procedure TPasAnalyzer.UseProcedureType(ProcType: TPasProcedureType;
  1076. Mark: boolean);
  1077. var
  1078. i: Integer;
  1079. Arg: TPasArgument;
  1080. begin
  1081. {$IFDEF VerbosePasAnalyzer}
  1082. writeln('TPasAnalyzer.UseProcedureType ',GetElModName(ProcType));
  1083. {$ENDIF}
  1084. if Mark and not MarkElementAsUsed(ProcType) then exit;
  1085. for i:=0 to ProcType.Args.Count-1 do
  1086. begin
  1087. Arg:=TPasArgument(ProcType.Args[i]);
  1088. // Note: argument are marked when used in code
  1089. // mark argument type
  1090. UseType(Arg.ArgType,paumElement);
  1091. end;
  1092. if ProcType is TPasFunctionType then
  1093. UseType(TPasFunctionType(ProcType).ResultEl.ResultType,paumElement);
  1094. end;
  1095. procedure TPasAnalyzer.UseType(El: TPasType; Mode: TPAUseMode);
  1096. var
  1097. C: TClass;
  1098. i: Integer;
  1099. begin
  1100. if El=nil then exit;
  1101. C:=El.ClassType;
  1102. if Mode=paumAllExports then
  1103. begin
  1104. {$IFDEF VerbosePasAnalyzer}
  1105. writeln('TPasAnalyzer.UseType searching exports in ',GetElModName(El),' ...');
  1106. {$ENDIF}
  1107. if C=TPasRecordType then
  1108. UseRecordType(TPasRecordType(El),Mode)
  1109. else if C=TPasClassType then
  1110. UseClassType(TPasClassType(El),Mode);
  1111. end
  1112. else
  1113. begin
  1114. {$IFDEF VerbosePasAnalyzer}
  1115. writeln('TPasAnalyzer.UseType using ',GetElModName(El),' Mode=',Mode);
  1116. {$ENDIF}
  1117. if C=TPasUnresolvedSymbolRef then
  1118. begin
  1119. if (El.CustomData is TResElDataBaseType)
  1120. or (El.CustomData is TResElDataBuiltInProc) then
  1121. else
  1122. RaiseNotSupported(20170307101353,El);
  1123. end
  1124. else if (C=TPasAliasType)
  1125. or (C=TPasTypeAliasType)
  1126. or (C=TPasClassOfType) then
  1127. begin
  1128. if not MarkElementAsUsed(El) then exit;
  1129. UseType(TPasAliasType(El).DestType,Mode);
  1130. end
  1131. else if C=TPasArrayType then
  1132. begin
  1133. if not MarkElementAsUsed(El) then exit;
  1134. for i:=0 to length(TPasArrayType(El).Ranges)-1 do
  1135. UseExpr(TPasArrayType(El).Ranges[i]);
  1136. UseType(TPasArrayType(El).ElType,Mode);
  1137. end
  1138. else if C=TPasRecordType then
  1139. UseRecordType(TPasRecordType(El),Mode)
  1140. else if C=TPasClassType then
  1141. UseClassType(TPasClassType(El),Mode)
  1142. else if C=TPasEnumType then
  1143. begin
  1144. if not MarkElementAsUsed(El) then exit;
  1145. end
  1146. else if C=TPasPointerType then
  1147. begin
  1148. if not MarkElementAsUsed(El) then exit;
  1149. UseType(TPasPointerType(El).DestType,Mode);
  1150. end
  1151. else if C=TPasRangeType then
  1152. begin
  1153. if not MarkElementAsUsed(El) then exit;
  1154. UseExpr(TPasRangeType(El).RangeExpr);
  1155. end
  1156. else if C=TPasSetType then
  1157. begin
  1158. if not MarkElementAsUsed(El) then exit;
  1159. UseType(TPasSetType(El).EnumType,Mode);
  1160. end
  1161. else if C.InheritsFrom(TPasProcedureType) then
  1162. UseProcedureType(TPasProcedureType(El),true)
  1163. else
  1164. RaiseNotSupported(20170306170315,El);
  1165. end;
  1166. end;
  1167. procedure TPasAnalyzer.UseRecordType(El: TPasRecordType; Mode: TPAUseMode);
  1168. // called by UseType
  1169. var
  1170. i: Integer;
  1171. begin
  1172. if Mode=paumAllExports then exit;
  1173. MarkElementAsUsed(El);
  1174. if (Mode=paumAllPublic) and not ElementVisited(El,Mode) then
  1175. for i:=0 to El.Members.Count-1 do
  1176. UseVariable(TObject(El.Members[i]) as TPasVariable,rraNone,true);
  1177. end;
  1178. procedure TPasAnalyzer.UseClassType(El: TPasClassType; Mode: TPAUseMode);
  1179. // called by UseType
  1180. var
  1181. i: Integer;
  1182. Member: TPasElement;
  1183. AllPublished, FirstTime: Boolean;
  1184. ProcScope: TPasProcedureScope;
  1185. ClassScope: TPasClassScope;
  1186. Ref: TResolvedReference;
  1187. begin
  1188. FirstTime:=true;
  1189. case Mode of
  1190. paumAllExports: exit;
  1191. paumAllPublic:
  1192. begin
  1193. if MarkElementAsUsed(El) then
  1194. ElementVisited(El,Mode)
  1195. else
  1196. begin
  1197. if ElementVisited(El,Mode) then exit;
  1198. FirstTime:=false;
  1199. end;
  1200. end;
  1201. paumElement:
  1202. if not MarkElementAsUsed(El) then exit;
  1203. else
  1204. RaiseInconsistency(20170414152143,IntToStr(ord(Mode)));
  1205. end;
  1206. {$IFDEF VerbosePasAnalyzer}
  1207. writeln('TPasAnalyzer.UseClassType ',GetElModName(El),' ',Mode,' First=',FirstTime);
  1208. {$ENDIF}
  1209. if El.IsForward then
  1210. begin
  1211. Ref:=El.CustomData as TResolvedReference;
  1212. UseClassType(Ref.Declaration as TPasClassType,Mode);
  1213. exit;
  1214. end;
  1215. ClassScope:=El.CustomData as TPasClassScope;
  1216. if FirstTime then
  1217. begin
  1218. UseType(ClassScope.DirectAncestor,paumElement);
  1219. UseType(El.HelperForType,paumElement);
  1220. UseExpr(El.GUIDExpr);
  1221. for i:=0 to El.Interfaces.Count-1 do
  1222. UseType(TPasType(El.Interfaces[i]),paumElement);
  1223. end;
  1224. // members
  1225. AllPublished:=(Mode<>paumAllExports);
  1226. for i:=0 to El.Members.Count-1 do
  1227. begin
  1228. Member:=TPasElement(El.Members[i]);
  1229. if FirstTime and (Member is TPasProcedure) then
  1230. begin
  1231. ProcScope:=Member.CustomData as TPasProcedureScope;
  1232. if TPasProcedure(Member).IsOverride and (ProcScope.OverriddenProc<>nil) then
  1233. begin
  1234. // this is an override
  1235. AddOverride(ProcScope.OverriddenProc,Member);
  1236. if ScopeModule<>nil then
  1237. begin
  1238. // when analyzingf a single module, all overrides are assumed to be called
  1239. UseElement(Member,rraNone,true);
  1240. continue;
  1241. end;
  1242. end;
  1243. end;
  1244. if AllPublished and (Member.Visibility=visPublished) then
  1245. begin
  1246. // include published
  1247. if not FirstTime then continue;
  1248. UsePublished(Member);
  1249. end
  1250. else if Mode=paumElement then
  1251. continue
  1252. else if IsModuleInternal(Member) then
  1253. // private or strict private
  1254. continue
  1255. else
  1256. ; // else: class is in unit interface, mark all non private members
  1257. UseElement(Member,rraNone,true);
  1258. end;
  1259. end;
  1260. procedure TPasAnalyzer.UseVariable(El: TPasVariable;
  1261. Access: TResolvedRefAccess; UseFull: boolean);
  1262. var
  1263. Usage: TPAElement;
  1264. UseRead, UseWrite: boolean;
  1265. procedure UpdateVarAccess(IsRead, IsWrite: boolean);
  1266. begin
  1267. if IsRead then
  1268. case Usage.Access of
  1269. paiaNone: begin Usage.Access:=paiaRead; UseRead:=true; end;
  1270. paiaRead: ;
  1271. paiaWrite: begin Usage.Access:=paiaWriteRead; UseRead:=true; end;
  1272. paiaReadWrite: ;
  1273. paiaWriteRead: ;
  1274. else RaiseInconsistency(20170311182420,'');
  1275. end;
  1276. if IsWrite then
  1277. case Usage.Access of
  1278. paiaNone: begin Usage.Access:=paiaWrite; UseWrite:=true; end;
  1279. paiaRead: begin Usage.Access:=paiaReadWrite; UseWrite:=true; end;
  1280. paiaWrite: ;
  1281. paiaReadWrite: ;
  1282. paiaWriteRead: ;
  1283. else RaiseInconsistency(20170311182536,'');
  1284. end;
  1285. end;
  1286. var
  1287. Prop: TPasProperty;
  1288. i: Integer;
  1289. IsRead, IsWrite, CanRead, CanWrite: Boolean;
  1290. begin
  1291. {$IFDEF VerbosePasAnalyzer}
  1292. writeln('TPasAnalyzer.UseVariable ',GetElModName(El),' ',Access,' Full=',UseFull);
  1293. {$ENDIF}
  1294. if El.ClassType=TPasProperty then
  1295. Prop:=TPasProperty(El)
  1296. else
  1297. Prop:=nil;
  1298. IsRead:=false;
  1299. IsWrite:=false;
  1300. if UseFull then
  1301. if (Prop<>nil) then
  1302. begin
  1303. CanRead:=Resolver.GetPasPropertyGetter(Prop)<>nil;
  1304. CanWrite:=Resolver.GetPasPropertySetter(Prop)<>nil;
  1305. if CanRead then
  1306. begin
  1307. if CanWrite then
  1308. Access:=rraReadAndAssign
  1309. else
  1310. Access:=rraRead;
  1311. end
  1312. else
  1313. if CanWrite then
  1314. Access:=rraAssign
  1315. else
  1316. Access:=rraNone;
  1317. end
  1318. else
  1319. Access:=rraRead;
  1320. case Access of
  1321. rraNone: ;
  1322. rraRead: IsRead:=true;
  1323. rraAssign: IsWrite:=true;
  1324. rraReadAndAssign,
  1325. rraVarParam,
  1326. rraOutParam: begin IsRead:=true; IsWrite:=true; end;
  1327. rraParamToUnknownProc: RaiseInconsistency(20170307153439,'');
  1328. else
  1329. RaiseInconsistency(20170308120949,'');
  1330. end;
  1331. UseRead:=false;
  1332. UseWrite:=false;
  1333. if MarkElementAsUsed(El) then
  1334. begin
  1335. // first access of this variable
  1336. Usage:=FindElement(El);
  1337. // first set flags
  1338. if El.Expr<>nil then
  1339. Usage.Access:=paiaWrite;
  1340. UpdateVarAccess(IsRead,IsWrite);
  1341. // then use recursively
  1342. UseType(El.VarType,paumElement);
  1343. UseExpr(El.Expr);
  1344. UseExpr(El.LibraryName);
  1345. UseExpr(El.ExportName);
  1346. if Prop<>nil then
  1347. begin
  1348. for i:=0 to Prop.Args.Count-1 do
  1349. UseType(TPasArgument(Prop.Args[i]).ArgType,paumElement);
  1350. UseExpr(Prop.IndexExpr);
  1351. // ToDo: Prop.ImplementsFunc
  1352. // ToDo: Prop.DispIDExpr
  1353. // ToDo: Prop.StoredAccessor;
  1354. // ToDo: Prop.DefaultExpr;
  1355. end;
  1356. end
  1357. else
  1358. begin
  1359. Usage:=FindElement(El);
  1360. if Usage=nil then
  1361. exit; // element outside of scope
  1362. // var is accessed another time
  1363. // first update flags
  1364. UpdateVarAccess(IsRead,IsWrite);
  1365. end;
  1366. // then use recursively
  1367. if Prop<>nil then
  1368. begin
  1369. {$IFDEF VerbosePasAnalyzer}
  1370. writeln('TPasAnalyzer.UseVariable Property=',Prop.FullName,
  1371. ' Ancestor=',GetElModName(Resolver.GetPasPropertyAncestor(Prop)),
  1372. ' UseRead=',UseRead,',Acc=',GetElModName(Resolver.GetPasPropertyGetter(Prop)),
  1373. ' UseWrite=',UseWrite,',Acc=',GetElModName(Resolver.GetPasPropertySetter(Prop)),
  1374. '');
  1375. {$ENDIF}
  1376. if UseRead then
  1377. UseElement(Resolver.GetPasPropertyGetter(Prop),rraRead,false);
  1378. if UseWrite then
  1379. UseElement(Resolver.GetPasPropertySetter(Prop),rraAssign,false);
  1380. end;
  1381. end;
  1382. procedure TPasAnalyzer.UseArgument(El: TPasArgument; Access: TResolvedRefAccess
  1383. );
  1384. var
  1385. Usage: TPAElement;
  1386. IsRead, IsWrite: Boolean;
  1387. begin
  1388. IsRead:=false;
  1389. IsWrite:=false;
  1390. case Access of
  1391. rraNone: ;
  1392. rraRead: IsRead:=true;
  1393. rraAssign: IsWrite:=true;
  1394. rraReadAndAssign,
  1395. rraVarParam,
  1396. rraOutParam: begin IsRead:=true; IsWrite:=true; end;
  1397. rraParamToUnknownProc: RaiseInconsistency(20170308121031,'');
  1398. else
  1399. RaiseInconsistency(20170308121037,'');
  1400. end;
  1401. if MarkElementAsUsed(El) then
  1402. begin
  1403. // first time
  1404. Usage:=FindElement(El);
  1405. end
  1406. else
  1407. begin
  1408. // used again
  1409. Usage:=FindElement(El);
  1410. if Usage=nil then
  1411. RaiseNotSupported(20170308121928,El);
  1412. end;
  1413. UpdateAccess(IsWrite, IsRead, Usage);
  1414. end;
  1415. procedure TPasAnalyzer.UseResultElement(El: TPasResultElement;
  1416. Access: TResolvedRefAccess);
  1417. var
  1418. IsRead, IsWrite: Boolean;
  1419. Usage: TPAElement;
  1420. begin
  1421. IsRead:=false;
  1422. IsWrite:=false;
  1423. case Access of
  1424. rraNone: ;
  1425. rraRead: IsRead:=true;
  1426. rraAssign: IsWrite:=true;
  1427. rraReadAndAssign,
  1428. rraVarParam,
  1429. rraOutParam: begin IsRead:=true; IsWrite:=true; end;
  1430. rraParamToUnknownProc: RaiseInconsistency(20170308122319,'');
  1431. else
  1432. RaiseInconsistency(20170308122324,'');
  1433. end;
  1434. if MarkElementAsUsed(El) then
  1435. begin
  1436. // first time
  1437. Usage:=FindElement(El);
  1438. end
  1439. else
  1440. begin
  1441. // used again
  1442. Usage:=FindElement(El);
  1443. if Usage=nil then
  1444. RaiseNotSupported(20170308122333,El);
  1445. end;
  1446. UpdateAccess(IsWrite, IsRead, Usage);
  1447. end;
  1448. procedure TPasAnalyzer.EmitElementHints(El: TPasElement);
  1449. begin
  1450. if El=nil then exit;
  1451. if El is TPasVariable then
  1452. EmitVariableHints(TPasVariable(El))
  1453. else if El is TPasType then
  1454. EmitTypeHints(TPasType(El))
  1455. else if El is TPasProcedure then
  1456. EmitProcedureHints(TPasProcedure(El))
  1457. else
  1458. RaiseInconsistency(20170312093126,'');
  1459. end;
  1460. procedure TPasAnalyzer.EmitSectionHints(Section: TPasSection);
  1461. var
  1462. i: Integer;
  1463. UsedModule, aModule: TPasModule;
  1464. UsesClause: TPasUsesClause;
  1465. begin
  1466. {$IFDEF VerbosePasAnalyzer}
  1467. writeln('TPasAnalyzer.EmitSectionHints ',GetElModName(Section));
  1468. {$ENDIF}
  1469. // initialization, program or library sections
  1470. aModule:=Section.GetModule;
  1471. UsesClause:=Section.UsesClause;
  1472. for i:=0 to length(UsesClause)-1 do
  1473. begin
  1474. if UsesClause[i].Module is TPasModule then
  1475. begin
  1476. UsedModule:=TPasModule(UsesClause[i].Module);
  1477. if CompareText(UsedModule.Name,'system')=0 then continue;
  1478. if FindNode(UsedModule)=nil then
  1479. EmitMessage(20170311191725,mtHint,nPAUnitNotUsed,sPAUnitNotUsed,
  1480. [UsedModule.Name,aModule.Name],aModule);
  1481. end;
  1482. end;
  1483. EmitDeclarationsHints(Section);
  1484. end;
  1485. procedure TPasAnalyzer.EmitDeclarationsHints(El: TPasDeclarations);
  1486. var
  1487. i: Integer;
  1488. Decl: TPasElement;
  1489. Usage: TPAElement;
  1490. begin
  1491. {$IFDEF VerbosePasAnalyzer}
  1492. writeln('TPasAnalyzer.EmitDeclarationsHints ',GetElModName(El));
  1493. {$ENDIF}
  1494. for i:=0 to El.Declarations.Count-1 do
  1495. begin
  1496. Decl:=TPasElement(El.Declarations[i]);
  1497. if Decl is TPasVariable then
  1498. EmitVariableHints(TPasVariable(Decl))
  1499. else if Decl is TPasType then
  1500. EmitTypeHints(TPasType(Decl))
  1501. else if Decl is TPasProcedure then
  1502. EmitProcedureHints(TPasProcedure(Decl))
  1503. else
  1504. begin
  1505. Usage:=FindPAElement(Decl);
  1506. if Usage=nil then
  1507. begin
  1508. // declaration was never used
  1509. EmitMessage(20170311231734,mtHint,nPALocalXYNotUsed,
  1510. sPALocalXYNotUsed,[Decl.ElementTypeName,Decl.Name],Decl);
  1511. end;
  1512. end;
  1513. end;
  1514. end;
  1515. procedure TPasAnalyzer.EmitTypeHints(El: TPasType);
  1516. var
  1517. C: TClass;
  1518. Usage: TPAElement;
  1519. i: Integer;
  1520. Member: TPasElement;
  1521. begin
  1522. {$IFDEF VerbosePasAnalyzer}
  1523. writeln('TPasAnalyzer.EmitTypeHints ',GetElModName(El));
  1524. {$ENDIF}
  1525. Usage:=FindPAElement(El);
  1526. if Usage=nil then
  1527. begin
  1528. // the whole type was never used
  1529. if (El.Visibility in [visPrivate,visStrictPrivate]) then
  1530. EmitMessage(20170312000020,mtHint,nPAPrivateTypeXNeverUsed,
  1531. sPAPrivateTypeXNeverUsed,[El.FullName],El)
  1532. else
  1533. EmitMessage(20170312000025,mtHint,nPALocalXYNotUsed,
  1534. sPALocalXYNotUsed,[El.ElementTypeName,El.Name],El);
  1535. exit;
  1536. end;
  1537. // emit hints for sub elements
  1538. C:=El.ClassType;
  1539. if C=TPasRecordType then
  1540. begin
  1541. for i:=0 to TPasRecordType(El).Members.Count-1 do
  1542. EmitVariableHints(TObject(TPasRecordType(El).Members[i]) as TPasVariable);
  1543. end
  1544. else if C=TPasClassType then
  1545. begin
  1546. if TPasClassType(El).IsForward then exit;
  1547. for i:=0 to TPasClassType(El).Members.Count-1 do
  1548. begin
  1549. Member:=TPasElement(TPasClassType(El).Members[i]);
  1550. EmitElementHints(Member);
  1551. end;
  1552. end;
  1553. end;
  1554. procedure TPasAnalyzer.EmitVariableHints(El: TPasVariable);
  1555. var
  1556. Usage: TPAElement;
  1557. begin
  1558. {$IFDEF VerbosePasAnalyzer}
  1559. writeln('TPasAnalyzer.EmitVariableHints ',GetElModName(El));
  1560. {$ENDIF}
  1561. Usage:=FindPAElement(El);
  1562. if Usage=nil then
  1563. begin
  1564. // not used
  1565. if El.Visibility in [visPrivate,visStrictPrivate] then
  1566. begin
  1567. if El.ClassType=TPasConst then
  1568. EmitMessage(20170311234602,mtHint,nPAPrivateConstXNeverUsed,
  1569. sPAPrivateConstXNeverUsed,[El.FullName],El)
  1570. else if El.ClassType=TPasProperty then
  1571. EmitMessage(20170311234634,mtHint,nPAPrivatePropertyXNeverUsed,
  1572. sPAPrivatePropertyXNeverUsed,[El.FullName],El)
  1573. else
  1574. EmitMessage(20170311231412,mtHint,nPAPrivateFieldIsNeverUsed,
  1575. sPAPrivateFieldIsNeverUsed,[El.FullName],El);
  1576. end
  1577. else if El.ClassType=TPasVariable then
  1578. EmitMessage(20170311234201,mtHint,nPALocalVariableNotUsed,
  1579. sPALocalVariableNotUsed,[El.Name],El)
  1580. else
  1581. EmitMessage(20170314221334,mtHint,nPALocalXYNotUsed,
  1582. sPALocalXYNotUsed,[El.ElementTypeName,El.Name],El);
  1583. end
  1584. else if Usage.Access=paiaWrite then
  1585. begin
  1586. // write without read
  1587. if El.Visibility in [visPrivate,visStrictPrivate] then
  1588. EmitMessage(20170311234159,mtHint,nPAPrivateFieldIsAssignedButNeverUsed,
  1589. sPAPrivateFieldIsAssignedButNeverUsed,[El.FullName],El)
  1590. else
  1591. EmitMessage(20170311233825,mtHint,nPALocalVariableIsAssignedButNeverUsed,
  1592. sPALocalVariableIsAssignedButNeverUsed,[El.Name],El);
  1593. end;
  1594. end;
  1595. procedure TPasAnalyzer.EmitProcedureHints(El: TPasProcedure);
  1596. var
  1597. Args: TFPList;
  1598. i: Integer;
  1599. Arg: TPasArgument;
  1600. Usage: TPAElement;
  1601. ProcScope: TPasProcedureScope;
  1602. PosEl: TPasElement;
  1603. begin
  1604. {$IFDEF VerbosePasAnalyzer}
  1605. writeln('TPasAnalyzer.EmitProcedureHints ',GetElModName(El));
  1606. {$ENDIF}
  1607. ProcScope:=El.CustomData as TPasProcedureScope;
  1608. if (ProcScope.DeclarationProc=nil) and (FindNode(El)=nil) then
  1609. begin
  1610. // procedure never used
  1611. if El.Visibility in [visPrivate,visStrictPrivate] then
  1612. EmitMessage(20170312093348,mtHint,nPAPrivateMethodIsNeverUsed,
  1613. sPAPrivateMethodIsNeverUsed,[El.FullName],El)
  1614. else
  1615. EmitMessage(20170312093418,mtHint,nPALocalXYNotUsed,
  1616. sPALocalXYNotUsed,[El.ElementTypeName,El.Name],El);
  1617. exit;
  1618. end;
  1619. // procedure was used
  1620. if [pmAbstract,pmAssembler,pmExternal]*El.Modifiers<>[] then exit;
  1621. if ProcScope.DeclarationProc=nil then
  1622. begin
  1623. // check parameters
  1624. Args:=El.ProcType.Args;
  1625. for i:=0 to Args.Count-1 do
  1626. begin
  1627. Arg:=TPasArgument(Args[i]);
  1628. Usage:=FindPAElement(Arg);
  1629. if (Usage=nil) or (Usage.Access=paiaNone) then
  1630. // parameter was never used
  1631. EmitMessage(20170312094401,mtHint,nPAParameterNotUsed,
  1632. sPAParameterNotUsed,[Arg.Name],Arg)
  1633. else
  1634. begin
  1635. // parameter was used
  1636. if (Usage.Access=paiaWrite) and (Arg.Access<>argOut) then
  1637. EmitMessage(20170312095348,mtHint,nPAValueParameterIsAssignedButNeverUsed,
  1638. sPAValueParameterIsAssignedButNeverUsed,[Arg.Name],Arg);
  1639. end;
  1640. end;
  1641. // check result
  1642. if (El is TPasFunction) then
  1643. begin
  1644. PosEl:=TPasFunction(El).FuncType.ResultEl;
  1645. if (ProcScope.ImplProc<>nil) and (TPasFunction(ProcScope.ImplProc).FuncType.ResultEl<>nil) then
  1646. PosEl:=TPasFunction(ProcScope.ImplProc).FuncType.ResultEl;
  1647. Usage:=FindPAElement(TPasFunction(El).FuncType.ResultEl);
  1648. if (Usage=nil) or (Usage.Access in [paiaNone,paiaRead]) then
  1649. // result was never used
  1650. EmitMessage(20170313214038,mtHint,nPAFunctionResultDoesNotSeemToBeSet,
  1651. sPAFunctionResultDoesNotSeemToBeSet,[],PosEl)
  1652. else
  1653. begin
  1654. // result was used
  1655. end;
  1656. end;
  1657. end;
  1658. if El.Body<>nil then
  1659. begin
  1660. // check declarations
  1661. EmitDeclarationsHints(El.Body);
  1662. // ToDo: emit hints for statements
  1663. end;
  1664. end;
  1665. constructor TPasAnalyzer.Create;
  1666. var
  1667. m: TPAUseMode;
  1668. begin
  1669. CreateTree;
  1670. for m in TPAUseMode do
  1671. FChecked[m]:=TAVLTree.Create;
  1672. FOverrideLists:=TAVLTree.Create(@ComparePAOverrideLists);
  1673. end;
  1674. destructor TPasAnalyzer.Destroy;
  1675. var
  1676. m: TPAUseMode;
  1677. begin
  1678. Clear;
  1679. FreeAndNil(FOverrideLists);
  1680. FreeAndNil(FUsedElements);
  1681. for m in TPAUseMode do
  1682. FreeAndNil(FChecked[m]);
  1683. inherited Destroy;
  1684. end;
  1685. procedure TPasAnalyzer.Clear;
  1686. var
  1687. m: TPAUseMode;
  1688. begin
  1689. FOverrideLists.FreeAndClear;
  1690. FUsedElements.FreeAndClear;
  1691. for m in TPAUseMode do
  1692. FChecked[m].Clear;
  1693. end;
  1694. procedure TPasAnalyzer.AnalyzeModule(aModule: TPasModule);
  1695. var
  1696. Mode: TPAUseMode;
  1697. begin
  1698. {$IFDEF VerbosePasAnalyzer}
  1699. writeln('TPasAnalyzer.AnalyzeModule START ',GetElModName(aModule));
  1700. {$ENDIF}
  1701. if Resolver=nil then
  1702. RaiseInconsistency(20170314223032,'TPasAnalyzer.AnalyzeModule missing Resolver');
  1703. if FUsedElements.Count>0 then
  1704. RaiseInconsistency(20170315153243,'');
  1705. ScopeModule:=aModule;
  1706. if (aModule is TPasProgram) or (aModule is TPasLibrary) then
  1707. Mode:=paumAllExports
  1708. else
  1709. Mode:=paumAllPublic;
  1710. UseModule(aModule,Mode);
  1711. {$IFDEF VerbosePasAnalyzer}
  1712. writeln('TPasAnalyzer.AnalyzeModule END ',GetElModName(aModule));
  1713. {$ENDIF}
  1714. end;
  1715. procedure TPasAnalyzer.AnalyzeWholeProgram(aStartModule: TPasProgram);
  1716. begin
  1717. {$IFDEF VerbosePasAnalyzer}
  1718. writeln('TPasAnalyzer.AnalyzeWholeProgram START ',GetElModName(aStartModule));
  1719. {$ENDIF}
  1720. if Resolver=nil then
  1721. RaiseInconsistency(20170315153201,'TPasAnalyzer.AnalyzeWholeProgram missing Resolver');
  1722. if FUsedElements.Count>0 then
  1723. RaiseInconsistency(20170315153252,'');
  1724. ScopeModule:=nil;
  1725. UseModule(aStartModule,paumAllExports);
  1726. {$IFDEF VerbosePasAnalyzer}
  1727. writeln('TPasAnalyzer.AnalyzeWholeProgram END ',GetElModName(aStartModule));
  1728. {$ENDIF}
  1729. end;
  1730. procedure TPasAnalyzer.EmitModuleHints(aModule: TPasModule);
  1731. begin
  1732. {$IFDEF VerbosePasAnalyzer}
  1733. writeln('TPasAnalyzer.EmitModuleHints ',GetElModName(aModule));
  1734. {$ENDIF}
  1735. if aModule.ClassType=TPasProgram then
  1736. EmitSectionHints(TPasProgram(aModule).ProgramSection)
  1737. else if aModule.ClassType=TPasLibrary then
  1738. EmitSectionHints(TPasLibrary(aModule).LibrarySection)
  1739. else
  1740. begin
  1741. // unit
  1742. EmitSectionHints(aModule.InterfaceSection);
  1743. EmitSectionHints(aModule.ImplementationSection);
  1744. end;
  1745. //EmitBlockHints(aModule.InitializationSection);
  1746. //EmitBlockHints(aModule.FinalizationSection);
  1747. end;
  1748. function TPasAnalyzer.FindElement(El: TPasElement): TPAElement;
  1749. var
  1750. Node: TAVLTreeNode;
  1751. begin
  1752. Node:=FindNode(El);
  1753. if Node=nil then
  1754. Result:=nil
  1755. else
  1756. Result:=TPAElement(Node.Data);
  1757. end;
  1758. function TPasAnalyzer.IsUsed(El: TPasElement): boolean;
  1759. var
  1760. ProcScope: TPasProcedureScope;
  1761. begin
  1762. if not IsIdentifier(El) then exit(true);
  1763. if El is TPasProcedure then
  1764. begin
  1765. ProcScope:=El.CustomData as TPasProcedureScope;
  1766. if ProcScope.DeclarationProc<>nil then
  1767. El:=ProcScope.DeclarationProc;
  1768. end;
  1769. Result:=FindElement(El)<>nil;
  1770. end;
  1771. function TPasAnalyzer.IsTypeInfoUsed(El: TPasElement): boolean;
  1772. begin
  1773. Result:=FChecked[paumPublished].Find(El)<>nil;
  1774. end;
  1775. function TPasAnalyzer.IsModuleInternal(El: TPasElement): boolean;
  1776. begin
  1777. if El=nil then
  1778. exit(true);
  1779. if El.ClassType=TInterfaceSection then
  1780. exit(false);
  1781. if IsExport(El) then exit(false);
  1782. case El.Visibility of
  1783. visPrivate,visStrictPrivate: exit(true);
  1784. visPublished: exit(false);
  1785. end;
  1786. Result:=IsModuleInternal(El.Parent);
  1787. end;
  1788. function TPasAnalyzer.IsExport(El: TPasElement): boolean;
  1789. begin
  1790. if El is TPasVariable then
  1791. Result:=[vmExport,vmPublic]*TPasVariable(El).VarModifiers<>[]
  1792. else if El is TPasProcedure then
  1793. Result:=[pmExport,pmPublic]*TPasProcedure(El).Modifiers<>[]
  1794. else
  1795. Result:=false;
  1796. end;
  1797. function TPasAnalyzer.IsIdentifier(El: TPasElement): boolean;
  1798. var
  1799. C: TClass;
  1800. begin
  1801. C:=El.ClassType;
  1802. Result:=C.InheritsFrom(TPasType)
  1803. or C.InheritsFrom(TPasVariable)
  1804. or C.InheritsFrom(TPasProcedure)
  1805. or C.InheritsFrom(TPasModule);
  1806. end;
  1807. function TPasAnalyzer.IsImplBlockEmpty(El: TPasImplBlock): boolean;
  1808. begin
  1809. Result:=true;
  1810. if (El=nil) or (El.Elements.Count=0) then exit;
  1811. Result:=false;
  1812. end;
  1813. procedure TPasAnalyzer.EmitMessage(const Id: int64;
  1814. const MsgType: TMessageType; MsgNumber: integer; Fmt: String;
  1815. const Args: array of const; PosEl: TPasElement);
  1816. var
  1817. Msg: TPAMessage;
  1818. begin
  1819. {$IFDEF VerbosePasAnalyzer}
  1820. //writeln('TPasAnalyzer.EmitMessage [',Id,'] ',MsgType,': (',MsgNumber,') Fmt={',Fmt,'} PosEl='+GetElModName(PosEl));
  1821. {$ENDIF}
  1822. Msg:=TPAMessage.Create;
  1823. Msg.Id:=Id;
  1824. Msg.MsgType:=MsgType;
  1825. Msg.MsgNumber:=MsgNumber;
  1826. Msg.MsgPattern:=Fmt;
  1827. Msg.MsgText:=SafeFormat(Fmt,Args);
  1828. CreateMsgArgs(Msg.Args,Args);
  1829. Msg.PosEl:=PosEl;
  1830. Msg.Filename:=PosEl.SourceFilename;
  1831. Resolver.UnmangleSourceLineNumber(PosEl.SourceLinenumber,Msg.Row,Msg.Col);
  1832. EmitMessage(Msg);
  1833. end;
  1834. procedure TPasAnalyzer.EmitMessage(Msg: TPAMessage);
  1835. begin
  1836. {$IFDEF VerbosePasAnalyzer}
  1837. writeln('TPasAnalyzer.EmitMessage [',Msg.Id,'] ',Msg.MsgType,': (',Msg.MsgNumber,') ',Msg.MsgText);
  1838. {$ENDIF}
  1839. try
  1840. OnMessage(Self,Msg);
  1841. finally
  1842. Msg.Release;
  1843. end;
  1844. end;
  1845. end.