summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Relocations.cpp40
-rw-r--r--lld/test/ELF/undef-suggest-extern-c2.s21
2 files changed, 57 insertions, 4 deletions
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 54035d94926..98abbe13fba 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -697,11 +697,26 @@ struct UndefinedDiag {
static std::vector<UndefinedDiag> undefs;
+// Check whether the definition name def is a mangled function name that matches
+// the reference name ref.
+static bool canSuggestExternCForCXX(StringRef ref, StringRef def) {
+ llvm::ItaniumPartialDemangler d;
+ if (d.partialDemangle(def.str().c_str()))
+ return false;
+ char *buf = d.getFunctionName(nullptr, nullptr);
+ if (!buf)
+ return false;
+ bool ret = ref == buf;
+ free(buf);
+ return ret;
+}
+
// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
// the suggested symbol, which is either in the symbol table, or in the same
// file of sym.
static const Symbol *getAlternativeSpelling(const Undefined &sym,
- std::string &pre_hint) {
+ std::string &pre_hint,
+ std::string &post_hint) {
// Build a map of local defined symbols.
DenseMap<StringRef, const Symbol *> map;
if (sym.file && !isa<SharedFile>(sym.file)) {
@@ -774,6 +789,23 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym,
return s;
}
}
+ } else {
+ const Symbol *s = nullptr;
+ for (auto &it : map)
+ if (canSuggestExternCForCXX(name, it.first)) {
+ s = it.second;
+ break;
+ }
+ if (!s)
+ symtab->forEachSymbol([&](Symbol *sym) {
+ if (!s && canSuggestExternCForCXX(name, sym->getName()))
+ s = sym;
+ });
+ if (s) {
+ pre_hint = " to declare ";
+ post_hint = " as extern \"C\"?";
+ return s;
+ }
}
return nullptr;
@@ -822,10 +854,10 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
.str();
if (correctSpelling) {
- std::string pre_hint = ": ";
+ std::string pre_hint = ": ", post_hint;
if (const Symbol *corrected =
- getAlternativeSpelling(cast<Undefined>(sym), pre_hint)) {
- msg += "\n>>> did you mean" + pre_hint + toString(*corrected);
+ getAlternativeSpelling(cast<Undefined>(sym), pre_hint, post_hint)) {
+ msg += "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint;
if (corrected->file)
msg += "\n>>> defined in: " + toString(corrected->file);
}
diff --git a/lld/test/ELF/undef-suggest-extern-c2.s b/lld/test/ELF/undef-suggest-extern-c2.s
new file mode 100644
index 00000000000..f017e38c249
--- /dev/null
+++ b/lld/test/ELF/undef-suggest-extern-c2.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+## The definition is mangled while the reference is not, suggest an arbitrary
+## C++ overload.
+# RUN: echo '.globl _Z3fooi; _Z3fooi:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
+# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s
+
+## Check that we can suggest a local definition.
+# RUN: echo '_Z3fooi: call foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
+# RUN: not ld.lld %t2.o -o /dev/null 2>&1 | FileCheck %s
+
+# CHECK: error: undefined symbol: foo
+# CHECK-NEXT: >>> referenced by {{.*}}
+# CHECK-NEXT: >>> did you mean to declare foo(int) as extern "C"?
+
+## Don't suggest nested names whose base name is "foo", e.g. F::foo().
+# RUN: echo '.globl _ZN1F3fooEv; _ZN1F3fooEv:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t3.o
+# RUN: not ld.lld %t.o %t3.o -o /dev/null 2>&1 | FileCheck /dev/null --implicit-check-not='did you mean'
+
+call foo
OpenPOWER on IntegriCloud