2004-05-27 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / gmcs / report.cs
1 //
2 // report.cs: report errors and warnings.
3 //
4 // Author: Miguel de Icaza (miguel@ximian.com)
5 //
6 // (C) 2001 Ximian, Inc. (http://www.ximian.com)
7 //
8
9 //
10 // FIXME: currently our class library does not support custom number format strings
11 //
12 using System;
13 using System.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                 /// <summary>
64                 /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported.
65                 /// </summary>
66                 static StringCollection related_symbols = new StringCollection ();
67                 
68                 struct WarningData {
69                         public WarningData (int level, string text) {
70                                 Level = level;
71                                 Message = text;
72                         }
73
74                         public bool IsEnabled ()
75                         {
76                                 return RootContext.WarningLevel >= Level;
77                         }
78
79                         public string Format (params object[] args)
80                         {
81                                 return String.Format (Message, args);
82                         }
83
84                         readonly string Message;
85                         readonly int Level;
86                 }
87
88                 static string GetErrorMsg (int error_no)
89                 {
90                         switch (error_no) {
91                                 case 0657: return "'{0}' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are '{1}'";
92                                 case 3001: return "Argument type '{0}' is not CLS-compliant";
93                                 case 3002: return "Return type of '{0}' is not CLS-compliant";
94                                 case 3003: return "Type of '{0}' is not CLS-compliant";
95                                 case 3005: return "Identifier '{0}' differing only in case is not CLS-compliant";
96                                 case 3006: return "Overloaded method '{0}' differing only in ref or out, or in array rank, is not CLS-compliant";
97                                 case 3008: return "Identifier '{0}' is not CLS-compliant";
98                                 case 3009: return "'{0}': base type '{1}' is not CLS-compliant";
99                                 case 3010: return "'{0}': CLS-compliant interfaces must have only CLS-compliant members";
100                                 case 3011: return "'{0}': only CLS-compliant members can be abstract";
101                                 case 3013: return "Added modules must be marked with the CLSCompliant attribute to match the assembly";
102                                 case 3014: return "'{0}' cannot be marked as CLS-compliant because the assembly does not have a CLSCompliant attribute";
103                                 case 3015: return "'{0}' has no accessible constructors which use only CLS-compliant types";
104                                 case 3016: return "Arrays as attribute arguments are not CLS-compliant";
105                         }
106                         throw new InternalErrorException (String.Format ("Missing error '{0}' text", error_no));
107                 }
108
109                 static WarningData GetWarningMsg (int warn_no)
110                 {
111                         switch (warn_no) {
112                                 case -24: return new WarningData (1, "The Microsoft Runtime cannot set this marshal info. Please use the Mono runtime instead.");
113                                 case -28: return new WarningData (1, "The Microsoft .NET Runtime 1.x does not permit setting custom attributes on the return type");
114                                 case 3012: return new WarningData (1, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
115                                 case 3019: return new WarningData (2, "CLS compliance checking will not be performed on '{0}' because it is private or internal");
116                         }
117
118                         throw new InternalErrorException (String.Format ("Wrong warning number '{0}'", warn_no));
119                 }
120                 
121                 static void Check (int code)
122                 {
123                         if (code == expected_error){
124                                 if (Fatal)
125                                         throw new Exception ();
126                                 
127                                 Environment.Exit (0);
128                         }
129                 }
130                 
131                 public static string FriendlyStackTrace (Exception e)
132                 {
133                         return FriendlyStackTrace (new StackTrace (e, true));
134                 }
135                 
136                 static string FriendlyStackTrace (StackTrace t)
137                 {               
138                         StringBuilder sb = new StringBuilder ();
139                         
140                         bool foundUserCode = false;
141                         
142                         for (int i = 0; i < t.FrameCount; i++) {
143                                 StackFrame f = t.GetFrame (i);
144                                 MethodBase mb = f.GetMethod ();
145                                 
146                                 if (!foundUserCode && mb.ReflectedType == typeof (Report))
147                                         continue;
148                                 
149                                 foundUserCode = true;
150                                 
151                                 sb.Append ("\tin ");
152                                 
153                                 if (f.GetFileLineNumber () > 0)
154                                         sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ());
155                                 
156                                 sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name);
157                                 
158                                 bool first = true;
159                                 foreach (ParameterInfo pi in mb.GetParameters ()) {
160                                         if (!first)
161                                                 sb.Append (", ");
162                                         first = false;
163                                         
164                                         sb.Append (TypeManager.CSharpName (pi.ParameterType));
165                                 }
166                                 sb.Append (")\n");
167                         }
168         
169                         return sb.ToString ();
170                 }
171                 
172                 [Obsolete ("Use SymbolRelatedToPreviousError for better error description")]
173                 static public void LocationOfPreviousError (Location loc)
174                 {
175                         Console.WriteLine (String.Format ("{0}({1}) (Location of symbol related to previous error)", loc.Name, loc.Row));
176                 }                
177
178                 /// <summary>
179                 /// In most error cases is very useful to have information about symbol that caused the error.
180                 /// Call this method before you call Report.Error when it makes sense.
181                 /// </summary>
182                 static public void SymbolRelatedToPreviousError (Location loc, string symbol)
183                 {
184                         SymbolRelatedToPreviousError (String.Format ("{0}({1})", loc.Name, loc.Row), symbol);
185                 }
186
187                 static public void SymbolRelatedToPreviousError (MemberInfo mi)
188                 {
189                         DeclSpace temp_ds = TypeManager.LookupDeclSpace (mi.DeclaringType);
190                         if (temp_ds == null) {
191                                 SymbolRelatedToPreviousError (mi.DeclaringType.Assembly.Location, TypeManager.GetFullNameSignature (mi));
192                         } else {
193                                 string name = String.Concat (temp_ds.Name, ".", mi.Name);
194                                 MemberCore mc = temp_ds.GetDefinition (name) as MemberCore;
195                                 SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
196                         }
197                 }
198
199                 static public void SymbolRelatedToPreviousError (Type type)
200                 {
201                         SymbolRelatedToPreviousError (type.Assembly.Location, TypeManager.CSharpName (type));
202                 }
203
204                 static void SymbolRelatedToPreviousError (string loc, string symbol)
205                 {
206                         related_symbols.Add (String.Format ("{0}: ('{1}' name of symbol related to previous error)", loc, symbol));
207                 }
208
209                 static public void RealError (string msg)
210                 {
211                         Errors++;
212                         Console.WriteLine (msg);
213
214                         foreach (string s in related_symbols)
215                                 Console.WriteLine (s);
216                         related_symbols.Clear ();
217
218                         if (Stacktrace)
219                                 Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
220                         
221                         if (Fatal)
222                                 throw new Exception (msg);
223                 }
224
225
226                 /// <summary>
227                 /// Method reports warning message. Only one reason why exist Warning and Report methods is beter code readability.
228                 /// </summary>
229                 static public void Warning_T (int code, Location loc, params object[] args)
230                 {
231                         WarningData warning = GetWarningMsg (code);
232                         if (warning.IsEnabled ())
233                                 Warning (code, loc, warning.Format (args));
234
235                         related_symbols.Clear ();
236                 }
237
238                 /// <summary>
239                 /// Reports error message.
240                 /// </summary>
241                 static public void Error_T (int code, Location loc, params object[] args)
242                 {
243                         Error_T (code, String.Format ("{0}({1})", loc.Name, loc.Row), args);
244                 }
245
246                 static public void Error_T (int code, string location, params object[] args)
247                 {
248                         string errorText = String.Format (GetErrorMsg (code), args);
249                         PrintError (code, location, errorText);
250                 }
251
252                 static void PrintError (int code, string l, string text)
253                 {
254                         if (code < 0)
255                                 code = 8000-code;
256                         
257                         string msg = String.Format ("{0} error CS{1:0000}: {2}", l, code, text);
258                         RealError (msg);
259                         Check (code);
260                 }
261
262                 static public void Error (int code, Location l, string text)
263                 {
264                         if (code < 0)
265                                 code = 8000-code;
266                         
267                         string msg = String.Format (
268                                 "{0}({1}) error CS{2:0000}: {3}", l.Name, l.Row, code, text);
269 //                              "{0}({1}) error CS{2}: {3}", l.Name, l.Row, code, text);
270                         
271                         RealError (msg);
272                         Check (code);
273                 }
274
275                 static public void Warning (int code, Location l, string text)
276                 {
277                         if (code < 0)
278                                 code = 8000-code;
279                         
280                         if (warning_ignore_table != null){
281                                 if (warning_ignore_table.Contains (code)) {
282                                         related_symbols.Clear ();
283                                         return;
284                                 }
285                         }
286                         
287                         if (WarningsAreErrors)
288                                 Error (code, l, text);
289                         else {
290                                 string row;
291                                 
292                                 if (Location.IsNull (l))
293                                         row = "";
294                                 else
295                                         row = l.Row.ToString ();
296                                 
297                                 Console.WriteLine (String.Format (
298                                         "{0}({1}) warning CS{2:0000}: {3}",
299 //                                      "{0}({1}) warning CS{2}: {3}",
300                                         l.Name,  row, code, text));
301                                 Warnings++;
302
303                                 foreach (string s in related_symbols)
304                                         Console.WriteLine (s);
305                                 related_symbols.Clear ();
306
307                                 Check (code);
308
309                                 if (Stacktrace)
310                                         Console.WriteLine (new StackTrace ().ToString ());
311                         }
312                 }
313                 
314                 static public void Warning (int code, string text)
315                 {
316                         Warning (code, Location.Null, text);
317                 }
318
319                 static public void Warning (int code, int level, string text)
320                 {
321                         if (RootContext.WarningLevel >= level)
322                                 Warning (code, Location.Null, text);
323                 }
324
325                 static public void Warning (int code, int level, Location l, string text)
326                 {
327                         if (RootContext.WarningLevel >= level)
328                                 Warning (code, l, text);
329                 }
330
331                 static public void Error (int code, string text)
332                 {
333                         if (code < 0)
334                                 code = 8000-code;
335                         
336                         string msg = String.Format ("error CS{0:0000}: {1}", code, text);
337 //                      string msg = String.Format ("error CS{0}: {1}", code, text);
338                         
339                         RealError (msg);
340                         Check (code);
341                 }
342
343                 static public void Error (int code, Location loc, string format, params object[] args)
344                 {
345                         Error (code, loc, String.Format (format, args));
346                 }
347
348                 static public void Warning (int code, Location loc, string format, params object[] args)
349                 {
350                         Warning (code, loc, String.Format (format, args));
351                 }
352
353                 static public void Warning (int code, string format, params object[] args)
354                 {
355                         Warning (code, String.Format (format, args));
356                 }
357
358                 static public void Message (Message m)
359                 {
360                         if (m is ErrorMessage)
361                                 Error (m.code, m.text);
362                         else
363                                 Warning (m.code, m.text);
364                 }
365
366                 static public void SetIgnoreWarning (int code)
367                 {
368                         if (warning_ignore_table == null)
369                                 warning_ignore_table = new Hashtable ();
370
371                         warning_ignore_table [code] = true;
372                 }
373                 
374                 static public int ExpectedError {
375                         set {
376                                 expected_error = value;
377                         }
378                         get {
379                                 return expected_error;
380                         }
381                 }
382
383                 public static int DebugFlags = 0;
384
385                 [Conditional ("MCS_DEBUG")]
386                 static public void Debug (string message, params object[] args)
387                 {
388                         Debug (4, message, args);
389                 }
390                         
391                 [Conditional ("MCS_DEBUG")]
392                 static public void Debug (int category, string message, params object[] args)
393                 {
394                         if ((category & DebugFlags) == 0)
395                                 return;
396
397                         StringBuilder sb = new StringBuilder (message);
398
399                         if ((args != null) && (args.Length > 0)) {
400                                 sb.Append (": ");
401
402                                 bool first = true;
403                                 foreach (object arg in args) {
404                                         if (first)
405                                                 first = false;
406                                         else
407                                                 sb.Append (", ");
408                                         if (arg == null)
409                                                 sb.Append ("null");
410                                         else if (arg is ICollection)
411                                                 sb.Append (PrintCollection ((ICollection) arg));
412                                         else if (arg is IntPtr)
413                                                 sb.Append (String.Format ("IntPtr(0x{0:x})", ((IntPtr) arg).ToInt32 ()));
414                                         else
415                                                 sb.Append (arg);
416                                 }
417                         }
418
419                         Console.WriteLine (sb.ToString ());
420                 }
421
422                 static public string PrintCollection (ICollection collection)
423                 {
424                         StringBuilder sb = new StringBuilder ();
425
426                         sb.Append (collection.GetType ());
427                         sb.Append ("(");
428
429                         bool first = true;
430                         foreach (object o in collection) {
431                                 if (first)
432                                         first = false;
433                                 else
434                                         sb.Append (", ");
435                                 sb.Append (o);
436                         }
437
438                         sb.Append (")");
439                         return sb.ToString ();
440                 }
441         }
442
443         public class Message {
444                 public int code;
445                 public string text;
446                 
447                 public Message (int code, string text)
448                 {
449                         this.code = code;
450                         this.text = text;
451                 }
452         }
453
454         public class WarningMessage : Message {
455                 public WarningMessage (int code, string text) : base (code, text)
456                 {
457                 }
458         }
459
460         public class ErrorMessage : Message {
461                 public ErrorMessage (int code, string text) : base (code, text)
462                 {
463                 }
464
465                 //
466                 // For compatibility reasons with old code.
467                 //
468                 public static void report_error (string error)
469                 {
470                         Console.Write ("ERROR: ");
471                         Console.WriteLine (error);
472                 }
473         }
474
475         public enum TimerType {
476                 FindMembers     = 0,
477                 TcFindMembers   = 1,
478                 MemberLookup    = 2,
479                 CachedLookup    = 3,
480                 CacheInit       = 4,
481                 MiscTimer       = 5,
482                 CountTimers     = 6
483         }
484
485         public enum CounterType {
486                 FindMembers     = 0,
487                 MemberCache     = 1,
488                 MiscCounter     = 2,
489                 CountCounters   = 3
490         }
491
492         public class Timer
493         {
494                 static DateTime[] timer_start;
495                 static TimeSpan[] timers;
496                 static long[] timer_counters;
497                 static long[] counters;
498
499                 static Timer ()
500                 {
501                         timer_start = new DateTime [(int) TimerType.CountTimers];
502                         timers = new TimeSpan [(int) TimerType.CountTimers];
503                         timer_counters = new long [(int) TimerType.CountTimers];
504                         counters = new long [(int) CounterType.CountCounters];
505
506                         for (int i = 0; i < (int) TimerType.CountTimers; i++) {
507                                 timer_start [i] = DateTime.Now;
508                                 timers [i] = TimeSpan.Zero;
509                         }
510                 }
511
512                 [Conditional("TIMER")]
513                 static public void IncrementCounter (CounterType which)
514                 {
515                         ++counters [(int) which];
516                 }
517
518                 [Conditional("TIMER")]
519                 static public void StartTimer (TimerType which)
520                 {
521                         timer_start [(int) which] = DateTime.Now;
522                 }
523
524                 [Conditional("TIMER")]
525                 static public void StopTimer (TimerType which)
526                 {
527                         timers [(int) which] += DateTime.Now - timer_start [(int) which];
528                         ++timer_counters [(int) which];
529                 }
530
531                 [Conditional("TIMER")]
532                 static public void ShowTimers ()
533                 {
534                         ShowTimer (TimerType.FindMembers, "- FindMembers timer");
535                         ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer");
536                         ShowTimer (TimerType.MemberLookup, "- MemberLookup timer");
537                         ShowTimer (TimerType.CachedLookup, "- CachedLookup timer");
538                         ShowTimer (TimerType.CacheInit, "- Cache init");
539                         ShowTimer (TimerType.MiscTimer, "- Misc timer");
540
541                         ShowCounter (CounterType.FindMembers, "- Find members");
542                         ShowCounter (CounterType.MemberCache, "- Member cache");
543                         ShowCounter (CounterType.MiscCounter, "- Misc counter");
544                 }
545
546                 static public void ShowCounter (CounterType which, string msg)
547                 {
548                         Console.WriteLine ("{0} {1}", counters [(int) which], msg);
549                 }
550
551                 static public void ShowTimer (TimerType which, string msg)
552                 {
553                         Console.WriteLine (
554                                 "[{0:00}:{1:000}] {2} (used {3} times)",
555                                 (int) timers [(int) which].TotalSeconds,
556                                 timers [(int) which].Milliseconds, msg,
557                                 timer_counters [(int) which]);
558                 }
559         }
560
561         public class InternalErrorException : Exception {
562                 public InternalErrorException ()
563                         : base ("Internal error")
564                 {
565                 }
566
567                 public InternalErrorException (string message)
568                         : base (message)
569                 {
570                 }
571
572                 public InternalErrorException (string format, params object[] args)
573                         : this (String.Format (format, args))
574                 { }
575         }
576 }