Menu

[r2000]: / trunk / Src / UWarnings.pas  Maximize  Restore  History

Download this file

532 lines (483 with data), 18.1 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
{
* UWarnings.pas
*
* Classes and interfaces that encapsulate Delphi $WARN directives used to
* switch off unwanted compiler warnings.
*
* $Rev$
* $Date$
*
* ***** BEGIN LICENSE BLOCK *****
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is UWarnings.pas
*
* The Initial Developer of the Original Code is Peter Johnson
* (http://www.delphidabbler.com/).
*
* Portions created by the Initial Developer are Copyright (C) 2010-2012 Peter
* Johnson. All Rights Reserved.
*
* Contributor(s)
* NONE
*
* ***** END LICENSE BLOCK *****
}
unit UWarnings;
interface
uses
// Delphi
Generics.Collections,
// Project
IntfCommon, UBaseObjects, USettings;
type
/// <summary>Encapsulates information needed to generate a $WARN compiler
/// directive to enable or disable a particular Delphi warning type.
/// </summary>
/// <remarks>$WARN compiler directives apply to Delphi 6 and later.</remarks>
TWarning = record
strict private
var
/// <summary>Value of Symbol property.</summary>
fSymbol: string;
/// <summary>Value of MinCompiler property.</summary>
fMinCompiler: Single;
/// <summary>Value of State property.</summary>
fState: Boolean;
/// <summary>Read accessor for MinCompiler property.</summary>
function GetMinCompiler: Single;
/// <summary>Write accessor for MinCompiler property.</summary>
procedure SetMinCompiler(const Value: Single);
/// <summary>Read accessor for Symbol property.</summary>
function GetSymbol: string;
public
const
/// <summary>Version number of earliest compiler that supports $WARN
/// directive.</summary>
/// <remarks>This is Delphi 6.</remarks>
MinSupportedCompiler = 14.0;
public
/// <summary>Record constructor that supplies values for all properties.
/// </summary>
/// <param name="ASymbol">string [in] Warning symbol.</param>
/// <param name="AMinCompiler">Single [in] Version of earliest compiler
/// that supports ASymbol.</param>
/// <param name="AState">Boolean [in] Warning state: on or off.</param>
constructor Create(const ASymbol: string; const AMinCompiler: Single;
const AState: Boolean);
/// <summary>Checks if properties of this warning are valid.</summary>
function IsValid: Boolean;
/// <summary>Symbol used to identify this warning in $WARN compiler
/// directives.</summary>
property Symbol: string read GetSymbol;
/// <summary>Version number of earliest compiler that supports this
/// warning.</summary>
property MinCompiler: Single read GetMinCompiler write SetMinCompiler;
/// <summary>State of warning switch.</summary>
property State: Boolean read fState write fState;
end;
type
/// <summary>Interface supported by classes that maintain a list of Delphi
/// warnings and generate compiler directives to switch the warnings on or
/// off.</summary>
IWarnings = interface(IInterface)
['{EBE8C8BD-535D-4B4B-A6D4-1AFC02E1C5B7}']
/// <summary>Adds given warning to list.</summary>
procedure Add(const AWarning: TWarning);
/// <summary>Clears list of warnings.</summary>
procedure Clear;
/// <summary>Returns number of warnings in list.</summary>
function Count: Integer;
/// <summary>Checks whether warnings list is empty.</summary>
function IsEmpty: Boolean;
/// <summary>Checks if a warning with given symbol is present in warnings
/// list.</summary>
function Contains(const ASymbol: string): Boolean;
/// <summary>Deletes given warning from list.</summary>
/// <remarks>First warning with a matching symbol is deleted, regardless of
/// value of other properties.</remarks>
procedure Delete(const AWarning: TWarning);
/// <summary>Generates and returns source code for compiler directives that
/// enable or disable warnings in list, taking account of supporting
/// compilers.</summary>
function Render: string;
/// <summary>Read accessor for Items[] property.</summary>
function GetItem(const Idx: Integer): TWarning;
/// <summary>Indexed list of warnings.</summary>
property Items[const Idx: Integer]: TWarning read GetItem; default;
/// <summary>Read accessor for Enabled property.</summary>
function GetEnabled: Boolean;
/// <summary>Write accessor for Enabled property.</summary>
procedure SetEnabled(const Value: Boolean);
/// <summary>Indicates whether compiler directives should be emitted for
/// listed warnings.</summary>
property Enabled: Boolean read GetEnabled write SetEnabled;
/// <summary>Creates and returns an enumerator for the warnings list.
/// </summary>
/// <remarks>Caller is responsible for freeing the enumerator.</remarks>
function GetEnumerator: TEnumerator<TWarning>;
end;
type
/// <summary>Static class that can save and load an IWarnings object's data
/// to and from persistent storage.</summary>
TWarningsPersist = class(TNoConstructObject)
strict private
/// <summary>Constructs the name of a warning value in storage from given
/// index number and property name.</summary>
class function WarningCompoundName(const Idx: Integer; const Prop: string):
string;
public
/// <summary>Loads data from given persistent storage section into given
/// warnings object.</summary>
class procedure Load(Storage: ISettingsSection; Warnings: IWarnings);
/// <summary>Saves data from given warnings object to givenpersistent
/// storage section.</summary>
class procedure Save(Storage: ISettingsSection; Warnings: IWarnings);
end;
type
/// <summary>Class that encapsulates information about Delphi compiler
/// warnings and whether code can be generated to supress or enable them.
/// </summary>
TWarnings = class(TInterfacedObject, IWarnings, IAssignable)
strict private
var
/// <summary>List of warning records.</summary>
fItems: TList<TWarning>;
/// <summary>Value of Enabled property.</summary>
fEnabled: Boolean;
public
/// <summary>Constructs warnings object.</summary>
constructor Create;
/// <summary>Destroys warnings object.</summary>
destructor Destroy; override;
/// <summary>Creates a TWarnings instance containing default warnings.
/// </summary>
class function Defaults: TWarnings;
/// <summary>Adds given warning to list.</summary>
/// <remarks>Method of IWarnings.</remarks>
procedure Add(const AWarning: TWarning);
/// <summary>Clears list of warnings.</summary>
/// <remarks>Method of IWarnings.</remarks>
procedure Clear;
/// <summary>Returns number of warnings in list.</summary>
/// <remarks>Method of IWarnings.</remarks>
function Count: Integer;
/// <summary>Checks whether warnings list is empty.</summary>
/// <remarks>Method of IWarnings.</remarks>
function IsEmpty: Boolean;
/// <summary>Checks if a warning with given symbol is present in warnings
/// list.</summary>
/// <remarks>Method of IWarnings.</remarks>
function Contains(const ASymbol: string): Boolean;
/// <summary>Deletes given warning from list.</summary>
/// <remarks>
/// <para>First warning with a matching symbol is deleted, regardless of
/// value of other properties.</para>
/// <para>Method of IWarnings.</para>
/// </remarks>
procedure Delete(const AWarning: TWarning);
/// <summary>Generates and returns source code for compiler directives that
/// enable or disable warnings in list, taking account of supporting
/// compilers.</summary>
/// <remarks>Method of IWarnings.</remarks>
function Render: string;
/// <summary>Read accessor for Items[] property.</summary>
/// <remarks>Method of IWarnings.</remarks>
function GetItem(const Idx: Integer): TWarning;
/// <summary>Indexed list of warnings.</summary>
/// <remarks>Property of IWarnings.</remarks>
property Items[const Idx: Integer]: TWarning read GetItem; default;
/// <summary>Read accessor for Enabled property.</summary>
/// <remarks>Method of IWarnings.</remarks>
function GetEnabled: Boolean;
/// <summary>Write accessor for Enabled property.</summary>
/// <remarks>Method of IWarnings.</remarks>
procedure SetEnabled(const Value: Boolean);
/// <summary>Indicates whether compiler directives should be emitted for
/// listed warnings.</summary>
/// <remarks>Property of IWarnings.</remarks>
property Enabled: Boolean read GetEnabled write SetEnabled;
/// <summary>Creates and returns an enumerator for the warnings list.
/// </summary>
/// <remarks>
/// <para>Caller is responsible for freeing the enumerator.</para>
/// <para>Method of IWarnings.</para>
/// </remarks>
function GetEnumerator: TEnumerator<TWarning>;
/// <summary>Assigns properties of another IWarnings instance to this
/// object.</summary>
/// <remarks>Method of IAssignable.</remarks>
procedure Assign(const Src: IInterface);
end;
implementation
uses
// Delphi
SysUtils, Generics.Defaults, Math,
// Project
UConsts, UExceptions, UStrUtils;
{ TWarning }
constructor TWarning.Create(const ASymbol: string; const AMinCompiler: Single;
const AState: Boolean);
begin
Assert(ASymbol <> '', 'TWarning.Create: ASymbol is empty string');
fSymbol := ASymbol;
MinCompiler := AMinCompiler;
fState := AState;
end;
function TWarning.GetMinCompiler: Single;
begin
Assert(fMinCompiler >= MinSupportedCompiler,
'TWarning.GetMinCompiler: fMinCompiler too small');
Result := fMinCompiler;
end;
function TWarning.GetSymbol: string;
begin
Assert(fSymbol <> '', 'TWarning.GetSymbol: fSymbol is empty string');
Result := fSymbol;
end;
function TWarning.IsValid: Boolean;
begin
Result := (fMinCompiler >= MinSupportedCompiler) and (fSymbol <> '');
end;
procedure TWarning.SetMinCompiler(const Value: Single);
begin
Assert(Value >= MinSupportedCompiler,
'TWarning.SetMinCompiler: AValue too small');
fMinCompiler := Value;
end;
{ TWarnings }
procedure TWarnings.Add(const AWarning: TWarning);
begin
Assert(AWarning.IsValid, ClassName + '.Add: AWarning not valid');
if fItems.Contains(AWarning) then
raise EBug.CreateFmt(
'%s.Add: AWarning %s already in list', [ClassName, AWarning.Symbol]
);
fItems.Add(AWarning);
end;
procedure TWarnings.Assign(const Src: IInterface);
var
W: TWarning; // references each in warning in Src.
begin
Clear;
for W in (Src as IWarnings) do
Add(W);
fEnabled := (Src as IWarnings).Enabled;
end;
procedure TWarnings.Clear;
begin
fItems.Clear;
end;
function TWarnings.Contains(const ASymbol: string): Boolean;
begin
Result := fItems.Contains(
// use fake warning: we only use Symbol property in search
TWarning.Create(ASymbol, TWarning.MinSupportedCompiler, False)
);
end;
function TWarnings.Count: Integer;
begin
Result := fItems.Count;
end;
constructor TWarnings.Create;
begin
inherited Create;
// use generic list that sorts on warning's symbol to store warnings
fItems := TList<TWarning>.Create(
TDelegatedComparer<TWarning>.Create(
function(const Left, Right: TWarning): Integer
begin
Result := StrCompareText(Left.Symbol, Right.Symbol);
end
)
);
end;
class function TWarnings.Defaults: TWarnings;
begin
Result := Create;
Result.Add(TWarning.Create('UNSAFE_TYPE', 15.0, False));
Result.Add(TWarning.Create('UNSAFE_CAST', 15.0, False));
Result.Add(TWarning.Create('UNSAFE_CODE', 15.0, False));
Result.Add(TWarning.Create('SYMBOL_PLATFORM', 14.0, False));
Result.Add(TWarning.Create('SYMBOL_DEPRECATED', 14.0, False));
Result.Add(TWarning.Create('SYMBOL_LIBRARY', 14.0, False));
Result.Add(TWarning.Create('IMPLICIT_STRING_CAST', 20.0, False));
Result.Add(TWarning.Create('EXPLICIT_STRING_CAST', 20.0, False));
end;
procedure TWarnings.Delete(const AWarning: TWarning);
begin
fItems.Remove(AWarning);
end;
destructor TWarnings.Destroy;
begin
fItems.Free;
inherited;
end;
function TWarnings.GetEnabled: Boolean;
begin
Result := fEnabled;
end;
function TWarnings.GetEnumerator: TEnumerator<TWarning>;
begin
Result := fItems.GetEnumerator;
end;
function TWarnings.GetItem(const Idx: Integer): TWarning;
begin
Result := fItems[Idx];
end;
function TWarnings.IsEmpty: Boolean;
begin
Result := Count = 0;
end;
function TWarnings.Render: string;
var
SB: TStringBuilder; // used to construct source code string
W: TWarning; // each warning in list
SortedList: TList<TWarning>; // list of warnings sorted by compiler version
CurrentVer: Single; // compiler version currently being processed
InsideVer: Boolean; // true if rendering warnings for a compiler ver
const
// values written to compiler directive, depending on warning state
StateStrings: array[Boolean] of string = ('OFF', 'ON');
begin
if not Enabled or IsEmpty then
Exit('');
// Create a list of warnings sorted by minimum compiler: we do this so we can
// group related warnings together in one conditional statement for each
// minimum compiler version
SortedList := TList<TWarning>.Create(
TDelegatedComparer<TWarning>.Create(
// sort TWarning records by MinCompiler field
function(const Left, Right: TWarning): Integer
begin
Result := Math.CompareValue(Left.MinCompiler, Right.MinCompiler);
end
)
);
try
for W in fItems do
SortedList.Add(W);
SortedList.Sort;
// Generate the source code
CurrentVer := 0.0;
InsideVer := False;
SB := TStringBuilder.Create;
try
SB.AppendLine('{$IFNDEF FPC}');
SB.AppendLine(' {$IFDEF CONDITIONALEXPRESSIONS}');
for W in SortedList do
begin
if not Math.SameValue(W.MinCompiler, CurrentVer) then
begin
// required compiler version has changed
if InsideVer then
begin
// we were writing warnings for previous version: close statement
SB.AppendLine(' {$IFEND}');
InsideVer := False;
end;
// create new condition for new version
SB.AppendFormat(
' {$IF CompilerVersion >= %.2f}' + EOL, [W.MinCompiler]
);
InsideVer := True;
CurrentVer := W.MinCompiler;
end;
// write directive to turn warning off
SB.AppendFormat(
' {$WARN %0:s %1:s}' + EOL, [W.Symbol, StateStrings[W.State]]
);
end;
// close off any open conditional statement
if InsideVer then
SB.AppendLine(' {$IFEND}');
// close bounding $IFDEFs
SB.AppendLine(' {$ENDIF}');
SB.AppendLine('{$ENDIF}');
Result := SB.ToString;
finally
SB.Free;
end;
finally
SortedList.Free;
end;
end;
procedure TWarnings.SetEnabled(const Value: Boolean);
begin
fEnabled := Value;
end;
{ TWarningsPersist }
const
// Names of values stored in persistent storage
cWarningsEnabledName = 'SwitchOffWarnings'; //! this name is historical
cWarningCountName = 'WarningCount';
cWarningCompoundName = 'Warning%d.%s';
// Names of properties used in compound value names
cWarningSymbolProp = 'Symbol';
cWarningSupportProp = 'MinCompiler';
cWarningStateProp = 'State';
class procedure TWarningsPersist.Load(Storage: ISettingsSection;
Warnings: IWarnings);
var
Idx: Integer; // loops thru all warnings in storage
CompilerVer: Double; // min compiler version for a warning read from storage
Symbol: string; // symbol of a warning read from storage
State: Boolean; // state of a warning read from storage
begin
Warnings.Clear;
Warnings.Enabled := Boolean(
StrToIntDef(Storage.ItemValues[cWarningsEnabledName], Ord(False))
);
for Idx := 0 to Pred(StrToIntDef(Storage.ItemValues[cWarningCountName], 0)) do
begin
Symbol := Storage.ItemValues[WarningCompoundName(Idx, cWarningSymbolProp)];
if (Symbol = '') or Warnings.Contains(Symbol) then
Continue;
CompilerVer := StrToFloatDef(
Storage.ItemValues[WarningCompoundName(Idx, cWarningSupportProp)],
TWarning.MinSupportedCompiler
);
if CompilerVer < TWarning.MinSupportedCompiler then
CompilerVer := TWarning.MinSupportedCompiler;
State := Boolean(
StrToIntDef(
Storage.ItemValues[WarningCompoundName(Idx, cWarningStateProp)],
Ord(False)
)
);
Warnings.Add(TWarning.Create(Symbol, CompilerVer, State));
end;
end;
class procedure TWarningsPersist.Save(Storage: ISettingsSection;
Warnings: IWarnings);
var
Idx: Integer; // loops through all warnings
begin
Storage.ItemValues[cWarningsEnabledName] := IntToStr(Ord(Warnings.Enabled));
Storage.ItemValues[cWarningCountName] := IntToStr(Warnings.Count);
for Idx := 0 to Pred(Warnings.Count) do
begin
Storage.ItemValues[WarningCompoundName(Idx, cWarningSymbolProp)] :=
Warnings[Idx].Symbol;
Storage.ItemValues[WarningCompoundName(Idx, cWarningSupportProp)] :=
Format('%.2f', [Warnings[Idx].MinCompiler]);
Storage.ItemValues[WarningCompoundName(Idx, cWarningStateProp)] :=
IntToStr(Ord(Warnings[Idx].State));
end;
Storage.Save;
end;
class function TWarningsPersist.WarningCompoundName(const Idx: Integer;
const Prop: string): string;
begin
Result := Format(cWarningCompoundName, [Idx, Prop]);
end;
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.