Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/build.d
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,7 @@ auto sourceFiles()
statement.h staticassert.h target.h template.h tokens.h version.h visitor.h
"),
lexer: fileArray(env["D"], "
console.d entity.d errors.d file_manager.d globals.d id.d identifier.d lexer.d location.d tokens.d
console.d entity.d errors.d errorsink.d file_manager.d globals.d id.d identifier.d lexer.d location.d tokens.d
") ~ fileArray(env["ROOT"], "
array.d bitarray.d ctfloat.d file.d filename.d hash.d port.d region.d rmem.d
rootobject.d stringtable.d utf.d
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ Note that these groups have no strict meaning, the category assignments are a bi
| [dinifile.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) |
| [vsoptions.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking |
| [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library |
| [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting functionality |
| [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting implementation |
| [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface |
| [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
| [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |

Expand Down
16 changes: 9 additions & 7 deletions compiler/src/dmd/cparse.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module dmd.cparse;
import core.stdc.stdio;
import core.stdc.string;
import dmd.astenums;
import dmd.errorsink;
import dmd.globals;
import dmd.id;
import dmd.identifier;
Expand Down Expand Up @@ -69,9 +70,10 @@ final class CParser(AST) : Parser!AST
OutBuffer* defines;

extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
ErrorSink errorSink,
const ref TARGET target, OutBuffer* defines) scope
{
super(_module, input, doDocComment);
super(_module, input, doDocComment, errorSink);

//printf("CParser.this()\n");
mod = _module;
Expand Down Expand Up @@ -273,7 +275,7 @@ final class CParser(AST) : Parser!AST
auto exp = cparseExpression();
if (token.value == TOK.identifier && exp.op == EXP.identifier)
{
error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
error(token.loc, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
nextToken();
}
else
Expand Down Expand Up @@ -754,7 +756,7 @@ final class CParser(AST) : Parser!AST
if (token.postfix)
{
if (token.postfix != postfix)
error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
postfix = token.postfix;
}

Expand Down Expand Up @@ -1948,7 +1950,7 @@ final class CParser(AST) : Parser!AST
case TOK.identifier:
if (s)
{
error("missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
goto Lend;
}
goto default;
Expand Down Expand Up @@ -2014,7 +2016,7 @@ final class CParser(AST) : Parser!AST
importBuiltins = true; // will need __va_list_tag
auto plLength = pl.length;
if (symbols.length != plLength)
error("%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);

/* Transfer the types and storage classes from symbols[] to pl[]
*/
Expand Down Expand Up @@ -5154,9 +5156,9 @@ final class CParser(AST) : Parser!AST
if (n.value == TOK.identifier && n.ident == Id.show)
{
if (packalign.isDefault())
warning(startloc, "current pack attribute is default");
eSink.warning(startloc, "current pack attribute is default");
else
warning(startloc, "current pack attribute is %d", packalign.get());
eSink.warning(startloc, "current pack attribute is %d", packalign.get());
scan(&n);
return closingParen();
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.errors;
import dmd.errorsink;
import dmd.expression;
import dmd.expressionsem;
import dmd.file_manager;
Expand Down Expand Up @@ -766,7 +767,7 @@ extern (C++) final class Module : Package
{
filetype = FileType.c;

scope p = new CParser!AST(this, buf, cast(bool) docfile, target.c, &defines);
scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines);
p.nextToken();
checkCompiledImport();
members = p.parseModule();
Expand All @@ -775,7 +776,7 @@ extern (C++) final class Module : Package
}
else
{
scope p = new Parser!AST(this, buf, cast(bool) docfile);
scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink);
p.nextToken();
p.parseModuleDeclaration();
md = p.md;
Expand Down Expand Up @@ -1382,7 +1383,7 @@ extern (C++) struct ModuleDeclaration
* for inclusion in ModuleInfo
* Params:
* mod = the Module
* aclasses = array to fill in
* aclasses = array to fill in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change, should be its own commit

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another case of tabs being converted to spaces by my checkin procedure. Not worth another PR.

* Returns: array of local classes
*/
extern (C++) void getLocalClasses(Module mod, ref ClassDeclarations aclasses)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/doc.d
Original file line number Diff line number Diff line change
Expand Up @@ -5183,6 +5183,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of
uint errorsave = global.startGagging();

scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1,
global.errorSink,
global.vendor, global.versionNumber());
OutBuffer res;
const(char)* lastp = cast(char*)buf[].ptr;
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
const len = buf.length;
buf.writeByte(0);
const str = buf.extractSlice()[0 .. len];
scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false);
scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink);
p.nextToken();

auto d = p.parseDeclDefs(0);
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -7561,7 +7561,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
//printf("\t staticIfDg on '%s %s' in '%s'\n", s.kind(), s.toChars(), this.toChars());
if (!s.isStaticIfDeclaration())
{
//s.dsymbolSemantic(sc2);
//s.dsymbolSemantic(sc2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, own commit

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That came about because of my checkin process converts all tabs to spaces, and apparently there were tabs on that line. Being a bit of trivia, there's no issue here.

done = true;
return;
}
Expand Down
54 changes: 53 additions & 1 deletion compiler/src/dmd/errors.d
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import core.stdc.stdarg;
import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.string;
import dmd.errorsink;
import dmd.globals;
import dmd.location;
import dmd.common.outbuffer;
Expand All @@ -24,6 +25,57 @@ import dmd.console;

nothrow:

/***************************
* Error message sink for D compiler.
*/
class ErrorSinkCompiler : ErrorSink
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's the right abstraction either. What about messages issued by pragma(msg) ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it not the right abstraction?

What has pragma(msg) got to do with this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args)
{
    if (!args)
        return true;
    foreach (arg; *args)
    {
        sc = sc.startCTFE();
        auto e = arg.expressionSemantic(sc);
        e = resolveProperties(sc, e);
        sc = sc.endCTFE();

        // pragma(msg) is allowed to contain types as well as expressions
        e = ctfeInterpretForPragmaMsg(e);
        if (e.op == EXP.error)
        {
            errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
            return false;
        }
        if (auto se = e.toStringExp())
        {
            const slice = se.toUTF8(sc).peekString();
            fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
        }
        else
            fprintf(stderr, "%s", e.toChars());
    }
    fprintf(stderr, "\n");
    return true;
}

impl should really be using message, but anyway motivates the need for another category of output in this type of interface.

{
nothrow:
extern (C++):
override:

void error(const ref Loc loc, const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
verror(loc, format, ap);
va_end(ap);
}
Comment on lines +37 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dangerously close to hijacking gcc error diagnostics here. Or am I expected to just fork out the implementation to the C++ side of the compiler?


void errorSupplemental(const ref Loc loc, const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
verrorSupplemental(loc, format, ap);
va_end(ap);
}

void warning(const ref Loc loc, const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
vwarning(loc, format, ap);
va_end(ap);
}

void deprecation(const ref Loc loc, const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
vdeprecation(loc, format, ap);
va_end(ap);
}

void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
{
va_list ap;
va_start(ap, format);
vdeprecationSupplemental(loc, format, ap);
va_end(ap);
}
}


/**
* Color highlighting to classify messages
*/
Expand Down Expand Up @@ -768,7 +820,7 @@ private void colorHighlightCode(ref OutBuffer buf)
++nested;

auto gaggedErrorsSave = global.startGagging();
scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.vendor, global.versionNumber());
scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, global.vendor, global.versionNumber());
OutBuffer res;
const(char)* lastp = cast(char*)buf[].ptr;
//printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr);
Expand Down
121 changes: 121 additions & 0 deletions compiler/src/dmd/errorsink.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* Provides an abstraction for what to do with error messages.
*
* Copyright: Copyright (C) 2023 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errorsink.d, _errorsink.d)
* Documentation: https://dlang.org/phobos/dmd_errorsink.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errorsink.d
*/

module dmd.errorsink;

import dmd.location;

/***************************************
* Where error/warning/deprecation messages go.
*/
abstract class ErrorSink
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing wheels or explain why they are insufficient.

Such deficiencies might include dmd.frontend imports std.

Note also that dmd.frontend is a stable externally available API and so at worst this PR should move the definition from there to here, and have a publicly imported alias to the new place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse existing wheels or explain why they are insufficient.

This line illustrates the insufficiency:

Returns: false if the message should also be printed to stderr, true otherwise

This is the wrong abstraction. The user of Lexer should be able to send the errors anywhere, baking into the API "stderr" or not is insufficent. ErrorSink puts no constraint on where the messages should go. They can even go nowhere, as ErrorSinkNull demonstrates.

It's other insufficiency is it is centered around a pointer to a function which overrides the default behavior. This is a primitive form of OOP that fell out of favor in the 1980s. The abstract class, for which the user adds derived classes with specific behaviors, is the more modern approach. The abstract class designer does not have to anticipate what the implementation classes will be doing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to touch dmd.frontend in this PR. As I mentioned to @ibuclaw it needs a significant overhaul.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I consider this PR to require approval from @jacob-carlborg before merging.

don't want to touch dmd.frontend in this PR

I do not trust you will ever get around to this. And this adds yet more files without packaging things properly and I trust even less that you will ever do that.

They can even go nowhere, as ErrorSinkNull demonstrates.

So can DiagnositReporter, return true;.

As I mentioned to @ibuclaw it needs a significant overhaul.

Where? why? (Hint, no it doesn't).

{
nothrow:
extern (C++):

void error(const ref Loc loc, const(char)* format, ...);

void errorSupplemental(const ref Loc loc, const(char)* format, ...);

void warning(const ref Loc loc, const(char)* format, ...);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warningSupplemental?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only added the ones used.


void deprecation(const ref Loc loc, const(char)* format, ...);

void deprecationSupplemental(const ref Loc loc, const(char)* format, ...);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message should also be included.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only added the ones used. Others can be added as necessary.


/*****************************************
* Just ignores the messages.
*/
class ErrorSinkNull : ErrorSink
{
nothrow:
extern (C++):
override:

void error(const ref Loc loc, const(char)* format, ...) { }

void errorSupplemental(const ref Loc loc, const(char)* format, ...) { }

void warning(const ref Loc loc, const(char)* format, ...) { }

void deprecation(const ref Loc loc, const(char)* format, ...) { }

void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
}

/*****************************************
* Simplest implementation, just sends messages to stderr.
*/
class ErrorSinkStderr : ErrorSink
{
import core.stdc.stdio;
import core.stdc.stdarg;

nothrow:
extern (C++):
override:

void error(const ref Loc loc, const(char)* format, ...)
{
fputs("Error: ", stderr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't loc come before "Error:" etc?

const p = loc.toChars();
if (*p)
{
fprintf(stderr, "%s: ", p);
//mem.xfree(cast(void*)p); // loc should provide the free()
}

va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
va_end(ap);
}

void errorSupplemental(const ref Loc loc, const(char)* format, ...) { }

void warning(const ref Loc loc, const(char)* format, ...)
{
fputs("Warning: ", stderr);
const p = loc.toChars();
if (*p)
{
fprintf(stderr, "%s: ", p);
//mem.xfree(cast(void*)p); // loc should provide the free()
}

va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
va_end(ap);
}

void deprecation(const ref Loc loc, const(char)* format, ...)
{
fputs("Deprecation: ", stderr);
const p = loc.toChars();
if (*p)
{
fprintf(stderr, "%s: ", p);
//mem.xfree(cast(void*)p); // loc should provide the free()
}

va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
fputc('\n', stderr);
va_end(ap);
}

void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { }
}
2 changes: 1 addition & 1 deletion compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -6101,7 +6101,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
uint errors = global.errors;
const len = buf.length;
const str = buf.extractChars()[0 .. len];
scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false);
scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink);
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);

Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class ArrayInitializer;
class ExpInitializer;
class CInitializer;
class FileManager;
class ErrorSink;
class ErrorStatement;
class ExpStatement;
class ConditionalStatement;
Expand Down Expand Up @@ -3382,6 +3383,7 @@ struct Global final
FileManager* fileManager;
enum : int32_t { recursionLimit = 500 };

ErrorSink* errorSink;
FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* );
uint32_t startGagging();
bool endGagging(uint32_t oldGagged);
Expand All @@ -3408,10 +3410,11 @@ struct Global final
hasMainFunction(),
varSequenceNumber(1u),
fileManager(),
errorSink(),
preprocess()
{
}
Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array<const char* >* path = nullptr, Array<const char* >* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array<Identifier* >* versionids = nullptr, Array<Identifier* >* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) :
Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array<const char* >* path = nullptr, Array<const char* >* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array<Identifier* >* versionids = nullptr, Array<Identifier* >* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) :
inifilename(inifilename),
copyright(copyright),
written(written),
Expand All @@ -3430,6 +3433,7 @@ struct Global final
hasMainFunction(hasMainFunction),
varSequenceNumber(varSequenceNumber),
fileManager(fileManager),
errorSink(errorSink),
preprocess(preprocess)
{}
};
Expand Down
Loading