diff --git a/src/dmd/compiler.d b/src/dmd/compiler.d index 308547bedcfe..9e45b23f54e4 100644 --- a/src/dmd/compiler.d +++ b/src/dmd/compiler.d @@ -23,6 +23,7 @@ import dmd.globals; import dmd.id; import dmd.identifier; import dmd.mtype; +import dmd.lexer; import dmd.parse; import dmd.root.array; import dmd.root.ctfloat; @@ -86,7 +87,8 @@ struct Compiler }; Identifier id = Id.entrypoint; auto m = new Module("__entrypoint.d", id, 0, 0); - scope p = new Parser!ASTCodegen(m, cmaincode, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(m, cmaincode, false, diagnosticReporter); p.scanloc = Loc.initial; p.nextToken(); m.members = p.parseModule(); diff --git a/src/dmd/dmodule.d b/src/dmd/dmodule.d index 53015a262531..53a2bc3db0e8 100644 --- a/src/dmd/dmodule.d +++ b/src/dmd/dmodule.d @@ -32,6 +32,7 @@ import dmd.expressionsem; import dmd.globals; import dmd.id; import dmd.identifier; +import dmd.lexer; import dmd.parse; import dmd.root.file; import dmd.root.filename; @@ -850,7 +851,8 @@ extern (C++) final class Module : Package isHdrFile = true; } { - scope p = new Parser!ASTCodegen(this, buf[0 .. buflen], docfile !is null); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(this, buf[0 .. buflen], docfile !is null, diagnosticReporter); p.nextToken(); members = p.parseModule(); md = p.md; diff --git a/src/dmd/doc.d b/src/dmd/doc.d index b84ce5071009..a4e49130f1bf 100644 --- a/src/dmd/doc.d +++ b/src/dmd/doc.d @@ -3480,7 +3480,8 @@ private void highlightCode3(Scope* sc, OutBuffer* buf, const(char)* p, const(cha private void highlightCode2(Scope* sc, Dsymbols* a, OutBuffer* buf, size_t offset) { uint errorsave = global.startGagging(); - scope Lexer lex = new Lexer(null, cast(char*)buf.data, 0, buf.offset - 1, 0, 1); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope Lexer lex = new Lexer(null, cast(char*)buf.data, 0, buf.offset - 1, 0, 1, diagnosticReporter); OutBuffer res; const(char)* lastp = cast(char*)buf.data; //printf("highlightCode2('%.*s')\n", cast(int)(buf.offset - 1), buf.data); diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index 0768de084ac2..78638ada3a4d 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -47,6 +47,7 @@ import dmd.identifier; import dmd.init; import dmd.initsem; import dmd.hdrgen; +import dmd.lexer; import dmd.mtype; import dmd.nogc; import dmd.nspace; @@ -1880,7 +1881,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const errors = global.errors; const len = buf.offset; const str = buf.extractString()[0 .. len]; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, diagnosticReporter); p.nextToken(); auto d = p.parseDeclDefs(0); diff --git a/src/dmd/errors.d b/src/dmd/errors.d index b167c0e63bb9..064115e64d3f 100644 --- a/src/dmd/errors.d +++ b/src/dmd/errors.d @@ -518,7 +518,8 @@ private void colorHighlightCode(OutBuffer* buf) ++nested; auto gaggedErrorsSave = global.startGagging(); - scope Lexer lex = new Lexer(null, cast(char*)buf.data, 0, buf.offset - 1, 0, 1); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope Lexer lex = new Lexer(null, cast(char*)buf.data, 0, buf.offset - 1, 0, 1, diagnosticReporter); OutBuffer res; const(char)* lastp = cast(char*)buf.data; //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.offset - 1), buf.data); diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index 9f8fb82aacb1..8878b0ccbf4e 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -48,6 +48,7 @@ import dmd.identifier; import dmd.imphint; import dmd.inline; import dmd.intrange; +import dmd.lexer; import dmd.mtype; import dmd.nspace; import dmd.opover; @@ -5435,7 +5436,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.offset; const str = buf.extractString()[0 .. len]; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, diagnosticReporter); p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/src/dmd/frontend.d b/src/dmd/frontend.d index 3dac5a47d97d..fcdb55393d33 100644 --- a/src/dmd/frontend.d +++ b/src/dmd/frontend.d @@ -15,6 +15,8 @@ module dmd.frontend; import dmd.dmodule : Module; +import dmd.lexer : DiagnosticReporter; + import std.range.primitives : isInputRange, ElementType; import std.traits : isNarrowString; import std.typecons : Tuple; @@ -271,10 +273,21 @@ Parse a module from a string. Params: fileName = file to parse code = text to use instead of opening the file + diagnosticReporter = the diagnostic reporter to use. By default a + diagnostic reporter which prints to stderr will be used Returns: the parsed module object */ -Tuple!(Module, "module_", Diagnostics, "diagnostics") parseModule(const(char)[] fileName, const(char)[] code = null) +Tuple!(Module, "module_", Diagnostics, "diagnostics") parseModule( + const(char)[] fileName, + const(char)[] code = null, + DiagnosticReporter diagnosticReporter = defaultDiagnosticReporter +) +in +{ + assert(diagnosticReporter !is null); +} +body { import dmd.astcodegen : ASTCodegen; import dmd.globals : Loc, global; @@ -284,9 +297,9 @@ Tuple!(Module, "module_", Diagnostics, "diagnostics") parseModule(const(char)[] import std.string : toStringz; import std.typecons : tuple; - static auto parse(Module m, const(char)[] code) + static auto parse(Module m, const(char)[] code, DiagnosticReporter diagnosticReporter) { - scope p = new Parser!ASTCodegen(m, code, false); + scope p = new Parser!ASTCodegen(m, code, false, diagnosticReporter); p.nextToken; // skip the initial token auto members = p.parseModule; if (p.errors) @@ -297,7 +310,7 @@ Tuple!(Module, "module_", Diagnostics, "diagnostics") parseModule(const(char)[] Identifier id = Identifier.idPool(fileName); auto m = new Module(fileName.toStringz, id, 0, 0); if (code !is null) - m.members = parse(m, code); + m.members = parse(m, code, diagnosticReporter); else { m.read(Loc.initial); @@ -350,3 +363,11 @@ string prettyPrint(Module m) auto generated = buf.extractData.fromStringz.replace("\t", " "); return generated.assumeUnique; } + +private DiagnosticReporter defaultDiagnosticReporter() +{ + import dmd.globals : global; + import dmd.lexer : StderrDiagnosticReporter; + + return new StderrDiagnosticReporter(global.params.useDeprecated); +} diff --git a/src/dmd/iasmgcc.d b/src/dmd/iasmgcc.d index ded88d2a12b4..42d54213a52b 100644 --- a/src/dmd/iasmgcc.d +++ b/src/dmd/iasmgcc.d @@ -24,6 +24,7 @@ import dmd.expression; import dmd.expressionsem; import dmd.identifier; import dmd.globals; +import dmd.lexer; import dmd.parse; import dmd.tokens; import dmd.statement; @@ -283,7 +284,8 @@ Ldone: public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - scope p = new Parser!ASTCodegen(sc._module, ";", false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(sc._module, ";", false, diagnosticReporter); // Make a safe copy of the token list before parsing. Token *toklist = null; @@ -369,7 +371,8 @@ unittest static int semanticAsm(Token* tokens) { scope gas = new GccAsmStatement(Loc.initial, tokens); - scope p = new Parser!ASTCodegen(null, ";", false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(null, ";", false, diagnosticReporter); p.token = *tokens; p.parseGccAsm(gas); return p.errors; @@ -379,7 +382,8 @@ unittest static void parseAsm(string input, bool expectError) { // Generate tokens from input test. - scope p = new Parser!ASTCodegen(null, input, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(null, input, false, diagnosticReporter); p.nextToken(); Token* toklist = null; diff --git a/src/dmd/lexer.d b/src/dmd/lexer.d index e67090d0eae1..1ac15e45b50c 100644 --- a/src/dmd/lexer.d +++ b/src/dmd/lexer.d @@ -146,7 +146,8 @@ unittest /* Not much here, just trying things out. */ string text = "int"; // We rely on the implicit null-terminator - scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, 0, 0); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, 0, 0, diagnosticReporter); TOK tok; tok = lex1.nextToken(); //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32); @@ -181,7 +182,8 @@ unittest foreach (testcase; testcases) { - scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, 0, 0); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, 0, 0, diagnosticReporter); TOK tok = lex2.nextToken(); size_t iterations = 1; while ((tok != TOK.endOfFile) && (iterations++ < testcase.length)) @@ -194,32 +196,214 @@ unittest } } -/** -Handles error messages -*/ -class ErrorHandler +/// Interface for diagnostic reporting. +abstract class DiagnosticReporter { + /// Returns: the number of errors that occurred during lexing or parsing. + abstract int errorCount(); + + /// Returns: the number of warnings that occurred during lexing or parsing. + abstract int warningCount(); + + /// Returns: the number of deprecations that occurred during lexing or parsing. + abstract int deprecationCount(); + /** - Report an error message. + Reports an error message. + Params: + loc = Location of error format = format string for error ... = format string arguments */ - abstract void error(const(char)* format, ...); + final void error(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + error(loc, format, args); + va_end(args); + } + + /// ditto + abstract void error(const ref Loc loc, const(char)* format, va_list args); /** - Report an error message. + Reports additional details about an error message. + Params: loc = Location of error - format = format string for error + format = format string for supplemental message + ... = format string arguments + */ + final void errorSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + errorSupplemental(loc, format, args); + va_end(args); + } + + /// ditto + abstract void errorSupplemental(const ref Loc loc, const(char)* format, va_list); + + /** + Reports a warning message. + + Params: + loc = Location of warning + format = format string for warning + ... = format string arguments + */ + final void warning(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + warning(loc, format, args); + va_end(args); + } + + /// ditto + abstract void warning(const ref Loc loc, const(char)* format, va_list args); + + /** + Reports additional details about a warning message. + + Params: + loc = Location of warning + format = format string for supplemental message ... = format string arguments */ - abstract void error(Loc loc, const(char)* format, ...); + final void warningSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + warningSupplemental(loc, format, args); + va_end(args); + } + + /// ditto + abstract void warningSupplemental(const ref Loc loc, const(char)* format, va_list); + + /** + Reports a deprecation message. + + Params: + loc = Location of the deprecation + format = format string for the deprecation + ... = format string arguments + */ + final void deprecation(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + deprecation(loc, format, args); + va_end(args); + } + + /// ditto + abstract void deprecation(const ref Loc loc, const(char)* format, va_list args); + + /** + Reports additional details about a deprecation message. + + Params: + loc = Location of deprecation + format = format string for supplemental message + ... = format string arguments + */ + final void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + deprecationSupplemental(loc, format, args); + va_end(args); + } + + /// ditto + abstract void deprecationSupplemental(const ref Loc loc, const(char)* format, va_list); +} + +/** +Diagnostic reporter which prints the diagnostic messages to stderr. + +This is usually the default diagnostic reporter. +*/ +final class StderrDiagnosticReporter : DiagnosticReporter +{ + private const Diagnostic useDeprecated; + + private int errorCount_; + private int warningCount_; + private int deprecationCount_; + + /** + Initializes this object. + + Params: + useDeprecated = indicates how deprecation diagnostics should be + handled + */ + this(Diagnostic useDeprecated) + { + this.useDeprecated = useDeprecated; + } + + override int errorCount() + { + return errorCount_; + } + + override int warningCount() + { + return warningCount_; + } + + override int deprecationCount() + { + return deprecationCount_; + } + + override void error(const ref Loc loc, const(char)* format, va_list args) + { + verror(loc, format, args); + errorCount_++; + } + + override void errorSupplemental(const ref Loc loc, const(char)* format, va_list args) + { + verrorSupplemental(loc, format, args); + } + + override void warning(const ref Loc loc, const(char)* format, va_list args) + { + vwarning(loc, format, args); + warningCount_++; + } + + override void warningSupplemental(const ref Loc loc, const(char)* format, va_list args) + { + vwarningSupplemental(loc, format, args); + } + + override void deprecation(const ref Loc loc, const(char)* format, va_list args) + { + vdeprecation(loc, format, args); + + if (useDeprecated == Diagnostic.error) + errorCount_++; + else + deprecationCount_++; + } + + override void deprecationSupplemental(const ref Loc loc, const(char)* format, va_list args) + { + vdeprecationSupplemental(loc, format, args); + } } /*********************************************************** */ -class Lexer : ErrorHandler +class Lexer { __gshared OutBuffer stringbuffer; @@ -235,7 +419,8 @@ class Lexer : ErrorHandler bool anyToken; // seen at least one token bool commentToken; // comments are TOK.comment's int lastDocLine; // last line of previous doc comment - bool errors; // errors occurred during lexing or parsing + + private DiagnosticReporter diagnosticReporter; private Token* tokenFreelist; @@ -250,9 +435,18 @@ class Lexer : ErrorHandler * endoffset = the last offset to read into base[] * doDocComment = handle documentation comments * commentToken = comments become TOK.comment's + * diagnosticReporter = the diagnostic reporter to use */ - this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken) + this(const(char)* filename, const(char)* base, size_t begoffset, + size_t endoffset, bool doDocComment, bool commentToken, + DiagnosticReporter diagnosticReporter) + in + { + assert(diagnosticReporter !is null); + } + body { + this.diagnosticReporter = diagnosticReporter; scanloc = Loc(filename, 1, 1); //printf("Lexer::Lexer(%p,%d)\n",base,length); //printf("lexer.filename = %s\n", filename); @@ -290,6 +484,12 @@ class Lexer : ErrorHandler } } + /// Returns: `true` if any errors occurred during lexing or parsing. + final bool errors() + { + return diagnosticReporter.errorCount > 0; + } + /// Returns: a newly allocated `Token`. Token* allocateToken() pure nothrow @safe { @@ -1152,19 +1352,25 @@ class Lexer : ErrorHandler */ final uint escapeSequence() { - return Lexer.escapeSequence(this, p); + return Lexer.escapeSequence(token.loc, diagnosticReporter, p); } /** Parse the given string literal escape sequence into a single character. Params: - handler = the error handler object + loc = the location of the current token + handler = the diagnostic reporter object sequence = pointer to string with escape sequence to parse. this is a reference variable that is also used to return the position after the sequence Returns: the escaped sequence as a single character */ - static dchar escapeSequence(ErrorHandler handler, ref const(char)* sequence) + static dchar escapeSequence(const ref Loc loc, DiagnosticReporter handler, ref const(char)* sequence) + in + { + assert(handler !is null); + } + body { const(char)* p = sequence; // cache sequence reference on stack scope(exit) sequence = p; @@ -1230,20 +1436,20 @@ class Lexer : ErrorHandler break; if (!ishex(cast(char)c)) { - handler.error("escape hex sequence has %d hex digits instead of %d", n, ndigits); + handler.error(loc, "escape hex sequence has %d hex digits instead of %d", n, ndigits); break; } } if (ndigits != 2 && !utf_isValidDchar(v)) { - handler.error("invalid UTF character \\U%08x", v); + handler.error(loc, "invalid UTF character \\U%08x", v); v = '?'; // recover with valid UTF character } c = v; } else { - handler.error("undefined escape hex sequence \\%c%c", sequence[0], c); + handler.error(loc, "undefined escape hex sequence \\%c%c", sequence[0], c); p++; } break; @@ -1257,7 +1463,7 @@ class Lexer : ErrorHandler c = HtmlNamedEntity(idstart, p - idstart); if (c == ~0) { - handler.error("unnamed character entity &%.*s;", cast(int)(p - idstart), idstart); + handler.error(loc, "unnamed character entity &%.*s;", cast(int)(p - idstart), idstart); c = '?'; } p++; @@ -1265,7 +1471,7 @@ class Lexer : ErrorHandler default: if (isalpha(*p) || (p != idstart && isdigit(*p))) continue; - handler.error("unterminated named entity &%.*s;", cast(int)(p - idstart + 1), idstart); + handler.error(loc, "unterminated named entity &%.*s;", cast(int)(p - idstart + 1), idstart); c = '?'; break; } @@ -1290,11 +1496,11 @@ class Lexer : ErrorHandler while (++n < 3 && isoctal(cast(char)c)); c = v; if (c > 0xFF) - handler.error("escape octal sequence \\%03o is larger than \\377", c); + handler.error(loc, "escape octal sequence \\%03o is larger than \\377", c); } else { - handler.error("undefined escape sequence \\%c", c); + handler.error(loc, "undefined escape sequence \\%c", c); p++; } break; @@ -2270,32 +2476,68 @@ class Lexer : ErrorHandler return scanloc; } - final override void error(const(char)* format, ...) + final void error(const(char)* format, ...) + { + va_list args; + va_start(args, format); + diagnosticReporter.error(token.loc, format, args); + va_end(args); + } + + final void error(const ref Loc loc, const(char)* format, ...) { - va_list ap; - va_start(ap, format); - .verror(token.loc, format, ap); - va_end(ap); - errors = true; + va_list args; + va_start(args, format); + diagnosticReporter.error(loc, format, args); + va_end(args); } - final override void error(Loc loc, const(char)* format, ...) + final void errorSupplemental(const ref Loc loc, const(char)* format, ...) { - va_list ap; - va_start(ap, format); - .verror(loc, format, ap); - va_end(ap); - errors = true; + va_list args; + va_start(args, format); + diagnosticReporter.errorSupplemental(loc, format, args); + va_end(args); + } + + final void warning(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + diagnosticReporter.warning(loc, format, args); + va_end(args); + } + + final void warningSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + diagnosticReporter.warningSupplemental(loc, format, args); + va_end(args); } final void deprecation(const(char)* format, ...) { - va_list ap; - va_start(ap, format); - .vdeprecation(token.loc, format, ap); - va_end(ap); - if (global.params.useDeprecated == Diagnostic.error) - errors = true; + va_list args; + va_start(args, format); + diagnosticReporter.deprecation(token.loc, format, args); + va_end(args); + } + + final void deprecation(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + diagnosticReporter.deprecation(loc, format, args); + va_end(args); + } + + final void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + { + va_list args; + va_start(args, format); + diagnosticReporter.deprecationSupplemental(loc, format, args); + va_end(args); } /********************************************* @@ -2619,16 +2861,23 @@ private: unittest { - static class AssertErrorHandler : ErrorHandler + static final class AssertDiagnosticReporter : DiagnosticReporter { - override final void error(const(char)* format, ...) { assert(0); } - override final void error(Loc loc, const(char)* format, ...) { assert(0); } + override int errorCount() { assert(0); } + override int warningCount() { assert(0); } + override int deprecationCount() { assert(0); } + override void error(const ref Loc, const(char)*, va_list) { assert(0); } + override void errorSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } + override void warning(const ref Loc, const(char)*, va_list) { assert(0); } + override void warningSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } + override void deprecation(const ref Loc, const(char)*, va_list) { assert(0); } + override void deprecationSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } } static void test(T)(string sequence, T expected) { - scope assertOnError = new AssertErrorHandler(); + scope assertOnError = new AssertDiagnosticReporter(); auto p = cast(const(char)*)sequence.ptr; - assert(expected == Lexer.escapeSequence(assertOnError, p)); + assert(expected == Lexer.escapeSequence(Loc.initial, assertOnError, p)); assert(p == sequence.ptr + sequence.length); } @@ -2667,28 +2916,35 @@ unittest } unittest { - static class ExpectErrorHandler : ErrorHandler + static final class ExpectDiagnosticReporter : DiagnosticReporter { string expected; bool gotError; this(string expected) { this.expected = expected; } - override final void error(const(char)* format, ...) + + override int errorCount() { assert(0); } + override int warningCount() { assert(0); } + override int deprecationCount() { assert(0); } + + override void error(const ref Loc loc, const(char)* format, va_list args) { gotError = true; char[100] buffer; - va_list ap; - va_start(ap, format); - auto actual = buffer[0 .. vsprintf(buffer.ptr, format, ap)]; - va_end(ap); + auto actual = buffer[0 .. vsprintf(buffer.ptr, format, args)]; assert(expected == actual); } - override final void error(Loc loc, const(char)* format, ...) { assert(0); } + + override void errorSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } + override void warning(const ref Loc, const(char)*, va_list) { assert(0); } + override void warningSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } + override void deprecation(const ref Loc, const(char)*, va_list) { assert(0); } + override void deprecationSupplemental(const ref Loc, const(char)*, va_list) { assert(0); } } static void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength) { - scope handler = new ExpectErrorHandler(expectedError); + scope handler = new ExpectDiagnosticReporter(expectedError); auto p = cast(const(char)*)sequence.ptr; - auto actualReturnValue = Lexer.escapeSequence(handler, p); + auto actualReturnValue = Lexer.escapeSequence(Loc.initial, handler, p); assert(handler.gotError); assert(expectedReturnValue == actualReturnValue); diff --git a/src/dmd/parse.d b/src/dmd/parse.d index e2181bd91e4d..d9b3ed127c47 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -281,9 +281,10 @@ final class Parser(AST) : Lexer * Input: * loc location in source file of mixin */ - extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment) + extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, + bool doDocComment, DiagnosticReporter diagnosticReporter) { - super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false); + super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, diagnosticReporter); //printf("Parser::Parser()\n"); scanloc = loc; @@ -303,9 +304,9 @@ final class Parser(AST) : Lexer //nextToken(); // start up the scanner } - extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment) + extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, DiagnosticReporter diagnosticReporter) { - super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false); + super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, diagnosticReporter); //printf("Parser::Parser()\n"); mod = _module; diff --git a/src/dmd/statement.d b/src/dmd/statement.d index 954360559a98..ae60f71a843c 100644 --- a/src/dmd/statement.d +++ b/src/dmd/statement.d @@ -39,6 +39,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.dinterpret; +import dmd.lexer; import dmd.mtype; import dmd.parse; import dmd.root.outbuffer; @@ -811,7 +812,8 @@ extern (C++) final class CompileStatement : Statement const errors = global.errors; const len = buf.offset; const str = buf.extractString()[0 .. len]; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTCodegen(loc, sc._module, str, false, diagnosticReporter); p.nextToken(); auto a = new Statements(); diff --git a/src/examples/avg.d b/src/examples/avg.d index a76b3f5545f5..4942dfbc878d 100755 --- a/src/examples/avg.d +++ b/src/examples/avg.d @@ -10,6 +10,7 @@ dependency "dmd" path="../.." module examples.avg; import dmd.astbase; +import dmd.lexer; import dmd.parse; import dmd.transitivevisitor; @@ -58,7 +59,8 @@ void main() auto m = new ASTBase.Module(&(fname.dup)[0], id, false, false); auto input = readText(fname); - scope p = new Parser!ASTBase(m, input, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTBase(m, input, false, diagnosticReporter); p.nextToken(); m.members = p.parseModule(); diff --git a/src/examples/impvisitor.d b/src/examples/impvisitor.d index b42069430650..c2bf60e51e44 100755 --- a/src/examples/impvisitor.d +++ b/src/examples/impvisitor.d @@ -77,6 +77,7 @@ void main() import std.file; import std.path : buildPath, dirName; + import dmd.lexer; import dmd.parse; import dmd.astbase; @@ -108,7 +109,8 @@ void main() auto input = readText(fn); //writeln("Started parsing..."); - scope p = new Parser!ASTBase(m, input, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope p = new Parser!ASTBase(m, input, false, diagnosticReporter); p.nextToken(); m.members = p.parseModule(); //writeln("Finished parsing. Starting transitive visitor"); diff --git a/test/dub_package/lexer.d b/test/dub_package/lexer.d index ea749ee80344..5f44f092090e 100755 --- a/test/dub_package/lexer.d +++ b/test/dub_package/lexer.d @@ -4,6 +4,7 @@ dependency "dmd" path="../.." +/ void main() { + import dmd.globals; import dmd.lexer; import dmd.tokens; @@ -17,7 +18,8 @@ void main() ]; immutable sourceCode = "void test() {} // foobar"; - scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0, diagnosticReporter); lexer.nextToken; TOK[] result; diff --git a/test/dub_package/parser.d b/test/dub_package/parser.d index b997724ddeb0..31be55ff4e29 100755 --- a/test/dub_package/parser.d +++ b/test/dub_package/parser.d @@ -5,8 +5,11 @@ dependency "dmd" path="../.." void main() { import dmd.astbase; + import dmd.globals; + import dmd.lexer; import dmd.parse; - scope parser = new Parser!ASTBase(null, null, false); + scope diagnosticReporter = new StderrDiagnosticReporter(global.params.useDeprecated); + scope parser = new Parser!ASTBase(null, null, false, diagnosticReporter); assert(parser !is null); } diff --git a/test/unit/lexer/diagnostic_reporter.d b/test/unit/lexer/diagnostic_reporter.d new file mode 100644 index 000000000000..0a9a91db8e94 --- /dev/null +++ b/test/unit/lexer/diagnostic_reporter.d @@ -0,0 +1,82 @@ +module lexer.diagnostic_reporter; + +import core.stdc.stdarg; + +import dmd.globals : Loc; +import dmd.lexer : DiagnosticReporter; + +import support : afterEach, NoopDiagnosticReporter; + +@afterEach deinitializeFrontend() +{ + import dmd.frontend : deinitializeDMD; + deinitializeDMD(); +} + +@("errors: unterminated /* */ comment") +unittest +{ + static final class ErrorCountingDiagnosticReporter : NoopDiagnosticReporter + { + int errorCount; + + override void error(const ref Loc, const(char)*, va_list) + { + errorCount++; + } + } + + scope reporter = new ErrorCountingDiagnosticReporter; + lexUntilEndOfFile("/*", reporter); + + assert(reporter.errorCount == 1); +} + +@("warnings: C preprocessor directive") +unittest +{ + static final class WarningCountingDiagnosticReporter : NoopDiagnosticReporter + { + int warningCount; + + override void warning(const ref Loc, const(char)*, va_list) + { + warningCount++; + } + } + + scope reporter = new WarningCountingDiagnosticReporter; + lexUntilEndOfFile(`#foo`, reporter); + + assert(reporter.warningCount == 1); +} + +@("deprecations: hex string literal") +unittest +{ + static final class DeprecationsCountingDiagnosticReporter : NoopDiagnosticReporter + { + int deprecationCount; + + override void deprecation(const ref Loc, const(char)*, va_list) + { + deprecationCount++; + } + } + + scope reporter = new DeprecationsCountingDiagnosticReporter; + lexUntilEndOfFile(`enum a = x"60";`, reporter); + + assert(reporter.deprecationCount == 1); +} + +private void lexUntilEndOfFile(string code, DiagnosticReporter reporter) +{ + import dmd.lexer : Lexer; + import dmd.tokens : TOK; + + scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, reporter); + lexer.nextToken; + + while (lexer.nextToken != TOK.endOfFile) {} +} diff --git a/test/unit/parser/diagnostic_reporter.d b/test/unit/parser/diagnostic_reporter.d new file mode 100644 index 000000000000..5f67ad805012 --- /dev/null +++ b/test/unit/parser/diagnostic_reporter.d @@ -0,0 +1,119 @@ +module parser.diagnostic_reporter; + +import core.stdc.stdarg; + +import dmd.frontend : parseModule; +import dmd.globals : Loc; + +import support : afterEach, beforeEach, NoopDiagnosticReporter; + +@beforeEach initializeFrontend() +{ + import dmd.frontend : initDMD; + initDMD(); +} + +@afterEach deinitializeFrontend() +{ + import dmd.frontend : deinitializeDMD; + deinitializeDMD(); +} + +@("errors: duplicated `deprecated` attribute for module declaration") +unittest +{ + static class ErrorCountingDiagnosticReporter : NoopDiagnosticReporter + { + int errorCount; + + override void error(const ref Loc, const(char)*, va_list) + { + errorCount++; + } + } + + scope reporter = new ErrorCountingDiagnosticReporter; + + parseModule("test.d", q{ + deprecated deprecated module test; + }, reporter); + + assert(reporter.errorCount == 1); +} + +@("errors supplemental: there's no `static else`, use `else` instead") +unittest +{ + static class ErrorSupplementalCountingDiagnosticReporter : NoopDiagnosticReporter + { + int supplementalCount; + + override void errorSupplemental(const ref Loc, const(char)*, va_list) + { + supplementalCount++; + } + } + + scope reporter = new ErrorSupplementalCountingDiagnosticReporter; + + parseModule("test.d", q{ + void main() + { + static if (true) {} + static else {} + } + }, reporter); + + assert(reporter.supplementalCount == 1); +} + +@("warnings: dangling else") +unittest +{ + static class WarningCountingDiagnosticReporter : NoopDiagnosticReporter + { + int warningCount; + + override void warning(const ref Loc, const(char)*, va_list) + { + warningCount++; + } + } + + scope reporter = new WarningCountingDiagnosticReporter; + + parseModule("test.d", q{ + void main() + { + if (true) + if (false) + assert(3); + else + assert(4); + } + }, reporter); + + assert(reporter.warningCount == 1); +} + +@("deprecations: extern(Pascal)") +unittest +{ + static class DeprecationsCountingDiagnosticReporter : NoopDiagnosticReporter + { + int deprecationCount; + + override void deprecation(const ref Loc, const(char)*, va_list) + { + deprecationCount++; + } + } + + scope reporter = new DeprecationsCountingDiagnosticReporter; + + parseModule("test.d", q{ + extern (Pascal) void foo(); + }, reporter); + + assert(reporter.deprecationCount == 1); +} diff --git a/test/unit/support.d b/test/unit/support.d index 6f13f5d7a487..857495563f7a 100644 --- a/test/unit/support.d +++ b/test/unit/support.d @@ -1,5 +1,7 @@ module support; +import dmd.lexer : DiagnosticReporter; + /// UDA used to indicate a function should be run before each test. enum beforeEach; @@ -21,3 +23,19 @@ string[] defaultImportPaths() environment.get("PHOBOS_PATH", phobosDir) ]; } + +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 void error(const ref Loc loc, const(char)* format, va_list) {} + override void errorSupplemental(const ref Loc loc, const(char)* format, va_list) {} + override void warning(const ref Loc loc, const(char)* format, va_list) {} + override void warningSupplemental(const ref Loc loc, const(char)* format, va_list) {} + override void deprecation(const ref Loc loc, const(char)* format, va_list) {} + override void deprecationSupplemental(const ref Loc loc, const(char)* format, va_list) {} +}