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