2005-05-06 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / gmcs / 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.LookupGenericTypeContainer (mi.DeclaringType);
305                         if (temp_ds == null) {
306                                 SymbolRelatedToPreviousError (mi.DeclaringType.Assembly.Location, TypeManager.GetFullNameSignature (mi));
307                         } else {
308                                 MethodBase mb = mi as MethodBase;
309                                 if (mb != null) {
310                                         while (mb.Mono_IsInflatedMethod)
311                                                 mb = mb.GetGenericMethodDefinition ();
312                                         IMethodData md = TypeManager.GetMethod (mb);
313                                         SymbolRelatedToPreviousError (md.Location, md.GetSignatureForError (temp_ds));
314                                         return;
315                                 }
316
317                                 MemberCore mc = temp_ds.GetDefinition (mi.Name);
318                                 SymbolRelatedToPreviousError (mc);
319                         }
320                 }
321
322                 static public void SymbolRelatedToPreviousError (MemberCore mc)
323                 {
324                         SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
325                 }
326
327                 static public void SymbolRelatedToPreviousError (Type type)
328                 {
329                         if (type.IsGenericInstance)
330                                 type = type.GetGenericTypeDefinition ();
331                         if (type is TypeBuilder) {
332                                 DeclSpace temp_ds = TypeManager.LookupDeclSpace (type);
333                                 SymbolRelatedToPreviousError (temp_ds.Location, TypeManager.CSharpName (type));
334                         } else if (type.HasElementType) {
335                                 SymbolRelatedToPreviousError (type.GetElementType ());
336                         } else {
337                                 SymbolRelatedToPreviousError (type.Assembly.Location, TypeManager.CSharpName (type));
338                         }
339                 }
340
341                 static void SymbolRelatedToPreviousError (string loc, string symbol)
342                 {
343                         extra_information.Add (String.Format ("{0}: '{1}' (name of symbol related to previous ", loc, symbol));
344                 }
345
346                 public static void ExtraInformation (Location loc, string msg)
347                 {
348                         extra_information.Add (String.Format ("{0}({1}) {2}", loc.Name, loc.Row, msg));
349                 }
350
351                 public static WarningRegions RegisterWarningRegion (Location location)
352                 {
353                         if (warning_regions_table == null)
354                                 warning_regions_table = new Hashtable ();
355
356                         WarningRegions regions = (WarningRegions)warning_regions_table [location.Name];
357                         if (regions == null) {
358                                 regions = new WarningRegions ();
359                                 warning_regions_table.Add (location.Name, regions);
360                         }
361                         return regions;
362                 }
363
364                 static public void Warning (int code, int level, Location loc, string format, params object[] args)
365                 {
366                         WarningMessage w = new WarningMessage (level);
367                         w.Print (code, loc, String.Format (format, args));
368                 }
369
370                 static public void Warning (int code, Location loc, string format, params object[] args)
371                 {
372                         WarningMessage w = new WarningMessage ();
373                         w.Print (code, loc, String.Format (format, args));
374                 }
375
376                 static public void Warning (int code, string format, params object[] args)
377                 {
378                         Warning (code, Location.Null, String.Format (format, args));
379                 }
380
381                 /// <summary>
382                 /// Did you test your WarningLevel, that you use this method
383                 /// </summary>
384                 static public void Warning (int code, string text)
385                 {
386                         Warning (code, Location.Null, text);
387                 }
388
389                 static public void Error (int code, string format, params object[] args)
390                 {
391                         Error (code, Location.Null, String.Format (format, args));
392                 }
393
394                 static public void Error (int code, Location loc, string format, params object[] args)
395                 {
396                         ErrorMessage e = new ErrorMessage ();
397                         e.Print (code, loc, String.Format (format, args));
398                 }
399
400                 static public void SetIgnoreWarning (int code)
401                 {
402                         if (warning_ignore_table == null)
403                                 warning_ignore_table = new Hashtable ();
404
405                         warning_ignore_table [code] = true;
406                 }
407                 
408                 static public int ExpectedError {
409                         set {
410                                 expected_error = value;
411                         }
412                         get {
413                                 return expected_error;
414                         }
415                 }
416
417                 public static int DebugFlags = 0;
418
419                 [Conditional ("MCS_DEBUG")]
420                 static public void Debug (string message, params object[] args)
421                 {
422                         Debug (4, message, args);
423                 }
424                         
425                 [Conditional ("MCS_DEBUG")]
426                 static public void Debug (int category, string message, params object[] args)
427                 {
428                         if ((category & DebugFlags) == 0)
429                                 return;
430
431                         StringBuilder sb = new StringBuilder (message);
432
433                         if ((args != null) && (args.Length > 0)) {
434                                 sb.Append (": ");
435
436                                 bool first = true;
437                                 foreach (object arg in args) {
438                                         if (first)
439                                                 first = false;
440                                         else
441                                                 sb.Append (", ");
442                                         if (arg == null)
443                                                 sb.Append ("null");
444                                         else if (arg is ICollection)
445                                                 sb.Append (PrintCollection ((ICollection) arg));
446                                         else
447                                                 sb.Append (arg);
448                                 }
449                         }
450
451                         Console.WriteLine (sb.ToString ());
452                 }
453
454                 static public string PrintCollection (ICollection collection)
455                 {
456                         StringBuilder sb = new StringBuilder ();
457
458                         sb.Append (collection.GetType ());
459                         sb.Append ("(");
460
461                         bool first = true;
462                         foreach (object o in collection) {
463                                 if (first)
464                                         first = false;
465                                 else
466                                         sb.Append (", ");
467                                 sb.Append (o);
468                         }
469
470                         sb.Append (")");
471                         return sb.ToString ();
472                 }
473         }
474
475         public enum TimerType {
476                 FindMembers     = 0,
477                 TcFindMembers   = 1,
478                 MemberLookup    = 2,
479                 CachedLookup    = 3,
480                 CacheInit       = 4,
481                 MiscTimer       = 5,
482                 CountTimers     = 6
483         }
484
485         public enum CounterType {
486                 FindMembers     = 0,
487                 MemberCache     = 1,
488                 MiscCounter     = 2,
489                 CountCounters   = 3
490         }
491
492         public class Timer
493         {
494                 static DateTime[] timer_start;
495                 static TimeSpan[] timers;
496                 static long[] timer_counters;
497                 static long[] counters;
498
499                 static Timer ()
500                 {
501                         timer_start = new DateTime [(int) TimerType.CountTimers];
502                         timers = new TimeSpan [(int) TimerType.CountTimers];
503                         timer_counters = new long [(int) TimerType.CountTimers];
504                         counters = new long [(int) CounterType.CountCounters];
505
506                         for (int i = 0; i < (int) TimerType.CountTimers; i++) {
507                                 timer_start [i] = DateTime.Now;
508                                 timers [i] = TimeSpan.Zero;
509                         }
510                 }
511
512                 [Conditional("TIMER")]
513                 static public void IncrementCounter (CounterType which)
514                 {
515                         ++counters [(int) which];
516                 }
517
518                 [Conditional("TIMER")]
519                 static public void StartTimer (TimerType which)
520                 {
521                         timer_start [(int) which] = DateTime.Now;
522                 }
523
524                 [Conditional("TIMER")]
525                 static public void StopTimer (TimerType which)
526                 {
527                         timers [(int) which] += DateTime.Now - timer_start [(int) which];
528                         ++timer_counters [(int) which];
529                 }
530
531                 [Conditional("TIMER")]
532                 static public void ShowTimers ()
533                 {
534                         ShowTimer (TimerType.FindMembers, "- FindMembers timer");
535                         ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer");
536                         ShowTimer (TimerType.MemberLookup, "- MemberLookup timer");
537                         ShowTimer (TimerType.CachedLookup, "- CachedLookup timer");
538                         ShowTimer (TimerType.CacheInit, "- Cache init");
539                         ShowTimer (TimerType.MiscTimer, "- Misc timer");
540
541                         ShowCounter (CounterType.FindMembers, "- Find members");
542                         ShowCounter (CounterType.MemberCache, "- Member cache");
543                         ShowCounter (CounterType.MiscCounter, "- Misc counter");
544                 }
545
546                 static public void ShowCounter (CounterType which, string msg)
547                 {
548                         Console.WriteLine ("{0} {1}", counters [(int) which], msg);
549                 }
550
551                 static public void ShowTimer (TimerType which, string msg)
552                 {
553                         Console.WriteLine (
554                                 "[{0:00}:{1:000}] {2} (used {3} times)",
555                                 (int) timers [(int) which].TotalSeconds,
556                                 timers [(int) which].Milliseconds, msg,
557                                 timer_counters [(int) which]);
558                 }
559         }
560
561         public class InternalErrorException : Exception {
562                 public InternalErrorException ()
563                         : base ("Internal error")
564                 {
565                 }
566
567                 public InternalErrorException (string message)
568                         : base (message)
569                 {
570                 }
571         }
572
573         /// <summary>
574         /// Handles #pragma warning
575         /// </summary>
576         public class WarningRegions {
577
578                 abstract class PragmaCmd
579                 {
580                         public int Line;
581
582                         protected PragmaCmd (int line)
583                         {
584                                 Line = line;
585                         }
586
587                         public abstract bool IsEnabled (int code, bool previous);
588                 }
589                 
590                 class Disable: PragmaCmd
591                 {
592                         int code;
593                         public Disable (int line, int code)
594                                 : base (line)
595                         {
596                                 this.code = code;
597                         }
598
599                         public override bool IsEnabled (int code, bool previous)
600                         {
601                                 return this.code == code ? false : previous;
602                         }
603                 }
604
605                 class DisableAll: PragmaCmd
606                 {
607                         public DisableAll (int line)
608                                 : base (line) {}
609
610                         public override bool IsEnabled(int code, bool previous)
611                         {
612                                 return false;
613                         }
614                 }
615
616                 class Enable: PragmaCmd
617                 {
618                         int code;
619                         public Enable (int line, int code)
620                                 : base (line)
621                         {
622                                 this.code = code;
623                         }
624
625                         public override bool IsEnabled(int code, bool previous)
626                         {
627                                 return this.code == code ? true : previous;
628                         }
629                 }
630
631                 class EnableAll: PragmaCmd
632                 {
633                         public EnableAll (int line)
634                                 : base (line) {}
635
636                         public override bool IsEnabled(int code, bool previous)
637                         {
638                                 return true;
639                         }
640                 }
641
642
643                 ArrayList regions = new ArrayList ();
644
645                 public void WarningDisable (int line)
646                 {
647                         regions.Add (new DisableAll (line));
648                 }
649
650                 public void WarningDisable (Location location, int code)
651                 {
652                         if (CheckWarningCode (code, location))
653                                 regions.Add (new Disable (location.Row, code));
654                 }
655
656                 public void WarningEnable (int line)
657                 {
658                         regions.Add (new EnableAll (line));
659                 }
660
661                 public void WarningEnable (Location location, int code)
662                 {
663                         if (CheckWarningCode (code, location))
664                                 regions.Add (new Enable (location.Row, code));
665                 }
666
667                 public bool IsWarningEnabled (int code, int src_line)
668                 {
669                         bool result = true;
670                         foreach (PragmaCmd pragma in regions) {
671                                 if (src_line < pragma.Line)
672                                         break;
673
674                                 result = pragma.IsEnabled (code, result);
675                         }
676                         return result;
677                 }
678
679                 bool CheckWarningCode (int code, Location loc)
680                 {
681                         if (Report.IsValidWarning (code))
682                                 return true;
683
684                         Report.Warning (1691, 1, loc, "'{0}' is not a valid warning number", code);
685                         return false;
686                 }
687         }
688 }