Skip to content
Open
63 changes: 31 additions & 32 deletions VSRAD.Package/BuildTools/Errors/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
using static VSRAD.BuildTools.IPCBuildResult;

namespace VSRAD.Package.BuildTools.Errors
Expand All @@ -15,21 +16,17 @@ public static ICollection<Message> ParseStderr(IEnumerable<string> outputs)
{
using (var reader = new StringReader(output))
{
Message lastMessage = null;

string line;
while ((line = reader.ReadLine()) != null)
{
var message = ParseScriptMessage(line) ?? ParseKeywordMessage(line) ?? ParseClangMessage(line);
var message =
ParseAsmMessage(line) ??
ParseScriptMessage(line) ??
ParseClangMessage(line);
if (message != null)
{
lastMessage = message;
messages.Add(message);
}
else if (lastMessage != null)
{
lastMessage.Text += Environment.NewLine + line;
}
else if (messages.Count > 0)
messages.Last().Text += Environment.NewLine + line;
}
}
}
Expand All @@ -53,51 +50,53 @@ private static Message ParseClangMessage(string header)
};
}

private static readonly Regex ScriptErrorRegex = new Regex(
private static readonly Regex AsmErrorRegex = new Regex(
@"\*(?<kind>[EW]),(?<code>[^:(]+)(?>\s\((?<file>.+):(?<line>\d+)\))?:\s(?<text>.+)", RegexOptions.Compiled);

private static readonly Regex ScriptErrorLocationInTextRegex = new Regex(
@"(?<text>.+)\s\((?<file>.+):(?<line>\d+)\)", RegexOptions.Compiled);

private static Message ParseScriptMessage(string header)
private static Message ParseAsmMessage(string header)
{
var match = ScriptErrorRegex.Match(header);
var match = AsmErrorRegex.Match(header);
if (!match.Success) return null;

var code = match.Groups["code"].Value;
var textAndMaybeLocation = match.Groups["text"].Value;

var message = new Message { Kind = ParseMessageKind(match.Groups["kind"].Value) };

if (!match.Groups["file"].Success)
match = ScriptErrorLocationInTextRegex.Match(textAndMaybeLocation);
var kind = ParseMessageKind(match.Groups["kind"].Value);

if (match.Success)
{
message.SourceFile = match.Groups["file"].Value;
message.Line = int.Parse(match.Groups["line"].Value);
var source = match.Groups["file"].Success
? match.Groups["file"].Value.Trim()
: "";
var line = match.Groups["line"].Success
? int.Parse(match.Groups["line"].Value)
: 0;
return new Message {
Kind = kind,
SourceFile = source,
Line = line,
Text = code + ": " + match.Groups["text"].Value
};
}
else
{
return new Message { Kind = kind, Text = code + ": " + textAndMaybeLocation };
}

message.Text = code + ": " + (match.Success ? match.Groups["text"].Value : textAndMaybeLocation);

return message;
}

private static readonly Regex KeywordErrorRegex = new Regex(
private static readonly Regex ScriptErrorRegex = new Regex(
@"(?<kind>ERROR|WARNING):\s*(?<text>.+)", RegexOptions.Compiled);

private static Message ParseKeywordMessage(string header)
private static Message ParseScriptMessage(string header)
{
var match = KeywordErrorRegex.Match(header);
var match = ScriptErrorRegex.Match(header);
if (!match.Success) return null;

var message = new Message
return new Message
{
Kind = ParseMessageKind(match.Groups["kind"].Value),
Text = match.Groups["text"].Value
};

return message;
}

private static MessageKind ParseMessageKind(string kind)
Expand Down
46 changes: 20 additions & 26 deletions VSRAD.PackageTests/BuildTools/Errors/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ public class ParserTests
printf(""h"");
^
C:\Absolute\Path\host.c:4:2: note: include the header<stdio.h> or explicitly provide a declaration for 'printf'

input.s:16:10: fatal error: 'abcde.s' file not found
#include ""abcde.s""
^~~~~~~~~
";
^~~~~~~~~";

public static readonly Message[] ClangExpectedMessages = new Message[]
{
Expand All @@ -39,7 +37,7 @@ public class ParserTests
printf(""h"");
^"},
new Message { Kind = MessageKind.Note, Line = 4, Column = 2, SourceFile = @"C:\Absolute\Path\host.c", Text =
"include the header<stdio.h> or explicitly provide a declaration for 'printf'\r\n" },
"include the header<stdio.h> or explicitly provide a declaration for 'printf'" },
new Message { Kind = MessageKind.Error, Line = 16, Column = 10, SourceFile = "input.s", Text =
@"'abcde.s' file not found
#include ""abcde.s""
Expand All @@ -53,43 +51,39 @@ public void ClangErrorTest()
Assert.Equal(ClangExpectedMessages, messages);
}

public const string ScriptStderr = @"
*E,fatal: undefined reference to 'printf' (<stdin>:3)
*E,fatal: undefined reference to 'printf' (C:\Absolute\Path\source.c:3)
*W,undefined: 1 undefined references found
public const string AsmStderr =
@"*W,undefined: 1 undefined references found
*E,syntax error (<stdin>:12): at symbol 'printf'
parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM
did you really mean to use the scope resolution op here?
*E,fatal (C:\Absolute\Path\source.c:35): Uncaught error: Undefined variable: user
";
*W,undefined: undefined reference to 'printf'";

public static readonly Message[] ScriptExpectedMessages = new Message[]
public static readonly Message[] AsmExpectedMessages = new Message[]
{
new Message { Kind = MessageKind.Error, Line = 3, SourceFile = "<stdin>", Text = "fatal: undefined reference to 'printf'"},
new Message { Kind = MessageKind.Error, Line = 3, SourceFile = @"C:\Absolute\Path\source.c", Text = "fatal: undefined reference to 'printf'"},
new Message { Kind = MessageKind.Warning, Line = 0, SourceFile = "", Text = "undefined: 1 undefined references found" },
new Message { Kind = MessageKind.Error, Line = 12, SourceFile = "<stdin>", Text =
@"syntax error: at symbol 'printf'
parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM
did you really mean to use the scope resolution op here?" },
new Message { Kind = MessageKind.Error, Line = 35, SourceFile = @"C:\Absolute\Path\source.c", Text = "fatal: Uncaught error: Undefined variable: user" }
new Message { Kind = MessageKind.Error, Line = 35, SourceFile = @"C:\Absolute\Path\source.c", Text = "fatal: Uncaught error: Undefined variable: user" },
new Message { Kind = MessageKind.Warning, Line = 0, SourceFile = "", Text = "undefined: undefined reference to 'printf'" }
};

[Fact]
public void ScriptErrorTest()
public void AsmErrorTest()
{
var messages = ParseStderr(new string[] { ScriptStderr }).ToArray();
Assert.Equal(ScriptExpectedMessages, messages);
var messages = ParseStderr(new string[] { AsmStderr }).ToArray();
Assert.Equal(AsmExpectedMessages, messages);
}

public const string KeywordStderr = @"
*E,fatal: undefined reference to 'printf' (<stdin>:3)
public const string ScriptStderr =
@"*E,fatal (<stdin>:3): undefined reference to 'printf'
ERROR: check if app exists and can be executed 'C:\NEVER\GONNA\GIVE\YOU\UP.exe'
WARNING: you are incredibly beautiful!
*E,fatal (auth.c:35): Uncaught error: Undefined variable: user
";
*E,fatal (auth.c:35): Uncaught error: Undefined variable: user";

public static readonly Message[] KeywordErrorExpectedMessages = new Message[]
public static readonly Message[] ScriptErrorExpectedMessages = new Message[]
{
new Message { Kind = MessageKind.Error, Line = 3, SourceFile = "<stdin>", Text = "fatal: undefined reference to 'printf'" },
new Message { Kind = MessageKind.Error, Line = 0, SourceFile = "", Text = @"check if app exists and can be executed 'C:\NEVER\GONNA\GIVE\YOU\UP.exe'" },
Expand All @@ -98,18 +92,18 @@ public void ScriptErrorTest()
};

[Fact]
public void KeywordErrorTest()
public void ScriptErrorTest()
{
var messages = ParseStderr(new string[] { KeywordStderr }).ToArray();
Assert.Equal(KeywordErrorExpectedMessages, messages);
var messages = ParseStderr(new string[] { ScriptStderr }).ToArray();
Assert.Equal(ScriptErrorExpectedMessages, messages);
}

[Fact]
public void MixedErrorFormatsTest()
{
var expectedMessages = ClangExpectedMessages.Concat(KeywordErrorExpectedMessages).Concat(ScriptExpectedMessages).ToArray();
var expectedMessages = ClangExpectedMessages.Concat(ScriptErrorExpectedMessages).Concat(AsmExpectedMessages).ToArray();

var separateOutputs = new[] { ClangStderr, KeywordStderr, ScriptStderr };
var separateOutputs = new[] { ClangStderr, ScriptStderr, AsmStderr };
var separateOutputsMessages = ParseStderr(separateOutputs).ToArray();
Assert.Equal(expectedMessages, separateOutputsMessages);

Expand Down