dmd/compiler/test/unit/support.d
Iain Buclaw 09d04945bd Fix build script paths to work with new merged repository structure
Co-Authored-By: Geod24 <pro.mathias.lang@gmail.com>
Co-Authored-By: Martin Kinkelin <noone@nowhere.com>
Co-Authored-By: Vladimir Panteleev <git@cy.md>
2022-07-09 23:49:27 +02:00

199 lines
5.4 KiB
D

// See ../README.md for information about DMD unit tests.
module support;
import core.stdc.stdarg : va_list;
import dmd.console : Color;
import dmd.frontend : DiagnosticReporter;
import dmd.globals : Loc;
/// UDA used to indicate a function should be run before each test.
enum beforeEach;
/// UDA used to indicate a function should be run after each test.
enum afterEach;
/// Returns: the default import paths, i.e. for Phobos and druntime.
string[] defaultImportPaths()
{
import std.path : buildNormalizedPath, buildPath, dirName;
import std.process : environment;
enum dlangDir = __FILE_FULL_PATH__.dirName.buildNormalizedPath("..", "..", "..");
enum druntimeDir = dlangDir.buildPath("druntime", "import");
enum phobosDir = dlangDir.buildPath("..", "phobos");
return [
environment.get("DRUNTIME_PATH", druntimeDir),
environment.get("PHOBOS_PATH", phobosDir)
];
}
/**
* Strips indentation and extra newlines in delimited strings.
*
* This is indented to be used on delimited string literals. It will strip the
* indentation and remove the first and the last newlines.
*
* Params:
* str = the delimited string to strip
*
* Return: the stripped string
*/
string stripDelimited(string str)
{
import std.string : chomp, outdent;
import dmd.root.string : stripLeadingLineTerminator;
return str
.stripLeadingLineTerminator
.outdent
.chomp;
}
const struct CompilationResult
{
import dmd.dmodule : Module;
Diagnostic[] diagnostics;
Module module_;
alias diagnostics this;
bool opCast(T : bool)()
{
return diagnostics.length == 0;
}
string toString()
{
import std.format : format;
return format!"%(%s\n%)"(diagnostics);
}
}
/**
* Compiles the given code.
*
* This will run the frontend up to, including, the semantic analysis.
*
* Params:
* code = the code to compile
* filename = the filename to use when compiling the code
*
* Returns: the diagnostics reported during the compilation
*/
CompilationResult compiles(string code, string filename = "test.d")
{
import dmd.globals : global;
import std.algorithm : each;
import dmd.frontend : addImport, fullSemantic, parseModule;
import dmd.errors : diagnosticHandler;
defaultImportPaths.each!addImport;
auto diagnosticCollector = DiagnosticCollector();
diagnosticHandler = &diagnosticCollector.handleDiagnostic;
auto t = parseModule(filename, code);
if (t.diagnostics.hasErrors)
return CompilationResult(diagnosticCollector.diagnostics);
t.module_.fullSemantic();
return CompilationResult(diagnosticCollector.diagnostics, t.module_);
}
class NoopDiagnosticReporter : DiagnosticReporter
{
import core.stdc.stdarg : va_list;
import dmd.globals : Loc;
override int errorCount() { return 0; }
override int warningCount() { return 0; }
override int deprecationCount() { return 0; }
override bool error(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
override bool errorSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
override bool warning(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
override bool warningSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
override bool deprecation(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
override bool deprecationSupplemental(const ref Loc loc, const(char)* format, va_list, const(char)* p1, const(char)* p2) { return true; }
}
/// A single diagnostic.
const struct Diagnostic
{
/// The location of the diagnostic.
Loc location;
/// The diagnostic message.
string message;
string toString() nothrow
{
import dmd.common.outbuffer : OutBuffer;
auto buffer = OutBuffer();
buffer.printf("%s: %.*s", location.toChars(true),
cast(int) message.length, message.ptr);
return buffer.extractSlice.idup;
}
}
/// Collects all diagnostics that are reported.
struct DiagnosticCollector
{
private Diagnostic[] diagnostics_;
/// Returns: the collected diagnostics
const(Diagnostic[]) diagnostics() const pure nothrow @nogc @safe
{
return diagnostics_;
}
/// Handles a diagnostic.
bool handleDiagnostic (
const ref Loc location,
Color headerColor,
const(char)* header,
const(char)* messageFormat,
va_list args,
const(char)* prefix1,
const(char)* prefix2
) nothrow
{
import std.array : replace;
import std.string : strip;
import dmd.common.outbuffer : OutBuffer;
auto buffer = OutBuffer();
void appendPrefix(const(char)* prefix)
{
if (!prefix)
return;
buffer.writestring(prefix);
buffer.writestring(" ");
}
buffer.writestring(header);
appendPrefix(prefix1);
appendPrefix(prefix2);
buffer.vprintf(messageFormat, args);
const string message = buffer
.extractSlice.idup
.replace("`", "")
.strip;
diagnostics_ ~= Diagnostic(location, message);
return true;
}
}