2005-03-02 Marek Safar <marek.safar@seznam.cz>
[mono.git] / mcs / mcs / report.cs
1 //
2 // report.cs: report errors and warnings.
3 //
4 // Author: Miguel de Icaza (miguel@ximian.com)
5 //
6 // (C) 2001 Ximian, Inc. (http://www.ximian.com)
7 //
8
9 //
10 // FIXME: currently our class library does not support custom number format strings
11 //
12 using System;
13 using System.Text;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Diagnostics;
17 using System.Reflection;
18 using System.Reflection.Emit;
19
20 namespace Mono.CSharp {
21
22         /// <summary>
23         ///   This class is used to report errors and warnings t te user.
24         /// </summary>
25         public class Report {
26                 /// <summary>  
27                 ///   Errors encountered so far
28                 /// </summary>
29                 static public int Errors;
30
31                 /// <summary>  
32                 ///   Warnings encountered so far
33                 /// </summary>
34                 static public int Warnings;
35
36                 /// <summary>  
37                 ///   Whether errors should be throw an exception
38                 /// </summary>
39                 static public bool Fatal;
40                 
41                 /// <summary>  
42                 ///   Whether warnings should be considered errors
43                 /// </summary>
44                 static public bool WarningsAreErrors;
45
46                 /// <summary>  
47                 ///   Whether to dump a stack trace on errors. 
48                 /// </summary>
49                 static public bool Stacktrace;
50                 
51                 //
52                 // If the 'expected' error code is reported then the
53                 // compilation succeeds.
54                 //
55                 // Used for the test suite to excercise the error codes
56                 //
57                 static int expected_error = 0;
58
59                 //
60                 // Keeps track of the warnings that we are ignoring
61                 //
62                 static Hashtable warning_ignore_table;
63
64                 static Hashtable warning_regions_table;
65
66                 /// <summary>
67                 /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported.
68                 /// </summary>
69                 static StringCollection extra_information = new StringCollection ();
70
71                 abstract class AbstractMessage {
72
73                         static void Check (int code)
74                         {
75                                 if (code == expected_error) {
76                                         Environment.Exit (0);
77                                 }
78                         }
79
80                         public abstract bool IsWarning { get; }
81
82                         public abstract string MessageType { get; }
83
84                         public virtual void Print (int code, string location, string text)
85                         {
86                                 if (code < 0)
87                                         code = 8000-code;
88
89                                 StringBuilder msg = new StringBuilder ();
90                                 if (location.Length != 0) {
91                                         msg.Append (location);
92                                         msg.Append (' ');
93                                 }
94                                 msg.AppendFormat ("{0} CS{1:0000}: {2}", MessageType, code, text);
95                                 Console.Error.WriteLine (msg.ToString ());
96
97                                 foreach (string s in extra_information) 
98                                         Console.Error.WriteLine (s + MessageType);
99
100                                 extra_information.Clear ();
101
102                                 if (Stacktrace)
103                                         Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
104
105                                 if (Fatal) {
106                                         if (!IsWarning || WarningsAreErrors)
107                                                 throw new Exception (text);
108                                 }
109
110                                 Check (code);
111                         }
112
113                         public virtual void Print (int code, Location location, string text)
114                         {
115                                 if (location.Equals (Location.Null)) {
116                                         Print (code, "", text);
117                                         return;
118                                 }
119                                 Print (code, String.Format ("{0}({1})", location.Name, location.Row), text);
120                         }
121                 }
122
123                 sealed class WarningMessage: AbstractMessage {
124                         Location loc = Location.Null;
125                         readonly int Level;
126
127                         public WarningMessage ():
128                                 this (-1) {}
129
130                         public WarningMessage (int level)
131                         {
132                                 Level = level;
133                         }
134
135                         public override bool IsWarning {
136                                 get { return true; }
137                         }
138
139                         bool IsEnabled (int code)
140                         {
141                                 if (RootContext.WarningLevel < Level)
142                                         return false;
143
144                                 if (warning_ignore_table != null) {
145                                         if (warning_ignore_table.Contains (code)) {
146                                                 return false;
147                                         }
148                                 }
149
150                                 if (warning_regions_table == null || loc.Equals (Location.Null))
151                                         return true;
152
153                                 WarningRegions regions = (WarningRegions)warning_regions_table [loc.Name];
154                                 if (regions == null)
155                                         return true;
156
157                                 return regions.IsWarningEnabled (code, loc.Row);
158                         }
159
160                         public override void Print(int code, string location, string text)
161                         {
162                                 if (!IsEnabled (code)) {
163                                         extra_information.Clear ();
164                                         return;
165                                 }
166
167                                 if (WarningsAreErrors) {
168                                         new ErrorMessage ().Print (code, location, text);
169                                         return;
170                                 }
171
172                                 Warnings++;
173                                 base.Print (code, location, text);
174                         }
175
176                         public override void Print(int code, Location location, string text)
177                         {
178                                 loc = location;
179                                 base.Print (code, location, text);
180                         }
181
182                         public override string MessageType {
183                                 get {
184                                         return "warning";
185                                 }
186                         }
187                 }
188
189                 sealed class ErrorMessage: AbstractMessage {
190
191                         public override void Print(int code, string location, string text)
192                         {
193                                 Errors++;
194                                 base.Print (code, location, text);
195                         }
196
197                         public override bool IsWarning {
198                                 get { return false; }
199                         }
200
201                         public override string MessageType {
202                                 get {
203                                         return "error";
204                                 }
205                         }
206
207                 }
208
209                 public static void FeatureIsNotStandardized (Location loc, string feature)
210                 {
211                         Report.Error (1644, loc, "Feature '{0}' cannot be used because it is not part of the standardized ISO C# language specification", feature);
212                 }
213                 
214                 public static string FriendlyStackTrace (Exception e)
215                 {
216                         return FriendlyStackTrace (new StackTrace (e, true));
217                 }
218                 
219                 static string FriendlyStackTrace (StackTrace t)
220                 {               
221                         StringBuilder sb = new StringBuilder ();
222                         
223                         bool foundUserCode = false;
224                         
225                         for (int i = 0; i < t.FrameCount; i++) {
226                                 StackFrame f = t.GetFrame (i);
227                                 MethodBase mb = f.GetMethod ();
228                                 
229                                 if (!foundUserCode && mb.ReflectedType == typeof (Report))
230                                         continue;
231                                 
232                                 foundUserCode = true;
233                                 
234                                 sb.Append ("\tin ");
235                                 
236                                 if (f.GetFileLineNumber () > 0)
237                                         sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ());
238                                 
239                                 sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name);
240                                 
241                                 bool first = true;
242                                 foreach (ParameterInfo pi in mb.GetParameters ()) {
243                                         if (!first)
244                                                 sb.Append (", ");
245                                         first = false;
246                                         
247                                         sb.Append (TypeManager.CSharpName (pi.ParameterType));
248                                 }
249                                 sb.Append (")\n");
250                         }
251         
252                         return sb.ToString ();
253                 }
254
255                 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
256                 // IF YOU ADD A NEW WARNING YOU HAVE TO DUPLICATE ITS ID HERE
257                 //
258                 public static bool IsValidWarning (int code)
259                 {
260                         int[] all_warnings = new int[] {
261                                 28, 67, 78, 105, 108, 109, 114, 192, 168, 169, 183, 184, 219, 251, 612, 618, 626, 628, 642, 649,
262                                 659, 660, 661, 672, 1030, 1522, 1616, 1691, 1692, 1901, 2002, 2023, 3012, 3019, 8024, 8028
263                         };
264                         
265                         foreach (int i in all_warnings) {
266                                 if (i == code)
267                                         return true;
268                         }
269                         return false;
270                 }
271                 
272                 static public void LocationOfPreviousError (Location loc)
273                 {
274                         Console.Error.WriteLine (String.Format ("{0}({1}) (Location of symbol related to previous error)", loc.Name, loc.Row));
275                 }    
276         
277                 static public void RuntimeMissingSupport (Location loc, string feature) 
278                 {
279                         Report.Error (-88, loc, "Your .NET Runtime does not support '{0}'. Please use the latest Mono runtime instead.", feature);
280                 }
281
282                 /// <summary>
283                 /// In most error cases is very useful to have information about symbol that caused the error.
284                 /// Call this method before you call Report.Error when it makes sense.
285                 /// </summary>
286                 static public void SymbolRelatedToPreviousError (Location loc, string symbol)
287                 {
288                         SymbolRelatedToPreviousError (String.Format ("{0}({1})", loc.Name, loc.Row), symbol);
289                 }
290
291                 static public void SymbolRelatedToPreviousError (MemberInfo mi)
292                 {
293                         TypeContainer temp_ds = TypeManager.LookupTypeContainer (mi.DeclaringType);
294                         if (temp_ds == null) {
295                                 SymbolRelatedToPreviousError (mi.DeclaringType.Assembly.Location, TypeManager.GetFullNameSignature (mi));
296                         } else {
297                                 if (mi is MethodBase) {
298                                         IMethodData md = TypeManager.GetMethod ((MethodBase)mi);
299                                         SymbolRelatedToPreviousError (md.Location, md.GetSignatureForError (temp_ds));
300                                         return;
301                                 }
302
303                                 string name = String.Concat (temp_ds.Name, ".", mi.Name);
304                                 MemberCore mc = temp_ds.GetDefinition (name);
305                                 SymbolRelatedToPreviousError (mc);
306                         }
307                 }
308
309                 static public void SymbolRelatedToPreviousError (MemberCore mc)
310                 {
311                         SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
312                 }
313
314                 static public void SymbolRelatedToPreviousError (Type type)
315                 {
316                         if (type is TypeBuilder) {
317                                 DeclSpace temp_ds = TypeManager.LookupDeclSpace (type);
318                                 SymbolRelatedToPreviousError (temp_ds.Location, TypeManager.CSharpName (type));
319                         } else if (type.HasElementType) {
320                                 SymbolRelatedToPreviousError (type.GetElementType ());
321                         } else {
322                                 SymbolRelatedToPreviousError (type.Assembly.Location, TypeManager.CSharpName (type));
323                         }
324                 }
325
326                 static void SymbolRelatedToPreviousError (string loc, string symbol)
327                 {
328                         extra_information.Add (String.Format ("{0}: '{1}' (name of symbol related to previous ", loc, symbol));
329                 }
330
331                 public static void ExtraInformation (Location loc, string msg)
332                 {
333                         extra_information.Add (String.Format ("{0}({1}) {2}", loc.Name, loc.Row, msg));
334                 }
335
336                 public static WarningRegions RegisterWarningRegion (Location location)
337                 {
338                         if (warning_regions_table == null)
339                                 warning_regions_table = new Hashtable ();
340
341                         WarningRegions regions = (WarningRegions)warning_regions_table [location.Name];
342                         if (regions == null) {
343                                 regions = new WarningRegions ();
344                                 warning_regions_table.Add (location.Name, regions);
345                         }
346                         return regions;
347                 }
348
349                 static public void Warning (int code, int level, Location loc, string format, params object[] args)
350                 {
351                         WarningMessage w = new WarningMessage (level);
352                         w.Print (code, loc, String.Format (format, args));
353                 }
354
355                 static public void Warning (int code, Location loc, string format, params object[] args)
356                 {
357                         WarningMessage w = new WarningMessage ();
358                         w.Print (code, loc, String.Format (format, args));
359                 }
360
361                 static public void Warning (int code, string format, params object[] args)
362                 {
363                         Warning (code, Location.Null, String.Format (format, args));
364                 }
365
366                 /// <summary>
367                 /// Did you test your WarningLevel, that you use this method
368                 /// </summary>
369                 static public void Warning (int code, string text)
370                 {
371                         Warning (code, Location.Null, text);
372                 }
373
374                 static public void Error (int code, string format, params object[] args)
375                 {
376                         Error (code, Location.Null, String.Format (format, args));
377                 }
378
379                 static public void Error (int code, Location loc, string format, params object[] args)
380                 {
381                         ErrorMessage e = new ErrorMessage ();
382                         e.Print (code, loc, String.Format (format, args));
383                 }
384
385                 static public void SetIgnoreWarning (int code)
386                 {
387                         if (warning_ignore_table == null)
388                                 warning_ignore_table = new Hashtable ();
389
390                         warning_ignore_table [code] = true;
391                 }
392                 
393                 static public int ExpectedError {
394                         set {
395                                 expected_error = value;
396                         }
397                         get {
398                                 return expected_error;
399                         }
400                 }
401
402                 public static int DebugFlags = 0;
403
404                 [Conditional ("MCS_DEBUG")]
405                 static public void Debug (string message, params object[] args)
406                 {
407                         Debug (4, message, args);
408                 }
409                         
410                 [Conditional ("MCS_DEBUG")]
411                 static public void Debug (int category, string message, params object[] args)
412                 {
413                         if ((category & DebugFlags) == 0)
414                                 return;
415
416                         StringBuilder sb = new StringBuilder (message);
417
418                         if ((args != null) && (args.Length > 0)) {
419                                 sb.Append (": ");
420
421                                 bool first = true;
422                                 foreach (object arg in args) {
423                                         if (first)
424                                                 first = false;
425                                         else
426                                                 sb.Append (", ");
427                                         if (arg == null)
428                                                 sb.Append ("null");
429                                         else if (arg is ICollection)
430                                                 sb.Append (PrintCollection ((ICollection) arg));
431                                         else
432                                                 sb.Append (arg);
433                                 }
434                         }
435
436                         Console.WriteLine (sb.ToString ());
437                 }
438
439                 static public string PrintCollection (ICollection collection)
440                 {
441                         StringBuilder sb = new StringBuilder ();
442
443                         sb.Append (collection.GetType ());
444                         sb.Append ("(");
445
446                         bool first = true;
447                         foreach (object o in collection) {
448                                 if (first)
449                                         first = false;
450                                 else
451                                         sb.Append (", ");
452                                 sb.Append (o);
453                         }
454
455                         sb.Append (")");
456                         return sb.ToString ();
457                 }
458         }
459
460         public enum TimerType {
461                 FindMembers     = 0,
462                 TcFindMembers   = 1,
463                 MemberLookup    = 2,
464                 CachedLookup    = 3,
465                 CacheInit       = 4,
466                 MiscTimer       = 5,
467                 CountTimers     = 6
468         }
469
470         public enum CounterType {
471                 FindMembers     = 0,
472                 MemberCache     = 1,
473                 MiscCounter     = 2,
474                 CountCounters   = 3
475         }
476
477         public class Timer
478         {
479                 static DateTime[] timer_start;
480                 static TimeSpan[] timers;
481                 static long[] timer_counters;
482                 static long[] counters;
483
484                 static Timer ()
485                 {
486                         timer_start = new DateTime [(int) TimerType.CountTimers];
487                         timers = new TimeSpan [(int) TimerType.CountTimers];
488                         timer_counters = new long [(int) TimerType.CountTimers];
489                         counters = new long [(int) CounterType.CountCounters];
490
491                         for (int i = 0; i < (int) TimerType.CountTimers; i++) {
492                                 timer_start [i] = DateTime.Now;
493                                 timers [i] = TimeSpan.Zero;
494                         }
495                 }
496
497                 [Conditional("TIMER")]
498                 static public void IncrementCounter (CounterType which)
499                 {
500                         ++counters [(int) which];
501                 }
502
503                 [Conditional("TIMER")]
504                 static public void StartTimer (TimerType which)
505                 {
506                         timer_start [(int) which] = DateTime.Now;
507                 }
508
509                 [Conditional("TIMER")]
510                 static public void StopTimer (TimerType which)
511                 {
512                         timers [(int) which] += DateTime.Now - timer_start [(int) which];
513                         ++timer_counters [(int) which];
514                 }
515
516                 [Conditional("TIMER")]
517                 static public void ShowTimers ()
518                 {
519                         ShowTimer (TimerType.FindMembers, "- FindMembers timer");
520                         ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer");
521                         ShowTimer (TimerType.MemberLookup, "- MemberLookup timer");
522                         ShowTimer (TimerType.CachedLookup, "- CachedLookup timer");
523                         ShowTimer (TimerType.CacheInit, "- Cache init");
524                         ShowTimer (TimerType.MiscTimer, "- Misc timer");
525
526                         ShowCounter (CounterType.FindMembers, "- Find members");
527                         ShowCounter (CounterType.MemberCache, "- Member cache");
528                         ShowCounter (CounterType.MiscCounter, "- Misc counter");
529                 }
530
531                 static public void ShowCounter (CounterType which, string msg)
532                 {
533                         Console.WriteLine ("{0} {1}", counters [(int) which], msg);
534                 }
535
536                 static public void ShowTimer (TimerType which, string msg)
537                 {
538                         Console.WriteLine (
539                                 "[{0:00}:{1:000}] {2} (used {3} times)",
540                                 (int) timers [(int) which].TotalSeconds,
541                                 timers [(int) which].Milliseconds, msg,
542                                 timer_counters [(int) which]);
543                 }
544         }
545
546         public class InternalErrorException : Exception {
547                 public InternalErrorException ()
548                         : base ("Internal error")
549                 {
550                 }
551
552                 public InternalErrorException (string message)
553                         : base (message)
554                 {
555                 }
556         }
557
558         /// <summary>
559         /// Handles #pragma warning
560         /// </summary>
561         public class WarningRegions {
562
563                 abstract class PragmaCmd
564                 {
565                         public int Line;
566
567                         protected PragmaCmd (int line)
568                         {
569                                 Line = line;
570                         }
571
572                         public abstract bool IsEnabled (int code, bool previous);
573                 }
574                 
575                 class Disable: PragmaCmd
576                 {
577                         int code;
578                         public Disable (int line, int code)
579                                 : base (line)
580                         {
581                                 this.code = code;
582                         }
583
584                         public override bool IsEnabled (int code, bool previous)
585                         {
586                                 return this.code == code ? false : previous;
587                         }
588                 }
589
590                 class DisableAll: PragmaCmd
591                 {
592                         public DisableAll (int line)
593                                 : base (line) {}
594
595                         public override bool IsEnabled(int code, bool previous)
596                         {
597                                 return false;
598                         }
599                 }
600
601                 class Enable: PragmaCmd
602                 {
603                         int code;
604                         public Enable (int line, int code)
605                                 : base (line)
606                         {
607                                 this.code = code;
608                         }
609
610                         public override bool IsEnabled(int code, bool previous)
611                         {
612                                 return this.code == code ? true : previous;
613                         }
614                 }
615
616                 class EnableAll: PragmaCmd
617                 {
618                         public EnableAll (int line)
619                                 : base (line) {}
620
621                         public override bool IsEnabled(int code, bool previous)
622                         {
623                                 return true;
624                         }
625                 }
626
627
628                 ArrayList regions = new ArrayList ();
629
630                 public void WarningDisable (int line)
631                 {
632                         regions.Add (new DisableAll (line));
633                 }
634
635                 public void WarningDisable (Location location, int code)
636                 {
637                         if (CheckWarningCode (code, location))
638                                 regions.Add (new Disable (location.Row, code));
639                 }
640
641                 public void WarningEnable (int line)
642                 {
643                         regions.Add (new EnableAll (line));
644                 }
645
646                 public void WarningEnable (Location location, int code)
647                 {
648                         if (CheckWarningCode (code, location))
649                                 regions.Add (new Enable (location.Row, code));
650                 }
651
652                 public bool IsWarningEnabled (int code, int src_line)
653                 {
654                         bool result = true;
655                         foreach (PragmaCmd pragma in regions) {
656                                 if (src_line < pragma.Line)
657                                         break;
658
659                                 result = pragma.IsEnabled (code, result);
660                         }
661                         return result;
662                 }
663
664                 bool CheckWarningCode (int code, Location loc)
665                 {
666                         if (Report.IsValidWarning (code))
667                                 return true;
668
669                         Report.Warning (1691, 1, loc, "'{0}' is not a valid warning number", code);
670                         return false;
671                 }
672         }
673 }