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