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