// 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; } }