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.LookupTypeContainer (mi.DeclaringType);
294 if (temp_ds == null) {
295 SymbolRelatedToPreviousError (mi.DeclaringType.Assembly.Location, TypeManager.GetFullNameSignature (mi));
297 if (mi is MethodBase) {
298 IMethodData md = TypeManager.GetMethod ((MethodBase)mi);
299 SymbolRelatedToPreviousError (md.Location, md.GetSignatureForError (temp_ds));
303 string name = String.Concat (temp_ds.Name, ".", mi.Name);
304 MemberCore mc = temp_ds.GetDefinition (name);
305 SymbolRelatedToPreviousError (mc);
309 static public void SymbolRelatedToPreviousError (MemberCore mc)
311 SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ());
314 static public void SymbolRelatedToPreviousError (Type type)
316 if (type is TypeBuilder) {
317 DeclSpace temp_ds = TypeManager.LookupDeclSpace (type);
318 SymbolRelatedToPreviousError (temp_ds.Location, TypeManager.CSharpName (type));
319 } else if (type.HasElementType) {
320 SymbolRelatedToPreviousError (type.GetElementType ());
322 SymbolRelatedToPreviousError (type.Assembly.Location, TypeManager.CSharpName (type));
326 static void SymbolRelatedToPreviousError (string loc, string symbol)
328 extra_information.Add (String.Format ("{0}: '{1}' (name of symbol related to previous ", loc, symbol));
331 public static void ExtraInformation (Location loc, string msg)
333 extra_information.Add (String.Format ("{0}({1}) {2}", loc.Name, loc.Row, msg));
336 public static WarningRegions RegisterWarningRegion (Location location)
338 if (warning_regions_table == null)
339 warning_regions_table = new Hashtable ();
341 WarningRegions regions = (WarningRegions)warning_regions_table [location.Name];
342 if (regions == null) {
343 regions = new WarningRegions ();
344 warning_regions_table.Add (location.Name, regions);
349 static public void Warning (int code, int level, Location loc, string format, params object[] args)
351 WarningMessage w = new WarningMessage (level);
352 w.Print (code, loc, String.Format (format, args));
355 static public void Warning (int code, Location loc, string format, params object[] args)
357 WarningMessage w = new WarningMessage ();
358 w.Print (code, loc, String.Format (format, args));
361 static public void Warning (int code, string format, params object[] args)
363 Warning (code, Location.Null, String.Format (format, args));
367 /// Did you test your WarningLevel, that you use this method
369 static public void Warning (int code, string text)
371 Warning (code, Location.Null, text);
374 static public void Error (int code, string format, params object[] args)
376 Error (code, Location.Null, String.Format (format, args));
379 static public void Error (int code, Location loc, string format, params object[] args)
381 ErrorMessage e = new ErrorMessage ();
382 e.Print (code, loc, String.Format (format, args));
385 static public void SetIgnoreWarning (int code)
387 if (warning_ignore_table == null)
388 warning_ignore_table = new Hashtable ();
390 warning_ignore_table [code] = true;
393 static public int ExpectedError {
395 expected_error = value;
398 return expected_error;
402 public static int DebugFlags = 0;
404 [Conditional ("MCS_DEBUG")]
405 static public void Debug (string message, params object[] args)
407 Debug (4, message, args);
410 [Conditional ("MCS_DEBUG")]
411 static public void Debug (int category, string message, params object[] args)
413 if ((category & DebugFlags) == 0)
416 StringBuilder sb = new StringBuilder (message);
418 if ((args != null) && (args.Length > 0)) {
422 foreach (object arg in args) {
429 else if (arg is ICollection)
430 sb.Append (PrintCollection ((ICollection) arg));
436 Console.WriteLine (sb.ToString ());
439 static public string PrintCollection (ICollection collection)
441 StringBuilder sb = new StringBuilder ();
443 sb.Append (collection.GetType ());
447 foreach (object o in collection) {
456 return sb.ToString ();
460 public enum TimerType {
470 public enum CounterType {
479 static DateTime[] timer_start;
480 static TimeSpan[] timers;
481 static long[] timer_counters;
482 static long[] counters;
486 timer_start = new DateTime [(int) TimerType.CountTimers];
487 timers = new TimeSpan [(int) TimerType.CountTimers];
488 timer_counters = new long [(int) TimerType.CountTimers];
489 counters = new long [(int) CounterType.CountCounters];
491 for (int i = 0; i < (int) TimerType.CountTimers; i++) {
492 timer_start [i] = DateTime.Now;
493 timers [i] = TimeSpan.Zero;
497 [Conditional("TIMER")]
498 static public void IncrementCounter (CounterType which)
500 ++counters [(int) which];
503 [Conditional("TIMER")]
504 static public void StartTimer (TimerType which)
506 timer_start [(int) which] = DateTime.Now;
509 [Conditional("TIMER")]
510 static public void StopTimer (TimerType which)
512 timers [(int) which] += DateTime.Now - timer_start [(int) which];
513 ++timer_counters [(int) which];
516 [Conditional("TIMER")]
517 static public void ShowTimers ()
519 ShowTimer (TimerType.FindMembers, "- FindMembers timer");
520 ShowTimer (TimerType.TcFindMembers, "- TypeContainer.FindMembers timer");
521 ShowTimer (TimerType.MemberLookup, "- MemberLookup timer");
522 ShowTimer (TimerType.CachedLookup, "- CachedLookup timer");
523 ShowTimer (TimerType.CacheInit, "- Cache init");
524 ShowTimer (TimerType.MiscTimer, "- Misc timer");
526 ShowCounter (CounterType.FindMembers, "- Find members");
527 ShowCounter (CounterType.MemberCache, "- Member cache");
528 ShowCounter (CounterType.MiscCounter, "- Misc counter");
531 static public void ShowCounter (CounterType which, string msg)
533 Console.WriteLine ("{0} {1}", counters [(int) which], msg);
536 static public void ShowTimer (TimerType which, string msg)
539 "[{0:00}:{1:000}] {2} (used {3} times)",
540 (int) timers [(int) which].TotalSeconds,
541 timers [(int) which].Milliseconds, msg,
542 timer_counters [(int) which]);
546 public class InternalErrorException : Exception {
547 public InternalErrorException ()
548 : base ("Internal error")
552 public InternalErrorException (string message)
559 /// Handles #pragma warning
561 public class WarningRegions {
563 abstract class PragmaCmd
567 protected PragmaCmd (int line)
572 public abstract bool IsEnabled (int code, bool previous);
575 class Disable: PragmaCmd
578 public Disable (int line, int code)
584 public override bool IsEnabled (int code, bool previous)
586 return this.code == code ? false : previous;
590 class DisableAll: PragmaCmd
592 public DisableAll (int line)
595 public override bool IsEnabled(int code, bool previous)
601 class Enable: PragmaCmd
604 public Enable (int line, int code)
610 public override bool IsEnabled(int code, bool previous)
612 return this.code == code ? true : previous;
616 class EnableAll: PragmaCmd
618 public EnableAll (int line)
621 public override bool IsEnabled(int code, bool previous)
628 ArrayList regions = new ArrayList ();
630 public void WarningDisable (int line)
632 regions.Add (new DisableAll (line));
635 public void WarningDisable (Location location, int code)
637 if (CheckWarningCode (code, location))
638 regions.Add (new Disable (location.Row, code));
641 public void WarningEnable (int line)
643 regions.Add (new EnableAll (line));
646 public void WarningEnable (Location location, int code)
648 if (CheckWarningCode (code, location))
649 regions.Add (new Enable (location.Row, code));
652 public bool IsWarningEnabled (int code, int src_line)
655 foreach (PragmaCmd pragma in regions) {
656 if (src_line < pragma.Line)
659 result = pragma.IsEnabled (code, result);
664 bool CheckWarningCode (int code, Location loc)
666 if (Report.IsValidWarning (code))
669 Report.Warning (1691, 1, loc, "'{0}' is not a valid warning number", code);