2 // report.cs: report errors and warnings.
4 // Author: Miguel de Icaza (miguel@ximian.com)
6 // (C) 2001 Ximian, Inc. (http://www.ximian.com)
10 // FIXME: currently our class library does not support custom number format strings
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Diagnostics;
17 using System.Reflection;
18 using System.Reflection.Emit;
20 namespace Mono.CSharp {
23 /// This class is used to report errors and warnings t te user.
27 /// Errors encountered so far
29 static public int Errors;
32 /// Warnings encountered so far
34 static public int Warnings;
37 /// Whether errors should be throw an exception
39 static public bool Fatal;
42 /// Whether warnings should be considered errors
44 static public bool WarningsAreErrors;
47 /// Whether to dump a stack trace on errors.
49 static public bool Stacktrace;
52 // If the 'expected' error code is reported then the
53 // compilation succeeds.
55 // Used for the test suite to excercise the error codes
57 static int expected_error = 0;
60 // Keeps track of the warnings that we are ignoring
62 static Hashtable warning_ignore_table;
64 static Hashtable warning_regions_table;
67 /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported.
69 static StringCollection extra_information = new StringCollection ();
71 abstract class AbstractMessage {
73 static void Check (int code)
75 if (code == expected_error) {
80 public abstract bool IsWarning { get; }
82 public abstract string MessageType { get; }
84 public virtual void Print (int code, string location, string text)
89 StringBuilder msg = new StringBuilder ();
90 if (location.Length != 0) {
91 msg.Append (location);
94 msg.AppendFormat ("{0} CS{1:0000}: {2}", MessageType, code, text);
95 Console.Error.WriteLine (msg.ToString ());
97 foreach (string s in extra_information)
98 Console.Error.WriteLine (s + MessageType);
100 extra_information.Clear ();
103 Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
106 if (!IsWarning || WarningsAreErrors)
107 throw new Exception (text);
113 public virtual void Print (int code, Location location, string text)
115 if (location.Equals (Location.Null)) {
116 Print (code, "", text);
119 Print (code, String.Format ("{0}({1})", location.Name, location.Row), text);
123 sealed class WarningMessage: AbstractMessage {
124 Location loc = Location.Null;
127 public WarningMessage ():
130 public WarningMessage (int level)
135 public override bool IsWarning {
139 bool IsEnabled (int code)
141 if (RootContext.WarningLevel < Level)
144 if (warning_ignore_table != null) {
145 if (warning_ignore_table.Contains (code)) {
150 if (warning_regions_table == null || loc.Equals (Location.Null))
153 WarningRegions regions = (WarningRegions)warning_regions_table [loc.Name];
157 return regions.IsWarningEnabled (code, loc.Row);
160 public override void Print(int code, string location, string text)
162 if (!IsEnabled (code)) {
163 extra_information.Clear ();
167 if (WarningsAreErrors) {
168 new ErrorMessage ().Print (code, location, text);
173 base.Print (code, location, text);
176 public override void Print(int code, Location location, string text)
179 base.Print (code, location, text);
182 public override string MessageType {
189 sealed class ErrorMessage: AbstractMessage {
191 public override void Print(int code, string location, string text)
194 base.Print (code, location, text);
197 public override bool IsWarning {
198 get { return false; }
201 public override string MessageType {
209 public static void FeatureIsNotStandardized (Location loc, string feature)
211 Report.Error (1644, loc, "Feature '{0}' cannot be used because it is not part of the standardized ISO C# language specification", feature);
214 public static string FriendlyStackTrace (Exception e)
216 return FriendlyStackTrace (new StackTrace (e, true));
219 static string FriendlyStackTrace (StackTrace t)
221 StringBuilder sb = new StringBuilder ();
223 bool foundUserCode = false;
225 for (int i = 0; i < t.FrameCount; i++) {
226 StackFrame f = t.GetFrame (i);
227 MethodBase mb = f.GetMethod ();
229 if (!foundUserCode && mb.ReflectedType == typeof (Report))
232 foundUserCode = true;
236 if (f.GetFileLineNumber () > 0)
237 sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ());
239 sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name);
242 foreach (ParameterInfo pi in mb.GetParameters ()) {
247 sb.Append (TypeManager.CSharpName (pi.ParameterType));
252 return sb.ToString ();
255 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
256 // IF YOU ADD A NEW WARNING YOU HAVE TO DUPLICATE ITS ID HERE
258 public static bool IsValidWarning (int code)
260 int[] all_warnings = new int[] {
261 28, 67, 78, 105, 108, 109, 114, 192, 168, 169, 183, 184, 219, 251, 612, 618, 626, 628, 642, 649,
262 659, 660, 661, 672, 1030, 1522, 1616, 1691, 1692, 1901, 2002, 2023, 3012, 3019, 8024, 8028
265 foreach (int i in all_warnings) {
272 static public void LocationOfPreviousError (Location loc)
274 Console.Error.WriteLine (String.Format ("{0}({1}) (Location of symbol related to previous error)", loc.Name, loc.Row));
277 static public void RuntimeMissingSupport (Location loc, string feature)
279 Report.Error (-88, loc, "Your .NET Runtime does not support '{0}'. Please use the latest Mono runtime instead.", feature);
283 /// In most error cases is very useful to have information about symbol that caused the error.
284 /// Call this method before you call Report.Error when it makes sense.
286 static public void SymbolRelatedToPreviousError (Location loc, string symbol)
288 SymbolRelatedToPreviousError (String.Format ("{0}({1})", loc.Name, loc.Row), symbol);
291 static public void SymbolRelatedToPreviousError (MemberInfo mi)
293 TypeContainer temp_ds = TypeManager.LookupGenericTypeContainer (mi.DeclaringType);
294 if (temp_ds == null) {
295 SymbolRelatedToPreviousError (mi.DeclaringType.Assembly.Location, TypeManager.GetFullNameSignature (mi));
297 MethodBase mb = mi as MethodBase;
299 while (mb.Mono_IsInflatedMethod)
300 mb = mb.GetGenericMethodDefinition ();
301 IMethodData md = TypeManager.GetMethod (mb);
302 SymbolRelatedToPreviousError (md.Location, md.GetSignatureForError (temp_ds));
306 string name = String.Concat (temp_ds.Name, ".", mi.Name);
307 MemberCore mc = temp_ds.GetDefinition (name);
308 SymbolRelatedToPreviousError (mc);
312 static public void SymbolRelatedToPreviousError (MemberCore mc)
314 SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
317 static public void SymbolRelatedToPreviousError (Type type)
319 if (type is TypeBuilder) {
320 DeclSpace temp_ds = TypeManager.LookupDeclSpace (type);
321 SymbolRelatedToPreviousError (temp_ds.Location, TypeManager.CSharpName (type));
322 } else if (type.HasElementType) {
323 SymbolRelatedToPreviousError (type.GetElementType ());
325 SymbolRelatedToPreviousError (type.Assembly.Location, TypeManager.CSharpName (type));
329 static void SymbolRelatedToPreviousError (string loc, string symbol)
331 extra_information.Add (String.Format ("{0}: '{1}' (name of symbol related to previous ", loc, symbol));
334 public static void ExtraInformation (Location loc, string msg)
336 extra_information.Add (String.Format ("{0}({1}) {2}", loc.Name, loc.Row, msg));
339 public static WarningRegions RegisterWarningRegion (Location location)
341 if (warning_regions_table == null)
342 warning_regions_table = new Hashtable ();
344 WarningRegions regions = (WarningRegions)warning_regions_table [location.Name];
345 if (regions == null) {
346 regions = new WarningRegions ();
347 warning_regions_table.Add (location.Name, regions);
352 static public void Warning (int code, int level, Location loc, string format, params object[] args)
354 WarningMessage w = new WarningMessage (level);
355 w.Print (code, loc, String.Format (format, args));
358 static public void Warning (int code, Location loc, string format, params object[] args)
360 WarningMessage w = new WarningMessage ();
361 w.Print (code, loc, String.Format (format, args));
364 static public void Warning (int code, string format, params object[] args)
366 Warning (code, Location.Null, String.Format (format, args));
370 /// Did you test your WarningLevel, that you use this method
372 static public void Warning (int code, string text)
374 Warning (code, Location.Null, text);
377 static public void Error (int code, string format, params object[] args)
379 Error (code, Location.Null, String.Format (format, args));
382 static public void Error (int code, Location loc, string format, params object[] args)
384 ErrorMessage e = new ErrorMessage ();
385 e.Print (code, loc, String.Format (format, args));
388 static public void SetIgnoreWarning (int code)
390 if (warning_ignore_table == null)
391 warning_ignore_table = new Hashtable ();
393 warning_ignore_table [code] = true;
396 static public int ExpectedError {
398 expected_error = value;
401 return expected_error;
405 public static int DebugFlags = 0;
407 [Conditional ("MCS_DEBUG")]
408 static public void Debug (string message, params object[] args)
410 Debug (4, message, args);
413 [Conditional ("MCS_DEBUG")]
414 static public void Debug (int category, string message, params object[] args)
416 if ((category & DebugFlags) == 0)
419 StringBuilder sb = new StringBuilder (message);
421 if ((args != null) && (args.Length > 0)) {
425 foreach (object arg in args) {
432 else if (arg is ICollection)
433 sb.Append (PrintCollection ((ICollection) arg));
439 Console.WriteLine (sb.ToString ());
442 static public string PrintCollection (ICollection collection)
444 StringBuilder sb = new StringBuilder ();
446 sb.Append (collection.GetType ());
450 foreach (object o in collection) {
459 return sb.ToString ();
463 public enum TimerType {
473 public enum CounterType {
482 static DateTime[] timer_start;
483 static TimeSpan[] timers;
484 static long[] timer_counters;
485 static long[] counters;
489 timer_start = new DateTime [(int) TimerType.CountTimers];
490 timers = new TimeSpan [(int) TimerType.CountTimers];
491 timer_counters = new long [(int) TimerType.CountTimers];
492 counters = new long [(int) CounterType.CountCounters];
494 for (int i = 0; i < (int) TimerType.CountTimers; i++) {
495 timer_start [i] = DateTime.Now;
496 timers [i] = TimeSpan.Zero;
500 [Conditional("TIMER")]
501 static public void IncrementCounter (CounterType which)
503 ++counters [(int) which];
506 [Conditional("TIMER")]
507 static public void StartTimer (TimerType which)
509 timer_start [(int) which] = DateTime.Now;
512 [Conditional("TIMER")]
513 static public void StopTimer (TimerType which)
515 timers [(int) which] += DateTime.Now - timer_start [(int) which];
516 ++timer_counters [(int) which];
519 [Conditional("TIMER")]
520 static public void ShowTimers ()
522 ShowTimer (TimerType.FindMembers, "- FindMembers timer");
523 ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer");
524 ShowTimer (TimerType.MemberLookup, "- MemberLookup timer");
525 ShowTimer (TimerType.CachedLookup, "- CachedLookup timer");
526 ShowTimer (TimerType.CacheInit, "- Cache init");
527 ShowTimer (TimerType.MiscTimer, "- Misc timer");
529 ShowCounter (CounterType.FindMembers, "- Find members");
530 ShowCounter (CounterType.MemberCache, "- Member cache");
531 ShowCounter (CounterType.MiscCounter, "- Misc counter");
534 static public void ShowCounter (CounterType which, string msg)
536 Console.WriteLine ("{0} {1}", counters [(int) which], msg);
539 static public void ShowTimer (TimerType which, string msg)
542 "[{0:00}:{1:000}] {2} (used {3} times)",
543 (int) timers [(int) which].TotalSeconds,
544 timers [(int) which].Milliseconds, msg,
545 timer_counters [(int) which]);
549 public class InternalErrorException : Exception {
550 public InternalErrorException ()
551 : base ("Internal error")
555 public InternalErrorException (string message)
562 /// Handles #pragma warning
564 public class WarningRegions {
566 abstract class PragmaCmd
570 protected PragmaCmd (int line)
575 public abstract bool IsEnabled (int code, bool previous);
578 class Disable: PragmaCmd
581 public Disable (int line, int code)
587 public override bool IsEnabled (int code, bool previous)
589 return this.code == code ? false : previous;
593 class DisableAll: PragmaCmd
595 public DisableAll (int line)
598 public override bool IsEnabled(int code, bool previous)
604 class Enable: PragmaCmd
607 public Enable (int line, int code)
613 public override bool IsEnabled(int code, bool previous)
615 return this.code == code ? true : previous;
619 class EnableAll: PragmaCmd
621 public EnableAll (int line)
624 public override bool IsEnabled(int code, bool previous)
631 ArrayList regions = new ArrayList ();
633 public void WarningDisable (int line)
635 regions.Add (new DisableAll (line));
638 public void WarningDisable (Location location, int code)
640 if (CheckWarningCode (code, location))
641 regions.Add (new Disable (location.Row, code));
644 public void WarningEnable (int line)
646 regions.Add (new EnableAll (line));
649 public void WarningEnable (Location location, int code)
651 if (CheckWarningCode (code, location))
652 regions.Add (new Enable (location.Row, code));
655 public bool IsWarningEnabled (int code, int src_line)
658 foreach (PragmaCmd pragma in regions) {
659 if (src_line < pragma.Line)
662 result = pragma.IsEnabled (code, result);
667 bool CheckWarningCode (int code, Location loc)
669 if (Report.IsValidWarning (code))
672 Report.Warning (1691, 1, loc, "'{0}' is not a valid warning number", code);