+*/
+ }
+
+ public abstract class AbstractMessage
+ {
+ readonly string[] extra_info;
+ protected readonly int code;
+ protected readonly Location location;
+ readonly string message;
+
+ protected AbstractMessage (int code, Location loc, string msg, List<string> extraInfo)
+ {
+ this.code = code;
+ if (code < 0)
+ this.code = 8000 - code;
+
+ this.location = loc;
+ this.message = msg;
+ if (extraInfo.Count != 0) {
+ this.extra_info = extraInfo.ToArray ();
+ }
+ }
+
+ protected AbstractMessage (AbstractMessage aMsg)
+ {
+ this.code = aMsg.code;
+ this.location = aMsg.location;
+ this.message = aMsg.message;
+ this.extra_info = aMsg.extra_info;
+ }
+
+ public int Code {
+ get { return code; }
+ }
+
+ public override bool Equals (object obj)
+ {
+ AbstractMessage msg = obj as AbstractMessage;
+ if (msg == null)
+ return false;
+
+ return code == msg.code && location.Equals (msg.location) && message == msg.message;
+ }
+
+ public override int GetHashCode ()
+ {
+ return code.GetHashCode ();
+ }
+
+ public abstract bool IsWarning { get; }
+
+ public Location Location {
+ get { return location; }
+ }
+
+ public abstract string MessageType { get; }
+
+ public string[] RelatedSymbols {
+ get { return extra_info; }
+ }
+
+ public string Text {
+ get { return message; }
+ }
+ }
+
+ sealed class WarningMessage : AbstractMessage
+ {
+ public WarningMessage (int code, Location loc, string message, List<string> extra_info)
+ : base (code, loc, message, extra_info)
+ {
+ }
+
+ public override bool IsWarning {
+ get { return true; }
+ }
+
+ public override string MessageType {
+ get {
+ return "warning";
+ }
+ }
+ }
+
+ sealed class ErrorMessage : AbstractMessage
+ {
+ public ErrorMessage (int code, Location loc, string message, List<string> extraInfo)
+ : base (code, loc, message, extraInfo)
+ {
+ }
+
+ public ErrorMessage (AbstractMessage aMsg)
+ : base (aMsg)
+ {
+ }
+
+ public override bool IsWarning {
+ get { return false; }
+ }
+
+ public override string MessageType {
+ get {
+ return "error";
+ }
+ }
+ }
+
+ //
+ // Generic base for any message writer
+ //
+ public abstract class ReportPrinter {
+ /// <summary>
+ /// Whether to dump a stack trace on errors.
+ /// </summary>
+ public bool Stacktrace;
+
+ int warnings, errors;
+
+ public int WarningsCount {
+ get { return warnings; }
+ }
+
+ public int ErrorsCount {
+ get { return errors; }
+ }
+
+ protected virtual string FormatText (string txt)
+ {
+ return txt;
+ }
+
+ //
+ // When (symbols related to previous ...) can be used
+ //
+ public virtual bool HasRelatedSymbolSupport {
+ get { return true; }
+ }
+
+ public virtual void Print (AbstractMessage msg)
+ {
+ if (msg.IsWarning)
+ ++warnings;
+ else
+ ++errors;
+ }
+
+ protected void Print (AbstractMessage msg, TextWriter output)
+ {
+ StringBuilder txt = new StringBuilder ();
+ if (!msg.Location.IsNull) {
+ txt.Append (msg.Location.ToString ());
+ txt.Append (" ");
+ }
+
+ txt.AppendFormat ("{0} CS{1:0000}: {2}", msg.MessageType, msg.Code, msg.Text);
+
+ if (!msg.IsWarning)
+ output.WriteLine (FormatText (txt.ToString ()));
+ else
+ output.WriteLine (txt.ToString ());
+
+ if (msg.RelatedSymbols != null) {
+ foreach (string s in msg.RelatedSymbols)
+ output.WriteLine (s + msg.MessageType + ")");
+ }
+ }
+ }
+
+ //
+ // Default message recorder, it uses two types of message groups.
+ // Common messages: messages reported in all sessions.
+ // Merged messages: union of all messages in all sessions.
+ //
+ // Used by the Lambda expressions to compile the code with various
+ // parameter values, or by attribute resolver
+ //
+ class SessionReportPrinter : ReportPrinter
+ {
+ List<AbstractMessage> session_messages;
+ //
+ // A collection of exactly same messages reported in all sessions
+ //
+ List<AbstractMessage> common_messages;
+
+ //
+ // A collection of unique messages reported in all sessions
+ //
+ List<AbstractMessage> merged_messages;
+
+ public override void Print (AbstractMessage msg)
+ {
+ //
+ // This line is useful when debugging recorded messages
+ //
+ // Console.WriteLine ("RECORDING: {0} {1} {2}", code, location, message);
+
+ if (session_messages == null)
+ session_messages = new List<AbstractMessage> ();
+
+ session_messages.Add (msg);
+
+ base.Print (msg);
+ }
+
+ public void EndSession ()
+ {
+ if (session_messages == null)
+ return;
+
+ //
+ // Handles the first session
+ //
+ if (common_messages == null) {
+ common_messages = new List<AbstractMessage> (session_messages);
+ merged_messages = session_messages;
+ session_messages = null;
+ return;
+ }
+
+ //
+ // Store common messages if any
+ //
+ for (int i = 0; i < common_messages.Count; ++i) {
+ AbstractMessage cmsg = (AbstractMessage) common_messages[i];
+ bool common_msg_found = false;
+ foreach (AbstractMessage msg in session_messages) {
+ if (cmsg.Equals (msg)) {
+ common_msg_found = true;
+ break;
+ }
+ }
+
+ if (!common_msg_found)
+ common_messages.RemoveAt (i);
+ }
+
+ //
+ // Merge session and previous messages
+ //
+ for (int i = 0; i < session_messages.Count; ++i) {
+ AbstractMessage msg = (AbstractMessage) session_messages[i];
+ bool msg_found = false;
+ for (int ii = 0; ii < merged_messages.Count; ++ii) {
+ if (msg.Equals (merged_messages[ii])) {
+ msg_found = true;
+ break;
+ }
+ }
+
+ if (!msg_found)
+ merged_messages.Add (msg);
+ }
+ }
+
+ public bool IsEmpty {
+ get {
+ return merged_messages == null && common_messages == null;
+ }
+ }
+
+ //
+ // Prints collected messages, common messages have a priority
+ //
+ public bool Merge (ReportPrinter dest)
+ {
+ var messages_to_print = merged_messages;
+ if (common_messages != null && common_messages.Count > 0) {
+ messages_to_print = common_messages;
+ }
+
+ if (messages_to_print == null)
+ return false;
+
+ foreach (AbstractMessage msg in messages_to_print)
+ dest.Print (msg);
+
+ return true;
+ }
+ }
+
+ class StreamReportPrinter : ReportPrinter
+ {
+ readonly TextWriter writer;
+
+ public StreamReportPrinter (TextWriter writer)
+ {
+ this.writer = writer;
+ }
+
+ public override void Print (AbstractMessage msg)
+ {
+ Print (msg, writer);
+ base.Print (msg);
+ }
+ }
+
+ class ConsoleReportPrinter : StreamReportPrinter
+ {
+ static readonly string prefix, postfix;
+
+ static ConsoleReportPrinter ()
+ {
+ string term = Environment.GetEnvironmentVariable ("TERM");
+ bool xterm_colors = false;
+
+ switch (term){
+ case "xterm":
+ case "rxvt":
+ case "rxvt-unicode":
+ if (Environment.GetEnvironmentVariable ("COLORTERM") != null){
+ xterm_colors = true;
+ }
+ break;
+
+ case "xterm-color":
+ xterm_colors = true;
+ break;
+ }
+ if (!xterm_colors)
+ return;
+
+ if (!(UnixUtils.isatty (1) && UnixUtils.isatty (2)))
+ return;
+
+ string config = Environment.GetEnvironmentVariable ("MCS_COLORS");
+ if (config == null){
+ config = "errors=red";
+ //config = "brightwhite,red";
+ }
+
+ if (config == "disable")
+ return;
+
+ if (!config.StartsWith ("errors="))
+ return;
+
+ config = config.Substring (7);
+
+ int p = config.IndexOf (",");
+ if (p == -1)
+ prefix = GetForeground (config);
+ else
+ prefix = GetBackground (config.Substring (p+1)) + GetForeground (config.Substring (0, p));
+ postfix = "\x001b[0m";
+ }
+
+ public ConsoleReportPrinter ()
+ : base (Console.Error)
+ {
+ }
+
+ public ConsoleReportPrinter (TextWriter writer)
+ : base (writer)
+ {
+ }
+
+ public int Fatal { get; set; }
+
+ static int NameToCode (string s)
+ {
+ switch (s) {
+ case "black":
+ return 0;
+ case "red":
+ return 1;
+ case "green":
+ return 2;
+ case "yellow":
+ return 3;
+ case "blue":
+ return 4;
+ case "magenta":
+ return 5;
+ case "cyan":
+ return 6;
+ case "grey":
+ case "white":
+ return 7;
+ }
+ return 7;
+ }
+
+ //
+ // maps a color name to its xterm color code
+ //
+ static string GetForeground (string s)
+ {
+ string highcode;
+
+ if (s.StartsWith ("bright")) {
+ highcode = "1;";
+ s = s.Substring (6);
+ } else
+ highcode = "";
+
+ return "\x001b[" + highcode + (30 + NameToCode (s)).ToString () + "m";
+ }
+
+ static string GetBackground (string s)
+ {
+ return "\x001b[" + (40 + NameToCode (s)).ToString () + "m";
+ }
+
+ protected override string FormatText (string txt)
+ {
+ if (prefix != null)
+ return prefix + txt + postfix;
+
+ return txt;
+ }
+
+ static string FriendlyStackTrace (StackTrace t)
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ bool foundUserCode = false;
+
+ for (int i = 0; i < t.FrameCount; i++) {
+ StackFrame f = t.GetFrame (i);
+ MethodBase mb = f.GetMethod ();
+
+ if (!foundUserCode && mb.ReflectedType == typeof (Report))
+ continue;
+
+ foundUserCode = true;
+
+ sb.Append ("\tin ");
+
+ if (f.GetFileLineNumber () > 0)
+ sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ());
+
+ sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name);
+
+ bool first = true;
+ foreach (ParameterInfo pi in mb.GetParameters ()) {
+ if (!first)
+ sb.Append (", ");
+ first = false;
+
+ sb.Append (pi.ParameterType.FullName);
+ }
+ sb.Append (")\n");
+ }
+
+ return sb.ToString ();
+ }
+
+ int print_count;
+ public override void Print (AbstractMessage msg)
+ {
+ base.Print (msg);
+
+ if (Stacktrace)
+ Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
+
+ if (++print_count == Fatal)
+ throw new Exception (msg.Text);
+ }
+
+ public static string FriendlyStackTrace (Exception e)
+ {
+ return FriendlyStackTrace (new StackTrace (e, true));
+ }
+
+ public static void StackTrace ()
+ {
+ Console.WriteLine (FriendlyStackTrace (new StackTrace (true)));
+ }