mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
Improve linker error suggestions (#21109)
- Newer versions of ld already demangle D symbols, recognize those as D symbols instead of C. - Don't suggest -i if it's already used - Add special hint for missing std/core symbols when using betterC - Simplify unittest to string compare the full error text instead of trying to half-parse the sentences
This commit is contained in:
parent
712cc9bf36
commit
c3923870ee
1 changed files with 91 additions and 28 deletions
|
@ -17,6 +17,7 @@ import core.stdc.stdlib;
|
|||
import core.stdc.string;
|
||||
|
||||
import dmd.astenums;
|
||||
import dmd.compiler : includeImports;
|
||||
import dmd.dmdparams;
|
||||
import dmd.errors;
|
||||
import dmd.errorsink;
|
||||
|
@ -333,7 +334,7 @@ public int runLINK(bool verbose, ErrorSink eSink)
|
|||
lnkfilename.toCStringThen!(lf => remove(lf.ptr));
|
||||
FileName.free(lnkfilename.ptr);
|
||||
}
|
||||
parseLinkerOutput(cast(const(char)[]) buf.peekSlice(), new ErrorSinkCompiler());
|
||||
parseLinkerOutput(cast(const(char)[]) buf.peekSlice(), new ErrorSinkCompiler(), global.params.betterC, includeImports);
|
||||
return status;
|
||||
}
|
||||
else
|
||||
|
@ -689,7 +690,7 @@ public int runLINK(bool verbose, ErrorSink eSink)
|
|||
}
|
||||
else
|
||||
{
|
||||
parseLinkerOutput(cast(const(char)[]) outputBuf.peekSlice(), new ErrorSinkCompiler());
|
||||
parseLinkerOutput(cast(const(char)[]) outputBuf.peekSlice(), new ErrorSinkCompiler(), global.params.betterC, includeImports);
|
||||
eSink.error(Loc.initial, "linker exited with status %d", status);
|
||||
eSink.errorSupplemental(Loc.initial, "%s", linkerCommand);
|
||||
}
|
||||
|
@ -1409,8 +1410,10 @@ Translate linker output to more user-friendly error messages, by extracting mang
|
|||
Params:
|
||||
linkerOutput = text that the linker printed
|
||||
eSink = sink for translated errors
|
||||
betterC = whether the `-betterC` flag is set (to give more specific help when druntime symbols are missing)
|
||||
iFlag = whether the `-i` flag is set, to give a suggestion to use it if it is not already used
|
||||
*/
|
||||
void parseLinkerOutput(const(char)[] linkerOutput, ErrorSink eSink)
|
||||
void parseLinkerOutput(const(char)[] linkerOutput, ErrorSink eSink, bool betterC, bool iFlag)
|
||||
{
|
||||
// Some linkers quote symbols like `so' or 'so', strip the quotes
|
||||
static string unquote(string s)
|
||||
|
@ -1433,6 +1436,8 @@ void parseLinkerOutput(const(char)[] linkerOutput, ErrorSink eSink)
|
|||
|
||||
bool missingCSymbols = false;
|
||||
bool missingDsymbols = false;
|
||||
bool missingDruntimeSymbols = false;
|
||||
bool missingPhobosSymbols = false;
|
||||
bool missingMain = false;
|
||||
|
||||
void missingSymbol(const(char)[] name, const(char)[] referencedFrom)
|
||||
|
@ -1440,11 +1445,17 @@ void parseLinkerOutput(const(char)[] linkerOutput, ErrorSink eSink)
|
|||
import core.demangle: demangle;
|
||||
if (name.startsWith("__D"))
|
||||
name = name[1 .. $]; // MS LINK prepends underscore to the existing one
|
||||
auto sym = demangle(name);
|
||||
|
||||
bool alreadyDemangled = !!findSplit(name, ".");
|
||||
auto sym = alreadyDemangled ? name : demangle(name);
|
||||
|
||||
if (sym == "main")
|
||||
missingMain = true;
|
||||
else if (sym != name)
|
||||
else if (sym.startsWith("core."))
|
||||
missingDruntimeSymbols = true;
|
||||
else if (sym.startsWith("std."))
|
||||
missingPhobosSymbols = true;
|
||||
else if (sym != name && !alreadyDemangled)
|
||||
missingDsymbols = true;
|
||||
else
|
||||
missingCSymbols = true;
|
||||
|
@ -1519,8 +1530,21 @@ void parseLinkerOutput(const(char)[] linkerOutput, ErrorSink eSink)
|
|||
|
||||
if (missingMain)
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps define a `void main() {}` function or use the `-main` switch");
|
||||
else if (missingDruntimeSymbols || missingPhobosSymbols)
|
||||
{
|
||||
const(char)* missingLib = missingDruntimeSymbols ? "druntime" : "phobos";
|
||||
if (betterC)
|
||||
eSink.errorSupplemental(Loc.initial, "the `-betterC` flag prevents linking with %s", missingLib);
|
||||
else
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps there is a mismatch in compiler and %s version", missingLib);
|
||||
}
|
||||
else if (missingDsymbols)
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps `.d` files need to be added on the command line, or use `-i` to compile imports");
|
||||
{
|
||||
if (iFlag)
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps `.d` files need to be added on the command line");
|
||||
else
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps `.d` files need to be added on the command line, or use `-i` to compile imports");
|
||||
}
|
||||
else if (missingCSymbols)
|
||||
eSink.errorSupplemental(Loc.initial, "perhaps a library needs to be added with the `-L` flag or `pragma(lib, ...)`");
|
||||
}
|
||||
|
@ -1576,48 +1600,67 @@ clang: error: linker command failed with exit code 1 (use -v to see invocation)
|
|||
|
||||
class ErrorSinkTest : ErrorSinkNull
|
||||
{
|
||||
public int errorCount = 0;
|
||||
string expectedFormat = "undefined reference to `%.*s`";
|
||||
string[] expectedSymbols;
|
||||
|
||||
OutBuffer result;
|
||||
extern(C++): override:
|
||||
|
||||
void verror(Loc loc, const(char)* format, va_list ap)
|
||||
{
|
||||
assert(format[0 .. strlen(format)] == expectedFormat);
|
||||
const expectedSymbol = expectedSymbols[errorCount++];
|
||||
assert(va_arg!int(ap) == expectedSymbol.length);
|
||||
const actualSymbol = va_arg!(char*)(ap)[0 .. expectedSymbol.length];
|
||||
assert(actualSymbol == expectedSymbol, "expected " ~ expectedSymbol ~ ", not " ~ actualSymbol);
|
||||
result.writestring("Error: ");
|
||||
result.vprintf(format, ap);
|
||||
result.writestring("\n");
|
||||
}
|
||||
|
||||
void verrorSupplemental(Loc loc, const(char)* format, va_list ap)
|
||||
{
|
||||
assert(format.startsWith("perhaps") || format.startsWith("referenced from "));
|
||||
result.vprintf(format, ap);
|
||||
result.writestring("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void test(T...)(string linkerName, string output, T expectedSymbols)
|
||||
void test(string linkerName, string output, bool betterC, bool iFlag, string expectedErrors)
|
||||
{
|
||||
auto testSink = new ErrorSinkTest();
|
||||
testSink.expectedSymbols = [expectedSymbols];
|
||||
parseLinkerOutput(output, testSink);
|
||||
assert(testSink.errorCount > 0, "failed to demangle output of " ~ linkerName);
|
||||
parseLinkerOutput(output, testSink, betterC, iFlag);
|
||||
|
||||
string result = testSink.result.extractSlice;
|
||||
assert(result == expectedErrors, "Failure parsing linker output of " ~ linkerName ~
|
||||
"\n# Expected output:\n" ~ expectedErrors ~ "# Actual output:\n" ~ result);
|
||||
}
|
||||
|
||||
test("ld", ldOutput, "void app.f()");
|
||||
test("lld", lldOutput, "void app.f()");
|
||||
test("gold", goldOutput, "void app.f()");
|
||||
test("link", linkOutput, "void app.f()");
|
||||
test("ld", macLd0, "void app.f()", "void app.h()");
|
||||
test("ld", macLd1, "void app.f()");
|
||||
string expected = "Error: undefined reference to `void app.f()`
|
||||
referenced from `void app.g()`
|
||||
perhaps `.d` files need to be added on the command line, or use `-i` to compile imports
|
||||
";
|
||||
|
||||
test("ld", ldOutput, /*betterC*/ false, /*iFlag*/ false, expected);
|
||||
test("gold", goldOutput, /*betterC*/ false, /*iFlag*/ false, expected);
|
||||
test("link", linkOutput, /*betterC*/ false, /*iFlag*/ false, expected);
|
||||
|
||||
test("ld", macLd0, /*betterC*/ false, /*iFlag*/ false,
|
||||
"Error: undefined reference to `void app.f()`
|
||||
Error: undefined reference to `void app.h()`
|
||||
perhaps `.d` files need to be added on the command line, or use `-i` to compile imports
|
||||
");
|
||||
|
||||
test("ld", macLd1, /*betterC*/ false, /*iFlag*/ false,
|
||||
"Error: undefined reference to `void app.f()`
|
||||
perhaps `.d` files need to be added on the command line, or use `-i` to compile imports
|
||||
");
|
||||
|
||||
test("lld", lldOutput, /*betterC*/ false, /*iFlag*/ true,
|
||||
"Error: undefined reference to `void app.f()`
|
||||
perhaps `.d` files need to be added on the command line
|
||||
");
|
||||
|
||||
string missingMainOutput = "
|
||||
/usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../lib/Scrt1.o: in function `_start':
|
||||
(.text+0x1b): undefined reference to `main'
|
||||
";
|
||||
|
||||
test("ld", missingMainOutput, "main");
|
||||
test("ld", missingMainOutput, /*betterC*/ false, /*iFlag*/ false,
|
||||
"Error: undefined reference to `main`
|
||||
perhaps define a `void main() {}` function or use the `-main` switch
|
||||
");
|
||||
|
||||
string missingExternCOutput = "
|
||||
/usr/bin/ld: app.o: in function `_D3app__T2αVAyaa3_616263ZQrFZv':
|
||||
|
@ -1625,6 +1668,26 @@ clang: error: linker command failed with exit code 1 (use -v to see invocation)
|
|||
/usr/bin/ld: ../test/app.d:(.text._D3app__T2αVAyaa3_616263ZQrFZv[_D3app__T2αVAyaa3_616263ZQrFZv]+0xa): undefined reference to `my_B'
|
||||
";
|
||||
|
||||
test("ld", missingExternCOutput, "my_A", "my_B");
|
||||
test("ld", missingExternCOutput, /*betterC*/ true, /*iFlag*/ false,
|
||||
"Error: undefined reference to `my_A`
|
||||
referenced from `void app.α!(\"abc\").α()`
|
||||
Error: undefined reference to `my_B`
|
||||
referenced from `void app.α!(\"abc\").α()`
|
||||
perhaps a library needs to be added with the `-L` flag or `pragma(lib, ...)`
|
||||
");
|
||||
|
||||
string betterCError = "
|
||||
/usr/bin/ld: test_.o: in function `main':
|
||||
../test/test_.d:(.text.main[main]+0xa): undefined reference to `core.time.dur!(\"msecs\").dur(long)'
|
||||
/usr/bin/ld: ../test/test_.d:(.text.main[main]+0x12): undefined reference to `core.thread.osthread.Thread.sleep(core.time.Duration)'
|
||||
";
|
||||
|
||||
test("ld", betterCError, /*betterC*/ true, /*iFlag*/ false,
|
||||
"Error: undefined reference to `core.time.dur!(\"msecs\").dur(long)`
|
||||
referenced from `main`
|
||||
Error: undefined reference to `core.thread.osthread.Thread.sleep(core.time.Duration)`
|
||||
referenced from `main`
|
||||
the `-betterC` flag prevents linking with druntime
|
||||
");
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue