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