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