Reorder fields to improve object layout since the runtime can't do it for corlib...
[mono.git] / mcs / mcs / report.cs
1 //
2 // report.cs: report errors and warnings.
3 //
4 // Author: Miguel de Icaza (miguel@ximian.com)
5 //         Marek Safar (marek.safar@seznam.cz)         
6 //
7 // Copyright 2001 Ximian, Inc. (http://www.ximian.com)
8 //
9
10 using System;
11 using System.IO;
12 using System.Text;
13 using System.Collections.Generic;
14 using System.Diagnostics;
15
16 namespace Mono.CSharp {
17
18         //
19         // Errors and warnings manager
20         //
21         public class Report
22         {
23                 /// <summary>  
24                 ///   Whether warnings should be considered errors
25                 /// </summary>
26                 public bool WarningsAreErrors;
27                 List<int> warnings_as_error;
28                 List<int> warnings_only;
29
30                 public const int RuntimeErrorId = 10000;
31
32                 //
33                 // Keeps track of the warnings that we are ignoring
34                 //
35                 HashSet<int> warning_ignore_table;
36
37                 Dictionary<int, WarningRegions> warning_regions_table;
38
39                 int warning_level;
40
41                 ReportPrinter printer;
42
43                 int reporting_disabled;
44                 
45                 /// <summary>
46                 /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported.
47                 /// </summary>
48                 List<string> extra_information = new List<string> ();
49
50                 // 
51                 // IF YOU ADD A NEW WARNING YOU HAVE TO ADD ITS ID HERE
52                 //
53                 public static readonly int[] AllWarnings = new int[] {
54                         28, 67, 78,
55                         105, 108, 109, 114, 162, 164, 168, 169, 183, 184, 197,
56                         219, 251, 252, 253, 278, 282,
57                         402, 414, 419, 420, 429, 436, 437, 440, 458, 464, 465, 467, 469, 472, 473,
58                         612, 618, 626, 628, 642, 649, 652, 657, 658, 659, 660, 661, 665, 672, 675, 693,
59                         728,
60                         809, 824,
61                         1030, 1058, 1066,
62                         1522, 1570, 1571, 1572, 1573, 1574, 1580, 1581, 1584, 1587, 1589, 1590, 1591, 1592,
63                         1607, 1616, 1633, 1634, 1635, 1685, 1690, 1691, 1692, 1695, 1696, 1699,
64                         1700, 1701, 1702, 1709, 1717, 1718, 1720,
65                         1901, 1956, 1981,
66                         2002, 2023, 2029,
67                         3000, 3001, 3002, 3003, 3005, 3006, 3007, 3008, 3009,
68                         3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019,
69                         3021, 3022, 3023, 3024, 3026, 3027
70                 };
71
72                 static HashSet<int> AllWarningsHashSet;
73
74                 public Report (ReportPrinter printer)
75                 {
76                         if (printer == null)
77                                 throw new ArgumentNullException ("printer");
78
79                         this.printer = printer;
80                         warning_level = 4;
81                 }
82
83                 public void DisableReporting ()
84                 {
85                         ++reporting_disabled;
86                 }
87
88                 public void EnableReporting ()
89                 {
90                         --reporting_disabled;
91                 }
92
93                 public void FeatureIsNotAvailable (CompilerContext compiler, Location loc, string feature)
94                 {
95                         string version;
96                         switch (compiler.Settings.Version) {
97                         case LanguageVersion.ISO_1:
98                                 version = "1.0";
99                                 break;
100                         case LanguageVersion.ISO_2:
101                                 version = "2.0";
102                                 break;
103                         case LanguageVersion.V_3:
104                                 version = "3.0";
105                                 break;
106                         default:
107                                 throw new InternalErrorException ("Invalid feature version", compiler.Settings.Version);
108                         }
109
110                         Error (1644, loc,
111                                 "Feature `{0}' cannot be used because it is not part of the C# {1} language specification",
112                                       feature, version);
113                 }
114
115                 public void FeatureIsNotSupported (Location loc, string feature)
116                 {
117                         Error (1644, loc,
118                                 "Feature `{0}' is not supported in Mono mcs1 compiler. Consider using the `gmcs' compiler instead",
119                                 feature);
120                 }
121                 
122                 bool IsWarningEnabled (int code, int level, Location loc)
123                 {
124                         if (WarningLevel < level)
125                                 return false;
126
127                         if (IsWarningDisabledGlobally (code))
128                                 return false;
129
130                         if (warning_regions_table == null || loc.IsNull)
131                                 return true;
132
133                         WarningRegions regions;
134                         if (!warning_regions_table.TryGetValue (loc.File, out regions))
135                                 return true;
136
137                         return regions.IsWarningEnabled (code, loc.Row);
138                 }
139
140                 public bool IsWarningDisabledGlobally (int code)
141                 {
142                         return warning_ignore_table != null && warning_ignore_table.Contains (code);
143                 }
144
145                 bool IsWarningAsError (int code)
146                 {
147                         bool is_error = WarningsAreErrors;
148
149                         // Check specific list
150                         if (warnings_as_error != null)
151                                 is_error |= warnings_as_error.Contains (code);
152
153                         // Ignore excluded warnings
154                         if (warnings_only != null && warnings_only.Contains (code))
155                                 is_error = false;
156
157                         return is_error;
158                 }
159                         
160                 public void RuntimeMissingSupport (Location loc, string feature) 
161                 {
162                         Error (-88, loc, "Your .NET Runtime does not support `{0}'. Please use the latest Mono runtime instead.", feature);
163                 }
164
165                 /// <summary>
166                 /// In most error cases is very useful to have information about symbol that caused the error.
167                 /// Call this method before you call Report.Error when it makes sense.
168                 /// </summary>
169                 public void SymbolRelatedToPreviousError (Location loc, string symbol)
170                 {
171                         SymbolRelatedToPreviousError (loc.ToString ());
172                 }
173
174                 public void SymbolRelatedToPreviousError (MemberSpec ms)
175                 {
176                         if (reporting_disabled > 0 || !printer.HasRelatedSymbolSupport)
177                                 return;
178
179                         var mc = ms.MemberDefinition as MemberCore;
180                         while (ms is ElementTypeSpec) {
181                                 ms = ((ElementTypeSpec) ms).Element;
182                                 mc = ms.MemberDefinition as MemberCore;
183                         }
184
185                         if (mc != null) {
186                                 SymbolRelatedToPreviousError (mc);
187                         } else {
188                                 if (ms.DeclaringType != null)
189                                         ms = ms.DeclaringType;
190
191                                 var imported_type = ms.MemberDefinition as ImportedTypeDefinition;
192                                 if (imported_type != null) {
193                                         var iad = imported_type.DeclaringAssembly as ImportedAssemblyDefinition;
194                                         SymbolRelatedToPreviousError (iad.Location);
195                                 }
196                         }
197                 }
198
199                 public void SymbolRelatedToPreviousError (MemberCore mc)
200                 {
201                         SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
202                 }
203
204                 public void SymbolRelatedToPreviousError (string loc)
205                 {
206                         string msg = String.Format ("{0} (Location of the symbol related to previous ", loc);
207                         if (extra_information.Contains (msg))
208                                 return;
209
210                         extra_information.Add (msg);
211                 }
212
213                 public void AddWarningAsError (string warningId)
214                 {
215                         int id;
216                         try {
217                                 id = int.Parse (warningId);
218                         } catch {
219                                 CheckWarningCode (warningId, Location.Null);
220                                 return;
221                         }
222
223                         if (!CheckWarningCode (id, Location.Null))
224                                 return;
225
226                         if (warnings_as_error == null)
227                                 warnings_as_error = new List<int> ();
228                         
229                         warnings_as_error.Add (id);
230                 }
231
232                 public void RemoveWarningAsError (string warningId)
233                 {
234                         int id;
235                         try {
236                                 id = int.Parse (warningId);
237                         } catch {
238                                 CheckWarningCode (warningId, Location.Null);
239                                 return;
240                         }
241
242                         if (!CheckWarningCode (id, Location.Null))
243                                 return;
244
245                         if (warnings_only == null)
246                                 warnings_only = new List<int> ();
247
248                         warnings_only.Add (id);
249                 }
250
251                 public bool CheckWarningCode (string code, Location loc)
252                 {
253                         Warning (1691, 1, loc, "`{0}' is not a valid warning number", code);
254                         return false;
255                 }
256
257                 public bool CheckWarningCode (int code, Location loc)
258                 {
259                         if (AllWarningsHashSet == null)
260                                 AllWarningsHashSet = new HashSet<int> (AllWarnings);
261
262                         if (AllWarningsHashSet.Contains (code))
263                                 return true;
264
265                         return CheckWarningCode (code.ToString (), loc);
266                 }
267
268                 public void ExtraInformation (Location loc, string msg)
269                 {
270                         extra_information.Add (String.Format ("{0} {1}", loc, msg));
271                 }
272
273                 public WarningRegions RegisterWarningRegion (Location location)
274                 {
275                         WarningRegions regions;
276                         if (warning_regions_table == null) {
277                                 regions = null;
278                                 warning_regions_table = new Dictionary<int, WarningRegions> ();
279                         } else {
280                                 warning_regions_table.TryGetValue (location.File, out regions);
281                         }
282
283                         if (regions == null) {
284                                 regions = new WarningRegions ();
285                                 warning_regions_table.Add (location.File, regions);
286                         }
287
288                         return regions;
289                 }
290
291                 public void Warning (int code, int level, Location loc, string message)
292                 {
293                         if (reporting_disabled > 0)
294                                 return;
295
296                         if (!IsWarningEnabled (code, level, loc))
297                                 return;
298
299                         AbstractMessage msg;
300                         if (IsWarningAsError (code)) {
301                                 message = "Warning as Error: " + message;
302                                 msg = new ErrorMessage (code, loc, message, extra_information);
303                         } else {
304                                 msg = new WarningMessage (code, loc, message, extra_information);
305                         }
306
307                         extra_information.Clear ();
308                         printer.Print (msg);
309                 }
310
311                 public void Warning (int code, int level, Location loc, string format, string arg)
312                 {
313                         Warning (code, level, loc, String.Format (format, arg));
314                 }
315
316                 public void Warning (int code, int level, Location loc, string format, string arg1, string arg2)
317                 {
318                         Warning (code, level, loc, String.Format (format, arg1, arg2));
319                 }
320
321                 public void Warning (int code, int level, Location loc, string format, params object[] args)
322                 {
323                         Warning (code, level, loc, String.Format (format, args));
324                 }
325
326                 public void Warning (int code, int level, string message)
327                 {
328                         Warning (code, level, Location.Null, message);
329                 }
330
331                 public void Warning (int code, int level, string format, string arg)
332                 {
333                         Warning (code, level, Location.Null, format, arg);
334                 }
335
336                 public void Warning (int code, int level, string format, string arg1, string arg2)
337                 {
338                         Warning (code, level, Location.Null, format, arg1, arg2);
339                 }
340
341                 public void Warning (int code, int level, string format, params string[] args)
342                 {
343                         Warning (code, level, Location.Null, String.Format (format, args));
344                 }
345
346                 //
347                 // Warnings encountered so far
348                 //
349                 public int Warnings {
350                         get { return printer.WarningsCount; }
351                 }
352
353                 public void Error (int code, Location loc, string error)
354                 {
355                         if (reporting_disabled > 0)
356                                 return;
357
358                         ErrorMessage msg = new ErrorMessage (code, loc, error, extra_information);
359                         extra_information.Clear ();
360
361                         printer.Print (msg);
362                 }
363
364                 public void Error (int code, Location loc, string format, string arg)
365                 {
366                         Error (code, loc, String.Format (format, arg));
367                 }
368
369                 public void Error (int code, Location loc, string format, string arg1, string arg2)
370                 {
371                         Error (code, loc, String.Format (format, arg1, arg2));
372                 }
373
374                 public void Error (int code, Location loc, string format, params string[] args)
375                 {
376                         Error (code, loc, String.Format (format, args));
377                 }
378
379                 public void Error (int code, string error)
380                 {
381                         Error (code, Location.Null, error);
382                 }
383
384                 public void Error (int code, string format, string arg)
385                 {
386                         Error (code, Location.Null, format, arg);
387                 }
388
389                 public void Error (int code, string format, string arg1, string arg2)
390                 {
391                         Error (code, Location.Null, format, arg1, arg2);
392                 }
393
394                 public void Error (int code, string format, params string[] args)
395                 {
396                         Error (code, Location.Null, String.Format (format, args));
397                 }
398
399                 //
400                 // Errors encountered so far
401                 //
402                 public int Errors {
403                         get { return printer.ErrorsCount; }
404                 }
405
406                 public bool IsDisabled {
407                         get {
408                                 return reporting_disabled > 0;
409                         }
410                 }
411
412                 public ReportPrinter Printer {
413                         get { return printer; }
414                 }
415
416                 public void SetIgnoreWarning (int code)
417                 {
418                         if (warning_ignore_table == null)
419                                 warning_ignore_table = new HashSet<int> ();
420
421                         warning_ignore_table.Add (code);
422                 }
423
424                 public ReportPrinter SetPrinter (ReportPrinter printer)
425                 {
426                         ReportPrinter old = this.printer;
427                         this.printer = printer;
428                         return old;
429                 }
430
431                 public int WarningLevel {
432                         get {
433                                 return warning_level;
434                         }
435                         set {
436                                 warning_level = value;
437                         }
438                 }
439
440                 [Conditional ("MCS_DEBUG")]
441                 static public void Debug (string message, params object[] args)
442                 {
443                         Debug (4, message, args);
444                 }
445                         
446                 [Conditional ("MCS_DEBUG")]
447                 static public void Debug (int category, string message, params object[] args)
448                 {
449 //                      if ((category & DebugFlags) == 0)
450 //                              return;
451
452                         StringBuilder sb = new StringBuilder (message);
453
454                         if ((args != null) && (args.Length > 0)) {
455                                 sb.Append (": ");
456
457                                 bool first = true;
458                                 foreach (object arg in args) {
459                                         if (first)
460                                                 first = false;
461                                         else
462                                                 sb.Append (", ");
463                                         if (arg == null)
464                                                 sb.Append ("null");
465 //                                      else if (arg is ICollection)
466 //                                              sb.Append (PrintCollection ((ICollection) arg));
467                                         else
468                                                 sb.Append (arg);
469                                 }
470                         }
471
472                         Console.WriteLine (sb.ToString ());
473                 }
474 /*
475                 static public string PrintCollection (ICollection collection)
476                 {
477                         StringBuilder sb = new StringBuilder ();
478
479                         sb.Append (collection.GetType ());
480                         sb.Append ("(");
481
482                         bool first = true;
483                         foreach (object o in collection) {
484                                 if (first)
485                                         first = false;
486                                 else
487                                         sb.Append (", ");
488                                 sb.Append (o);
489                         }
490
491                         sb.Append (")");
492                         return sb.ToString ();
493                 }
494 */ 
495         }
496
497         public abstract class AbstractMessage
498         {
499                 readonly string[] extra_info;
500                 protected readonly int code;
501                 protected readonly Location location;
502                 readonly string message;
503
504                 protected AbstractMessage (int code, Location loc, string msg, List<string> extraInfo)
505                 {
506                         this.code = code;
507                         if (code < 0)
508                                 this.code = 8000 - code;
509
510                         this.location = loc;
511                         this.message = msg;
512                         if (extraInfo.Count != 0) {
513                                 this.extra_info = extraInfo.ToArray ();
514                         }
515                 }
516
517                 protected AbstractMessage (AbstractMessage aMsg)
518                 {
519                         this.code = aMsg.code;
520                         this.location = aMsg.location;
521                         this.message = aMsg.message;
522                         this.extra_info = aMsg.extra_info;
523                 }
524
525                 public int Code {
526                         get { return code; }
527                 }
528
529                 public override bool Equals (object obj)
530                 {
531                         AbstractMessage msg = obj as AbstractMessage;
532                         if (msg == null)
533                                 return false;
534
535                         return code == msg.code && location.Equals (msg.location) && message == msg.message;
536                 }
537
538                 public override int GetHashCode ()
539                 {
540                         return code.GetHashCode ();
541                 }
542
543                 public abstract bool IsWarning { get; }
544
545                 public Location Location {
546                         get { return location; }
547                 }
548
549                 public abstract string MessageType { get; }
550
551                 public string[] RelatedSymbols {
552                         get { return extra_info; }
553                 }
554
555                 public string Text {
556                         get { return message; }
557                 }
558         }
559
560         sealed class WarningMessage : AbstractMessage
561         {
562                 public WarningMessage (int code, Location loc, string message, List<string> extra_info)
563                         : base (code, loc, message, extra_info)
564                 {
565                 }
566
567                 public override bool IsWarning {
568                         get { return true; }
569                 }
570
571                 public override string MessageType {
572                         get {
573                                 return "warning";
574                         }
575                 }
576         }
577
578         sealed class ErrorMessage : AbstractMessage
579         {
580                 public ErrorMessage (int code, Location loc, string message, List<string> extraInfo)
581                         : base (code, loc, message, extraInfo)
582                 {
583                 }
584
585                 public ErrorMessage (AbstractMessage aMsg)
586                         : base (aMsg)
587                 {
588                 }
589
590                 public override bool IsWarning {
591                         get { return false; }
592                 }
593
594                 public override string MessageType {
595                         get {
596                                 return "error";
597                         }
598                 }
599         }
600
601         //
602         // Generic base for any message writer
603         //
604         public abstract class ReportPrinter
605         {
606                 #region Properties
607
608                 public int FatalCounter { get; set; }
609
610                 public int ErrorsCount { get; protected set; }
611         
612                 public bool ShowFullPaths { get; set; }
613
614                 //
615                 // Whether to dump a stack trace on errors. 
616                 //
617                 public bool Stacktrace { get; set; }
618
619                 public int WarningsCount { get; private set; }
620         
621                 //
622                 // When (symbols related to previous ...) can be used
623                 //
624                 public virtual bool HasRelatedSymbolSupport {
625                         get { return true; }
626                 }
627
628                 #endregion
629
630
631                 protected virtual string FormatText (string txt)
632                 {
633                         return txt;
634                 }
635
636                 public virtual void Print (AbstractMessage msg)
637                 {
638                         if (msg.IsWarning) {
639                                 ++WarningsCount;
640                         } else {
641                                 ++ErrorsCount;
642
643                                 if (ErrorsCount == FatalCounter)
644                                         throw new Exception (msg.Text);
645                         }
646                 }
647
648                 protected void Print (AbstractMessage msg, TextWriter output)
649                 {
650                         StringBuilder txt = new StringBuilder ();
651                         if (!msg.Location.IsNull) {
652                                 if (ShowFullPaths)
653                                         txt.Append (msg.Location.ToStringFullName ());
654                                 else
655                                         txt.Append (msg.Location.ToString ());
656
657                                 txt.Append (" ");
658                         }
659
660                         txt.AppendFormat ("{0} CS{1:0000}: {2}", msg.MessageType, msg.Code, msg.Text);
661
662                         if (!msg.IsWarning)
663                                 output.WriteLine (FormatText (txt.ToString ()));
664                         else
665                                 output.WriteLine (txt.ToString ());
666
667                         if (msg.RelatedSymbols != null) {
668                                 foreach (string s in msg.RelatedSymbols)
669                                         output.WriteLine (s + msg.MessageType + ")");
670                         }
671                 }
672
673                 public void Reset ()
674                 {
675                         // HACK: Temporary hack for broken repl flow
676                         ErrorsCount = WarningsCount = 0;
677                 }
678         }
679
680         sealed class NullReportPrinter : ReportPrinter
681         {
682         }
683
684         //
685         // Default message recorder, it uses two types of message groups.
686         // Common messages: messages reported in all sessions.
687         // Merged messages: union of all messages in all sessions. 
688         //
689         // Used by the Lambda expressions to compile the code with various
690         // parameter values, or by attribute resolver
691         //
692         class SessionReportPrinter : ReportPrinter
693         {
694                 List<AbstractMessage> session_messages;
695                 //
696                 // A collection of exactly same messages reported in all sessions
697                 //
698                 List<AbstractMessage> common_messages;
699
700                 //
701                 // A collection of unique messages reported in all sessions
702                 //
703                 List<AbstractMessage> merged_messages;
704
705                 public override void Print (AbstractMessage msg)
706                 {
707                         //
708                         // This line is useful when debugging recorded messages
709                         //
710                         // Console.WriteLine ("RECORDING: {0}", msg.ToString ());
711
712                         if (session_messages == null)
713                                 session_messages = new List<AbstractMessage> ();
714
715                         session_messages.Add (msg);
716
717                         base.Print (msg);
718                 }
719
720                 public void EndSession ()
721                 {
722                         if (session_messages == null)
723                                 return;
724
725                         //
726                         // Handles the first session
727                         //
728                         if (common_messages == null) {
729                                 common_messages = new List<AbstractMessage> (session_messages);
730                                 merged_messages = session_messages;
731                                 session_messages = null;
732                                 return;
733                         }
734
735                         //
736                         // Store common messages if any
737                         //
738                         for (int i = 0; i < common_messages.Count; ++i) {
739                                 AbstractMessage cmsg = common_messages[i];
740                                 bool common_msg_found = false;
741                                 foreach (AbstractMessage msg in session_messages) {
742                                         if (cmsg.Equals (msg)) {
743                                                 common_msg_found = true;
744                                                 break;
745                                         }
746                                 }
747
748                                 if (!common_msg_found)
749                                         common_messages.RemoveAt (i);
750                         }
751
752                         //
753                         // Merge session and previous messages
754                         //
755                         for (int i = 0; i < session_messages.Count; ++i) {
756                                 AbstractMessage msg = session_messages[i];
757                                 bool msg_found = false;
758                                 for (int ii = 0; ii < merged_messages.Count; ++ii) {
759                                         if (msg.Equals (merged_messages[ii])) {
760                                                 msg_found = true;
761                                                 break;
762                                         }
763                                 }
764
765                                 if (!msg_found)
766                                         merged_messages.Add (msg);
767                         }
768                 }
769
770                 public bool IsEmpty {
771                         get {
772                                 return merged_messages == null && common_messages == null;
773                         }
774                 }
775
776                 //
777                 // Prints collected messages, common messages have a priority
778                 //
779                 public bool Merge (ReportPrinter dest)
780                 {
781                         var messages_to_print = merged_messages;
782                         if (common_messages != null && common_messages.Count > 0) {
783                                 messages_to_print = common_messages;
784                         }
785
786                         if (messages_to_print == null)
787                                 return false;
788
789                         bool error_msg = false;
790                         foreach (AbstractMessage msg in messages_to_print) {
791                                 dest.Print (msg);
792                                 error_msg |= !msg.IsWarning;
793                         }
794
795                         return error_msg;
796                 }
797         }
798
799         public class StreamReportPrinter : ReportPrinter
800         {
801                 readonly TextWriter writer;
802
803                 public StreamReportPrinter (TextWriter writer)
804                 {
805                         this.writer = writer;
806                 }
807
808                 public override void Print (AbstractMessage msg)
809                 {
810                         Print (msg, writer);
811                         base.Print (msg);
812                 }
813         }
814
815         public class ConsoleReportPrinter : StreamReportPrinter
816         {
817                 static readonly string prefix, postfix;
818
819                 static ConsoleReportPrinter ()
820                 {
821                         string term = Environment.GetEnvironmentVariable ("TERM");
822                         bool xterm_colors = false;
823                         
824                         switch (term){
825                         case "xterm":
826                         case "rxvt":
827                         case "rxvt-unicode": 
828                                 if (Environment.GetEnvironmentVariable ("COLORTERM") != null){
829                                         xterm_colors = true;
830                                 }
831                                 break;
832
833                         case "xterm-color":
834                         case "xterm-256color":
835                                 xterm_colors = true;
836                                 break;
837                         }
838                         if (!xterm_colors)
839                                 return;
840
841                         if (!(UnixUtils.isatty (1) && UnixUtils.isatty (2)))
842                                 return;
843                         
844                         string config = Environment.GetEnvironmentVariable ("MCS_COLORS");
845                         if (config == null){
846                                 config = "errors=red";
847                                 //config = "brightwhite,red";
848                         }
849
850                         if (config == "disable")
851                                 return;
852
853                         if (!config.StartsWith ("errors="))
854                                 return;
855
856                         config = config.Substring (7);
857                         
858                         int p = config.IndexOf (",");
859                         if (p == -1)
860                                 prefix = GetForeground (config);
861                         else
862                                 prefix = GetBackground (config.Substring (p+1)) + GetForeground (config.Substring (0, p));
863                         postfix = "\x001b[0m";
864                 }
865
866                 public ConsoleReportPrinter ()
867                         : base (Console.Error)
868                 {
869                 }
870
871                 public ConsoleReportPrinter (TextWriter writer)
872                         : base (writer)
873                 {
874                 }
875
876                 static int NameToCode (string s)
877                 {
878                         switch (s) {
879                         case "black":
880                                 return 0;
881                         case "red":
882                                 return 1;
883                         case "green":
884                                 return 2;
885                         case "yellow":
886                                 return 3;
887                         case "blue":
888                                 return 4;
889                         case "magenta":
890                                 return 5;
891                         case "cyan":
892                                 return 6;
893                         case "grey":
894                         case "white":
895                                 return 7;
896                         }
897                         return 7;
898                 }
899
900                 //
901                 // maps a color name to its xterm color code
902                 //
903                 static string GetForeground (string s)
904                 {
905                         string highcode;
906
907                         if (s.StartsWith ("bright")) {
908                                 highcode = "1;";
909                                 s = s.Substring (6);
910                         } else
911                                 highcode = "";
912
913                         return "\x001b[" + highcode + (30 + NameToCode (s)).ToString () + "m";
914                 }
915
916                 static string GetBackground (string s)
917                 {
918                         return "\x001b[" + (40 + NameToCode (s)).ToString () + "m";
919                 }
920
921                 protected override string FormatText (string txt)
922                 {
923                         if (prefix != null)
924                                 return prefix + txt + postfix;
925
926                         return txt;
927                 }
928
929                 static string FriendlyStackTrace (StackTrace t)
930                 {               
931                         StringBuilder sb = new StringBuilder ();
932                         
933                         bool foundUserCode = false;
934                         
935                         for (int i = 0; i < t.FrameCount; i++) {
936                                 StackFrame f = t.GetFrame (i);
937                                 var mb = f.GetMethod ();
938                                 
939                                 if (!foundUserCode && mb.ReflectedType == typeof (Report))
940                                         continue;
941                                 
942                                 foundUserCode = true;
943                                 
944                                 sb.Append ("\tin ");
945                                 
946                                 if (f.GetFileLineNumber () > 0)
947                                         sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ());
948                                 
949                                 sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name);
950                                 
951                                 bool first = true;
952                                 foreach (var pi in mb.GetParameters ()) {
953                                         if (!first)
954                                                 sb.Append (", ");
955                                         first = false;
956
957                                         sb.Append (pi.ParameterType.FullName);
958                                 }
959                                 sb.Append (")\n");
960                         }
961         
962                         return sb.ToString ();
963                 }
964
965                 public override void Print (AbstractMessage msg)
966                 {
967                         base.Print (msg);
968
969                         if (Stacktrace)
970                                 Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
971                 }
972
973                 public static string FriendlyStackTrace (Exception e)
974                 {
975                         return FriendlyStackTrace (new StackTrace (e, true));
976                 }
977
978                 public static void StackTrace ()
979                 {
980                         Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
981                 }
982         }
983
984         class TimeReporter
985         {
986                 public enum TimerType
987                 {
988                         ParseTotal,
989                         AssemblyBuilderSetup,
990                         CreateTypeTotal,
991                         ReferencesLoading,
992                         ReferencesImporting,
993                         PredefinedTypesInit,
994                         ModuleDefinitionTotal,
995                         UsingResolve,
996                         EmitTotal,
997                         CloseTypes,
998                         Resouces,
999                         OutputSave,
1000                         DebugSave,
1001                 }
1002
1003                 readonly Stopwatch[] timers;
1004                 Stopwatch total;
1005
1006                 public TimeReporter (bool enabled)
1007                 {
1008                         if (!enabled)
1009                                 return;
1010
1011                         timers = new Stopwatch[System.Enum.GetValues(typeof (TimerType)).Length];
1012                 }
1013
1014                 public void Start (TimerType type)
1015                 {
1016                         if (timers != null) {
1017                                 var sw = new Stopwatch ();
1018                                 timers[(int) type] = sw;
1019                                 sw.Start ();
1020                         }
1021                 }
1022
1023                 public void StartTotal ()
1024                 {
1025                         total = new Stopwatch ();
1026                         total.Start ();
1027                 }
1028
1029                 public void Stop (TimerType type)
1030                 {
1031                         if (timers != null) {
1032                                 timers[(int) type].Stop ();
1033                         }
1034                 }
1035
1036                 public void StopTotal ()
1037                 {
1038                         total.Stop ();
1039                 }
1040
1041                 public void ShowStats ()
1042                 {
1043                         if (timers == null)
1044                                 return;
1045
1046                         Dictionary<TimerType, string> timer_names = new Dictionary<TimerType,string> () {
1047                                 { TimerType.ParseTotal, "Parsing source files" },
1048                                 { TimerType.AssemblyBuilderSetup, "Assembly builder setup" },
1049                                 { TimerType.CreateTypeTotal, "Compiled types created" },
1050                                 { TimerType.ReferencesLoading, "Referenced assemblies loading" },
1051                                 { TimerType.ReferencesImporting, "Referenced assemblies importing" },
1052                                 { TimerType.PredefinedTypesInit, "Predefined types initialization" },
1053                                 { TimerType.ModuleDefinitionTotal, "Module definition" },
1054                                 { TimerType.UsingResolve, "Top-level usings resolve" },
1055                                 { TimerType.EmitTotal, "Resolving and emitting members blocks" },
1056                                 { TimerType.CloseTypes, "Module types closed" },
1057                                 { TimerType.Resouces, "Embedding resources" },
1058                                 { TimerType.OutputSave, "Writing output file" },
1059                                 { TimerType.DebugSave, "Writing debug symbols file" },
1060                         };
1061
1062                         int counter = 0;
1063                         double percentage = (double) total.ElapsedMilliseconds / 100;
1064                         long subtotal = total.ElapsedMilliseconds;
1065                         foreach (var timer in timers) {
1066                                 string msg = timer_names[(TimerType) counter++];
1067                                 var ms = timer == null ? 0 : timer.ElapsedMilliseconds;
1068                                 Console.WriteLine ("{0,4:0.0}% {1,5}ms {2}", ms / percentage, ms, msg);
1069                                 subtotal -= ms;
1070                         }
1071
1072                         Console.WriteLine ("{0,4:0.0}% {1,5}ms Other tasks", subtotal / percentage, subtotal);
1073                         Console.WriteLine ();
1074                         Console.WriteLine ("Total elapsed time: {0}", total.Elapsed);
1075                 }
1076         }
1077
1078         public class InternalErrorException : Exception {
1079                 public InternalErrorException (MemberCore mc, Exception e)
1080                         : base (mc.Location + " " + mc.GetSignatureForError (), e)
1081                 {
1082                 }
1083
1084                 public InternalErrorException ()
1085                         : base ("Internal error")
1086                 {
1087                 }
1088
1089                 public InternalErrorException (string message)
1090                         : base (message)
1091                 {
1092                 }
1093
1094                 public InternalErrorException (string message, params object[] args)
1095                         : base (String.Format (message, args))
1096                 {
1097                 }
1098
1099                 public InternalErrorException (Exception exception, string message, params object[] args)
1100                         : base (String.Format (message, args), exception)
1101                 {
1102                 }
1103                 
1104                 public InternalErrorException (Exception e, Location loc)
1105                         : base (loc.ToString (), e)
1106                 {
1107                 }
1108         }
1109
1110         /// <summary>
1111         /// Handles #pragma warning
1112         /// </summary>
1113         public class WarningRegions {
1114
1115                 abstract class PragmaCmd
1116                 {
1117                         public int Line;
1118
1119                         protected PragmaCmd (int line)
1120                         {
1121                                 Line = line;
1122                         }
1123
1124                         public abstract bool IsEnabled (int code, bool previous);
1125                 }
1126                 
1127                 class Disable : PragmaCmd
1128                 {
1129                         int code;
1130                         public Disable (int line, int code)
1131                                 : base (line)
1132                         {
1133                                 this.code = code;
1134                         }
1135
1136                         public override bool IsEnabled (int code, bool previous)
1137                         {
1138                                 return this.code == code ? false : previous;
1139                         }
1140                 }
1141
1142                 class DisableAll : PragmaCmd
1143                 {
1144                         public DisableAll (int line)
1145                                 : base (line) {}
1146
1147                         public override bool IsEnabled(int code, bool previous)
1148                         {
1149                                 return false;
1150                         }
1151                 }
1152
1153                 class Enable : PragmaCmd
1154                 {
1155                         int code;
1156                         public Enable (int line, int code)
1157                                 : base (line)
1158                         {
1159                                 this.code = code;
1160                         }
1161
1162                         public override bool IsEnabled(int code, bool previous)
1163                         {
1164                                 return this.code == code ? true : previous;
1165                         }
1166                 }
1167
1168                 class EnableAll : PragmaCmd
1169                 {
1170                         public EnableAll (int line)
1171                                 : base (line) {}
1172
1173                         public override bool IsEnabled(int code, bool previous)
1174                         {
1175                                 return true;
1176                         }
1177                 }
1178
1179
1180                 List<PragmaCmd> regions = new List<PragmaCmd> ();
1181
1182                 public void WarningDisable (int line)
1183                 {
1184                         regions.Add (new DisableAll (line));
1185                 }
1186
1187                 public void WarningDisable (Location location, int code, Report Report)
1188                 {
1189                         if (Report.CheckWarningCode (code, location))
1190                                 regions.Add (new Disable (location.Row, code));
1191                 }
1192
1193                 public void WarningEnable (int line)
1194                 {
1195                         regions.Add (new EnableAll (line));
1196                 }
1197
1198                 public void WarningEnable (Location location, int code, Report Report)
1199                 {
1200                         if (!Report.CheckWarningCode (code, location))
1201                                 return;
1202
1203                         if (Report.IsWarningDisabledGlobally (code))
1204                                 Report.Warning (1635, 1, location, "Cannot restore warning `CS{0:0000}' because it was disabled globally", code);
1205
1206                         regions.Add (new Enable (location.Row, code));
1207                 }
1208
1209                 public bool IsWarningEnabled (int code, int src_line)
1210                 {
1211                         bool result = true;
1212                         foreach (PragmaCmd pragma in regions) {
1213                                 if (src_line < pragma.Line)
1214                                         break;
1215
1216                                 result = pragma.IsEnabled (code, result);
1217                         }
1218                         return result;
1219                 }
1220         }
1221 }