forked from OSchip/llvm-project
lld-link, clang: Treat non-existent input files as possible spellos for option flags
OptTable treats arguments starting with / that aren't a known option as filenames. This means lld-link's and clang-cl's typo correction for unknown flags didn't do spell checking for misspelled options that start with /. I first tried changing OptTable, but that got pretty messy, see PR41787 comments 2 and 3. Instead, let lld-link's and clang's (including clang-cl's) "file not found" diagnostic check if a non-existent file looks like it could be a mis-spelled option, and if so add a "did you mean" suggestion to the "file not found" diagnostic. While here, make formatting of a few diagnostics a bit more self-consistent. Fixes PR41787. Differential Revision: https://reviews.llvm.org/D62276 llvm-svn: 361518
This commit is contained in:
parent
2049e4dd8f
commit
9b2830b46e
|
@ -9,9 +9,11 @@
|
|||
let Component = "Driver" in {
|
||||
|
||||
def err_drv_no_such_file : Error<"no such file or directory: '%0'">;
|
||||
def err_drv_no_such_file_with_suggestion : Error<
|
||||
"no such file or directory: '%0'; did you mean '%1'?">;
|
||||
def err_drv_unsupported_opt : Error<"unsupported option '%0'">;
|
||||
def err_drv_unsupported_opt_with_suggestion
|
||||
: Error<"unsupported option '%0', did you mean '%1'?">;
|
||||
def err_drv_unsupported_opt_with_suggestion : Error<
|
||||
"unsupported option '%0'; did you mean '%1'?">;
|
||||
def err_drv_unsupported_opt_for_target : Error<
|
||||
"unsupported option '%0' for target '%1'">;
|
||||
def err_drv_unsupported_option_argument : Error<
|
||||
|
@ -166,13 +168,13 @@ def err_arch_unsupported_isa
|
|||
def err_drv_I_dash_not_supported : Error<
|
||||
"'%0' not supported, please use -iquote instead">;
|
||||
def err_drv_unknown_argument : Error<"unknown argument: '%0'">;
|
||||
def err_drv_unknown_argument_with_suggestion
|
||||
: Error<"unknown argument '%0', did you mean '%1'?">;
|
||||
def err_drv_unknown_argument_with_suggestion : Error<
|
||||
"unknown argument '%0'; did you mean '%1'?">;
|
||||
def warn_drv_unknown_argument_clang_cl : Warning<
|
||||
"unknown argument ignored in clang-cl: '%0'">,
|
||||
InGroup<UnknownArgument>;
|
||||
def warn_drv_unknown_argument_clang_cl_with_suggestion : Warning<
|
||||
"unknown argument ignored in clang-cl '%0' (did you mean '%1'?)">,
|
||||
"unknown argument ignored in clang-cl '%0'; did you mean '%1'?">,
|
||||
InGroup<UnknownArgument>;
|
||||
|
||||
def warn_drv_ycyu_different_arg_clang_cl : Warning<
|
||||
|
|
|
@ -394,6 +394,14 @@ public:
|
|||
void BuildUniversalActions(Compilation &C, const ToolChain &TC,
|
||||
const InputList &BAInputs) const;
|
||||
|
||||
/// Check that the file referenced by Value exists. If it doesn't,
|
||||
/// issue a diagnostic and return false.
|
||||
/// If TypoCorrect is true and the file does not exist, see if it looks
|
||||
/// like a likely typo for a flag and if so print a "did you mean" blurb.
|
||||
bool DiagnoseInputExistence(const llvm::opt::DerivedArgList &Args,
|
||||
StringRef Value, types::ID Ty,
|
||||
bool TypoCorrect) const;
|
||||
|
||||
/// BuildJobs - Bind actions to concrete tools and translate
|
||||
/// arguments to form the list of jobs to run.
|
||||
///
|
||||
|
|
|
@ -1975,11 +1975,9 @@ void Driver::BuildUniversalActions(Compilation &C, const ToolChain &TC,
|
|||
}
|
||||
}
|
||||
|
||||
/// Check that the file referenced by Value exists. If it doesn't,
|
||||
/// issue a diagnostic and return false.
|
||||
static bool DiagnoseInputExistence(const Driver &D, const DerivedArgList &Args,
|
||||
StringRef Value, types::ID Ty) {
|
||||
if (!D.getCheckInputsExist())
|
||||
bool Driver::DiagnoseInputExistence(const DerivedArgList &Args, StringRef Value,
|
||||
types::ID Ty, bool TypoCorrect) const {
|
||||
if (!getCheckInputsExist())
|
||||
return true;
|
||||
|
||||
// stdin always exists.
|
||||
|
@ -1995,10 +1993,10 @@ static bool DiagnoseInputExistence(const Driver &D, const DerivedArgList &Args,
|
|||
}
|
||||
}
|
||||
|
||||
if (D.getVFS().exists(Path))
|
||||
if (getVFS().exists(Path))
|
||||
return true;
|
||||
|
||||
if (D.IsCLMode()) {
|
||||
if (IsCLMode()) {
|
||||
if (!llvm::sys::path::is_absolute(Twine(Path)) &&
|
||||
llvm::sys::Process::FindInEnvPath("LIB", Value))
|
||||
return true;
|
||||
|
@ -2011,7 +2009,26 @@ static bool DiagnoseInputExistence(const Driver &D, const DerivedArgList &Args,
|
|||
}
|
||||
}
|
||||
|
||||
D.Diag(clang::diag::err_drv_no_such_file) << Path;
|
||||
if (TypoCorrect) {
|
||||
// Check if the filename is a typo for an option flag. OptTable thinks
|
||||
// that all args that are not known options and that start with / are
|
||||
// filenames, but e.g. `/diagnostic:caret` is more likely a typo for
|
||||
// the option `/diagnostics:caret` than a reference to a file in the root
|
||||
// directory.
|
||||
unsigned IncludedFlagsBitmask;
|
||||
unsigned ExcludedFlagsBitmask;
|
||||
std::tie(IncludedFlagsBitmask, ExcludedFlagsBitmask) =
|
||||
getIncludeExcludeOptionFlagMasks(IsCLMode());
|
||||
std::string Nearest;
|
||||
if (getOpts().findNearest(Value, Nearest, IncludedFlagsBitmask,
|
||||
ExcludedFlagsBitmask) <= 1) {
|
||||
Diag(clang::diag::err_drv_no_such_file_with_suggestion)
|
||||
<< Path << Nearest;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Diag(clang::diag::err_drv_no_such_file) << Path;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2128,19 +2145,21 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args,
|
|||
}
|
||||
}
|
||||
|
||||
if (DiagnoseInputExistence(*this, Args, Value, Ty))
|
||||
if (DiagnoseInputExistence(Args, Value, Ty, /*TypoCorrect=*/true))
|
||||
Inputs.push_back(std::make_pair(Ty, A));
|
||||
|
||||
} else if (A->getOption().matches(options::OPT__SLASH_Tc)) {
|
||||
StringRef Value = A->getValue();
|
||||
if (DiagnoseInputExistence(*this, Args, Value, types::TY_C)) {
|
||||
if (DiagnoseInputExistence(Args, Value, types::TY_C,
|
||||
/*TypoCorrect=*/false)) {
|
||||
Arg *InputArg = MakeInputArg(Args, *Opts, A->getValue());
|
||||
Inputs.push_back(std::make_pair(types::TY_C, InputArg));
|
||||
}
|
||||
A->claim();
|
||||
} else if (A->getOption().matches(options::OPT__SLASH_Tp)) {
|
||||
StringRef Value = A->getValue();
|
||||
if (DiagnoseInputExistence(*this, Args, Value, types::TY_CXX)) {
|
||||
if (DiagnoseInputExistence(Args, Value, types::TY_CXX,
|
||||
/*TypoCorrect=*/false)) {
|
||||
Arg *InputArg = MakeInputArg(Args, *Opts, A->getValue());
|
||||
Inputs.push_back(std::make_pair(types::TY_CXX, InputArg));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// RUN: FileCheck %s --check-prefix=CL
|
||||
// RUN: %clang_cl -Brepo -### -- %s 2>&1 | \
|
||||
// RUN: FileCheck %s --check-prefix=CL-DID-YOU-MEAN
|
||||
// RUN: %clang_cl /Brepo -### -- %s 2>&1 | \
|
||||
// RUN: FileCheck %s --check-prefix=CL-DID-YOU-MEAN-SLASH
|
||||
// RUN: not %clang_cl -cake-is-lie -%0 -%d -HHHH -munknown-to-clang-option -print-stats -funknown-to-clang-option -c -Werror=unknown-argument -### -- %s 2>&1 | \
|
||||
// RUN: FileCheck %s --check-prefix=CL-ERROR
|
||||
// RUN: not %clang_cl -helo -Werror=unknown-argument -### -- %s 2>&1 | \
|
||||
|
@ -28,9 +30,9 @@
|
|||
// CHECK: error: unknown argument: '-funknown-to-clang-option'
|
||||
// CHECK: error: unknown argument: '-ifoo'
|
||||
// MULTILIB: warning: argument unused during compilation: '-imultilib dir'
|
||||
// DID-YOU-MEAN: error: unknown argument '-stdlibs=foo', did you mean '-stdlib=foo'?
|
||||
// DID-YOU-MEAN: error: unknown argument '-hell', did you mean '-help'?
|
||||
// DID-YOU-MEAN: error: unknown argument '-version', did you mean '--version'?
|
||||
// DID-YOU-MEAN: error: unknown argument '-stdlibs=foo'; did you mean '-stdlib=foo'?
|
||||
// DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'?
|
||||
// DID-YOU-MEAN: error: unknown argument '-version'; did you mean '--version'?
|
||||
// CL: warning: unknown argument ignored in clang-cl: '-cake-is-lie'
|
||||
// CL: warning: unknown argument ignored in clang-cl: '-%0'
|
||||
// CL: warning: unknown argument ignored in clang-cl: '-%d'
|
||||
|
@ -38,7 +40,8 @@
|
|||
// CL: warning: unknown argument ignored in clang-cl: '-munknown-to-clang-option'
|
||||
// CL: warning: unknown argument ignored in clang-cl: '-print-stats'
|
||||
// CL: warning: unknown argument ignored in clang-cl: '-funknown-to-clang-option'
|
||||
// CL-DID-YOU-MEAN: warning: unknown argument ignored in clang-cl '-Brepo' (did you mean '-Brepro'?)
|
||||
// CL-DID-YOU-MEAN: warning: unknown argument ignored in clang-cl '-Brepo'; did you mean '-Brepro'?
|
||||
// CL-DID-YOU-MEAN-SLASH: error: no such file or directory: '/Brepo'; did you mean '/Brepro'?
|
||||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-cake-is-lie'
|
||||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-%0'
|
||||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-%d'
|
||||
|
@ -46,11 +49,11 @@
|
|||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-munknown-to-clang-option'
|
||||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-print-stats'
|
||||
// CL-ERROR: error: unknown argument ignored in clang-cl: '-funknown-to-clang-option'
|
||||
// CL-ERROR-DID-YOU-MEAN: error: unknown argument ignored in clang-cl '-helo' (did you mean '-help'?)
|
||||
// CL-ERROR-DID-YOU-MEAN: error: unknown argument ignored in clang-cl '-helo'; did you mean '-help'?
|
||||
// SILENT-NOT: error:
|
||||
// SILENT-NOT: warning:
|
||||
// CC1AS-DID-YOU-MEAN: error: unknown argument '-hell', did you mean '-help'?
|
||||
// CC1AS-DID-YOU-MEAN: error: unknown argument '--version', did you mean '-version'?
|
||||
// CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'?
|
||||
// CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'?
|
||||
// UNKNOWN-INTEGRATED: error: unknown integrated tool 'asphalt'. Valid tools include '-cc1' and '-cc1as'.
|
||||
|
||||
// RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN
|
||||
|
||||
// CHECK: error: unsupported option '--hedonism'
|
||||
// DID-YOU-MEAN: error: unsupported option '--hell', did you mean '--help'?
|
||||
// DID-YOU-MEAN: error: unsupported option '--hell'; did you mean '--help'?
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN
|
||||
|
||||
// CHECK: error: unknown argument: '--helium'
|
||||
// DID-YOU-MEAN: error: unknown argument '--hel[', did you mean '--help'?
|
||||
// DID-YOU-MEAN: error: unknown argument '--hel['; did you mean '--help'?
|
||||
|
|
|
@ -203,9 +203,20 @@ void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) {
|
|||
std::string PathStr = Path;
|
||||
enqueueTask([=]() {
|
||||
auto MBOrErr = Future->get();
|
||||
if (MBOrErr.second)
|
||||
error("could not open " + PathStr + ": " + MBOrErr.second.message());
|
||||
if (MBOrErr.second) {
|
||||
std::string Error =
|
||||
"could not open '" + PathStr + "': " + MBOrErr.second.message();
|
||||
// Check if the filename is a typo for an option flag. OptTable thinks
|
||||
// that all args that are not known options and that start with / are
|
||||
// filenames, but e.g. `/nodefaultlibs` is more likely a typo for
|
||||
// the option `/nodefaultlib` than a reference to a file in the root
|
||||
// directory.
|
||||
std::string Nearest;
|
||||
if (COFFOptTable().findNearest(PathStr, Nearest) > 1)
|
||||
error(Error);
|
||||
else
|
||||
error(Error + "; did you mean '" + Nearest + "'");
|
||||
} else
|
||||
Driver->addBuffer(std::move(MBOrErr.first), WholeArchive);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -833,7 +833,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
|
|||
|
||||
// Expand response files (arguments in the form of @<filename>)
|
||||
// and then parse the argument again.
|
||||
SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size());
|
||||
SmallVector<const char *, 256> ExpandedArgv(Argv.data(),
|
||||
Argv.data() + Argv.size());
|
||||
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv);
|
||||
Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex,
|
||||
MissingCount);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# RUN: | FileCheck -check-prefix=COLOR %s
|
||||
|
||||
# COLOR: {{lld-link: .\[0;1;35mwarning: .\[0mignoring unknown argument '-xyz'}}
|
||||
# COLOR: {{lld-link: .\[0;1;31merror: .\[0mcould not open /nosuchfile}}
|
||||
# COLOR: {{lld-link: .\[0;1;31merror: .\[0mcould not open '/nosuchfile'}}
|
||||
|
||||
# RUN: not lld-link /nosuchfile 2>&1 | FileCheck -check-prefix=NOCOLOR %s
|
||||
# RUN: not lld-link -color-diagnostics=never /nosuchfile 2>&1 \
|
||||
|
@ -15,4 +15,4 @@
|
|||
# RUN: not lld-link -color-diagnostics=always -no-color-diagnostics \
|
||||
# RUN: /nosuchfile 2>&1 | FileCheck -check-prefix=NOCOLOR %s
|
||||
|
||||
# NOCOLOR: lld-link: error: could not open /nosuchfile
|
||||
# NOCOLOR: lld-link: error: could not open '/nosuchfile'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
RUN: not lld-link 01 2>&1 | FileCheck %s
|
||||
|
||||
CHECK: could not open 01
|
||||
CHECK: could not open '01'
|
||||
CHECK-NOT: /machine is not specified
|
||||
CHECK-NOT: subsystem must be defined
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# RUN: not lld-link nosuchfile.obj >& %t.log
|
||||
# RUN: FileCheck -check-prefix=MISSING %s < %t.log
|
||||
MISSING: nosuchfile.obj: {{[Nn]}}o such file or directory
|
||||
MISSING: 'nosuchfile.obj': {{[Nn]}}o such file or directory
|
||||
|
||||
# RUN: lld-link --version | FileCheck -check-prefix=VERSION %s
|
||||
VERSION: {{LLD [0-9]+\.[0-9]+}}
|
||||
|
@ -27,3 +27,13 @@ SPELLVERSION: no input files
|
|||
# RUN: not lld-link -nodefaultlibs 2>&1 | FileCheck -check-prefix=SPELLNODEFAULTLIB %s
|
||||
SPELLNODEFAULTLIB: ignoring unknown argument '-nodefaultlibs', did you mean '-nodefaultlib'
|
||||
SPELLNODEFAULTLIB: no input files
|
||||
|
||||
# RUN: not lld-link /nodefaultlibs 2>&1 | FileCheck -check-prefix=SPELLNODEFAULTLIB_SLASH %s
|
||||
SPELLNODEFAULTLIB_SLASH: could not open '/nodefaultlibs': {{.*}}; did you mean '/nodefaultlib'
|
||||
SPELLNODEFAULTLIB_SLASH-NOT: no input files
|
||||
|
||||
# Getting flags as typo corrections for normal input files is a side effect
|
||||
# of how spell checking for /-style flags is implemented.
|
||||
# RUN: not lld-link force 2>&1 | FileCheck -check-prefix=SPELLFORCE %s
|
||||
SPELLFORCE: could not open 'force': {{.*}}; did you mean '/force'
|
||||
SPELLFORCE-NOT: no input files
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
RUN: not lld-link 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 \
|
||||
RUN: 21 22 2>&1 | FileCheck -check-prefix=DEFAULT %s
|
||||
|
||||
DEFAULT: could not open 01
|
||||
DEFAULT: could not open 20
|
||||
DEFAULT: could not open '01'
|
||||
DEFAULT: could not open '20'
|
||||
DEFAULT-NEXT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
|
||||
DEFAULT-NOT: could not open 21
|
||||
DEFAULT-NOT: could not open '21'
|
||||
|
||||
RUN: not lld-link /errorlimit:5 01 02 03 04 05 06 07 08 09 10 2>&1 \
|
||||
RUN: | FileCheck -check-prefix=LIMIT5 %s
|
||||
|
||||
LIMIT5: could not open 01
|
||||
LIMIT5: could not open 05
|
||||
LIMIT5: could not open '01'
|
||||
LIMIT5: could not open '05'
|
||||
LIMIT5-NEXT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
|
||||
LIMIT5-NOT: could not open 06
|
||||
LIMIT5-NOT: could not open '06'
|
||||
|
||||
RUN: not lld-link /errorlimit:0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
|
||||
RUN: 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=UNLIMITED %s
|
||||
|
||||
UNLIMITED: could not open 01
|
||||
UNLIMITED: could not open 20
|
||||
UNLIMITED: could not open 21
|
||||
UNLIMITED: could not open 22
|
||||
UNLIMITED: could not open '01'
|
||||
UNLIMITED: could not open '20'
|
||||
UNLIMITED: could not open '21'
|
||||
UNLIMITED: could not open '22'
|
||||
UNLIMITED-NOT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
|
||||
|
||||
RUN: not lld-link /errorlimit:XYZ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 \
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
# RUN: /nodefaultlib:std64.lib >& %t.log || true
|
||||
# RUN: FileCheck -check-prefix=CHECK3 %s < %t.log
|
||||
|
||||
CHECK1: error: could not open hello64.obj: {{[Nn]}}o such file or directory
|
||||
CHECK2: error: could not open hello64: {{[Nn]}}o such file or directory
|
||||
CHECK1: error: could not open 'hello64.obj': {{[Nn]}}o such file or directory
|
||||
CHECK2: error: could not open 'hello64': {{[Nn]}}o such file or directory
|
||||
CHECK3: error: undefined symbol: MessageBoxA
|
||||
CHECK3-NEXT: >>> referenced by {{.*}}hello64.obj:(main)
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ INVRSP: invalid response file quoting: foobar
|
|||
# RUN: echo "blah\foo" > %t.rsp
|
||||
# RUN: not lld-link @%t.rsp 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=DEFRSP %s
|
||||
DEFRSP: error: could not open blah\foo
|
||||
DEFRSP: error: could not open 'blah\foo'
|
||||
|
||||
# RUN: echo "blah\foo" > %t.rsp
|
||||
# RUN: not lld-link --rsp-quoting=windows @%t.rsp 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=WINRSP %s
|
||||
WINRSP: error: could not open blah\foo
|
||||
WINRSP: error: could not open 'blah\foo'
|
||||
|
||||
# RUN: echo "blah\foo" > %t.rsp
|
||||
# RUN: not lld-link --rsp-quoting=posix @%t.rsp 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=POSRSP %s
|
||||
POSRSP: error: could not open blahfoo
|
||||
POSRSP: error: could not open 'blahfoo'
|
||||
|
|
Loading…
Reference in New Issue