diff --git a/src/analysis/helpers.d b/src/analysis/helpers.d index f379118..e976170 100644 --- a/src/analysis/helpers.d +++ b/src/analysis/helpers.d @@ -54,7 +54,7 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, StringCache cache = StringCache(StringCache.defaultBucketCount); RollbackAllocator r; const(Token)[] tokens; - const(Module) m = parseModule(file, cast(ubyte[]) code, &r, cache, false, tokens); + const(Module) m = parseModule(file, cast(ubyte[]) code, &r, defaultErrorFormat, cache, false, tokens); auto moduleCache = ModuleCache(new CAllocatorImpl!Mallocator); diff --git a/src/analysis/run.d b/src/analysis/run.d index b3f13d6..ebec954 100644 --- a/src/analysis/run.d +++ b/src/analysis/run.d @@ -87,9 +87,24 @@ bool first = true; private alias ASTAllocator = CAllocatorImpl!( AllocatorList!(n => Region!Mallocator(1024 * 128), Mallocator)); +immutable string defaultErrorFormat = "{filepath}({line}:{column})[{type}]: {message}"; + +void messageFunctionFormat(string format, string fileName, size_t line, size_t column, string message, bool isError) +{ + auto s = format; + + s = s.replace("{filepath}", fileName); + s = s.replace("{line}", to!string(line)); + s = s.replace("{column}", to!string(column)); + s = s.replace("{type}", isError ? "error" : "warn"); + s = s.replace("{message}", message); + + writefln("%s", s); +} + void messageFunction(string fileName, size_t line, size_t column, string message, bool isError) { - writefln("%s(%d:%d)[%s]: %s", fileName, line, column, isError ? "error" : "warn", message); + messageFunctionFormat(defaultErrorFormat, fileName, line, column, message, isError); } void messageFunctionJSON(string fileName, size_t line, size_t column, string message, bool) @@ -112,10 +127,10 @@ void writeJSON(string key, string fileName, size_t line, size_t column, string m write(" }"); } -bool syntaxCheck(string[] fileNames, ref StringCache stringCache, ref ModuleCache moduleCache) +bool syntaxCheck(string[] fileNames, string errorFormat, ref StringCache stringCache, ref ModuleCache moduleCache) { StaticAnalysisConfig config = defaultStaticAnalysisConfig(); - return analyze(fileNames, config, stringCache, moduleCache, false); + return analyze(fileNames, config, errorFormat, stringCache, moduleCache, false); } void generateReport(string[] fileNames, const StaticAnalysisConfig config, @@ -134,7 +149,7 @@ void generateReport(string[] fileNames, const StaticAnalysisConfig config, continue; RollbackAllocator r; const(Token)[] tokens; - const Module m = parseModule(fileName, code, &r, cache, true, tokens, &lineOfCodeCount); + const Module m = parseModule(fileName, code, &r, defaultErrorFormat, cache, true, tokens, &lineOfCodeCount); stats.visit(m); MessageSet results = analyze(fileName, m, config, moduleCache, tokens, true); foreach (result; results[]) @@ -160,7 +175,7 @@ void generateReport(string[] fileNames, const StaticAnalysisConfig config, * * Returns: true if there were errors or if there were warnings and `staticAnalyze` was true. */ -bool analyze(string[] fileNames, const StaticAnalysisConfig config, +bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat, ref StringCache cache, ref ModuleCache moduleCache, bool staticAnalyze = true) { bool hasErrors; @@ -174,7 +189,7 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config, uint errorCount; uint warningCount; const(Token)[] tokens; - const Module m = parseModule(fileName, code, &r, cache, false, tokens, + const Module m = parseModule(fileName, code, &r, errorFormat, cache, false, tokens, null, &errorCount, &warningCount); assert(m); if (errorCount > 0 || (staticAnalyze && warningCount > 0)) @@ -185,19 +200,22 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config, foreach (result; results[]) { hasErrors = true; - writefln("%s(%d:%d)[warn]: %s", result.fileName, result.line, - result.column, result.message); + messageFunctionFormat(errorFormat, result.fileName, result.line, result.column, result.message, false); } } return hasErrors; } const(Module) parseModule(string fileName, ubyte[] code, RollbackAllocator* p, - ref StringCache cache, bool report, ref const(Token)[] tokens, + string errorFormat, ref StringCache cache, bool report, ref const(Token)[] tokens, ulong* linesOfCode = null, uint* errorCount = null, uint* warningCount = null) { import stats : isLineOfCode; + auto writeMessages = delegate(string fileName, size_t line, size_t column, string message, bool isError){ + return messageFunctionFormat(errorFormat, fileName, line, column, message, isError); + }; + LexerConfig config; config.fileName = fileName; config.stringBehavior = StringBehavior.source; @@ -205,7 +223,7 @@ const(Module) parseModule(string fileName, ubyte[] code, RollbackAllocator* p, if (linesOfCode !is null) (*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens); return dparse.parser.parseModule(tokens, fileName, p, - report ? toDelegate(&messageFunctionJSON) : toDelegate(&messageFunction), + report ? toDelegate(&messageFunctionJSON) : writeMessages, errorCount, warningCount); } diff --git a/src/main.d b/src/main.d index 8f87c4a..c5e135a 100644 --- a/src/main.d +++ b/src/main.d @@ -67,6 +67,7 @@ else string[] importPaths; bool printVersion; bool explore; + string errorFormat; try { @@ -94,7 +95,8 @@ else "version", &printVersion, "muffinButton", &muffin, "explore", &explore, - "skipTests", &skipTests); + "skipTests", &skipTests, + "errorFormat|f", &errorFormat); //dfmt on } catch (ConvException e) @@ -141,6 +143,9 @@ else return 0; } + if (!errorFormat.length) + errorFormat = defaultErrorFormat; + const(string[]) absImportPaths = importPaths.map!(a => a.absolutePath() .buildNormalizedPath()).array(); @@ -234,11 +239,11 @@ else if (report) generateReport(expandArgs(args), config, cache, moduleCache); else - return analyze(expandArgs(args), config, cache, moduleCache, true) ? 1 : 0; + return analyze(expandArgs(args), config, errorFormat, cache, moduleCache, true) ? 1 : 0; } else if (syntaxCheck) { - return .syntaxCheck(usingStdin ? ["stdin"] : expandArgs(args), cache, moduleCache) ? 1 : 0; + return .syntaxCheck(usingStdin ? ["stdin"] : expandArgs(args), errorFormat, cache, moduleCache) ? 1 : 0; } else {