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