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