From a663efb444b6006ea3832063f5daacede5f9f888 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 23 May 2019 17:58:33 +0000 Subject: [PATCH] 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@361518 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticDriverKinds.td | 12 +++--- include/clang/Driver/Driver.h | 8 ++++ lib/Driver/Driver.cpp | 41 ++++++++++++++------ test/Driver/unknown-arg.c | 37 ++++++++++-------- test/Driver/unsupported-option.c | 2 +- test/Frontend/unknown-arg.c | 2 +- 6 files changed, 67 insertions(+), 35 deletions(-) diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 9ce7def88e..2ece1f0f57 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -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; 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; def warn_drv_ycyu_different_arg_clang_cl : Warning< diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index 03e6458a5e..244a0f3276 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -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. /// diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 06c0e3bdb3..0f7bbb6eb0 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -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)); } diff --git a/test/Driver/unknown-arg.c b/test/Driver/unknown-arg.c index 4ea43278b7..074a57bb6f 100644 --- a/test/Driver/unknown-arg.c +++ b/test/Driver/unknown-arg.c @@ -1,23 +1,25 @@ // RUN: not %clang %s -cake-is-lie -%0 -%d -HHHH -munknown-to-clang-option -print-stats -funknown-to-clang-option -ifoo -imultilib dir -### 2>&1 | \ -// RUN: FileCheck %s +// RUN: FileCheck %s // RUN: %clang %s -imultilib dir -### 2>&1 | \ -// RUN: FileCheck %s --check-prefix=MULTILIB +// RUN: FileCheck %s --check-prefix=MULTILIB // RUN: not %clang %s -stdlibs=foo -hell -version -### 2>&1 | \ -// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN +// RUN: FileCheck %s --check-prefix=DID-YOU-MEAN // RUN: %clang_cl -cake-is-lie -%0 -%d -HHHH -munknown-to-clang-option -print-stats -funknown-to-clang-option -### -c -- %s 2>&1 | \ -// RUN: FileCheck %s --check-prefix=CL +// RUN: FileCheck %s --check-prefix=CL // RUN: %clang_cl -Brepo -### -- %s 2>&1 | \ -// RUN: FileCheck %s --check-prefix=CL-DID-YOU-MEAN +// 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: FileCheck %s --check-prefix=CL-ERROR // RUN: not %clang_cl -helo -Werror=unknown-argument -### -- %s 2>&1 | \ -// RUN: FileCheck %s --check-prefix=CL-ERROR-DID-YOU-MEAN +// RUN: FileCheck %s --check-prefix=CL-ERROR-DID-YOU-MEAN // RUN: %clang_cl -cake-is-lie -%0 -%d -HHHH -munknown-to-clang-option -print-stats -funknown-to-clang-option -c -Wno-unknown-argument -### -- %s 2>&1 | \ -// RUN: FileCheck %s --check-prefix=SILENT +// RUN: FileCheck %s --check-prefix=SILENT // RUN: not %clang -cc1as -hell --version 2>&1 | \ -// RUN: FileCheck %s --check-prefix=CC1AS-DID-YOU-MEAN +// RUN: FileCheck %s --check-prefix=CC1AS-DID-YOU-MEAN // RUN: not %clang -cc1asphalt -help 2>&1 | \ -// RUN: FileCheck %s --check-prefix=UNKNOWN-INTEGRATED +// RUN: FileCheck %s --check-prefix=UNKNOWN-INTEGRATED // CHECK: error: unknown argument: '-cake-is-lie' // CHECK: error: unknown argument: '-%0' @@ -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 diff --git a/test/Driver/unsupported-option.c b/test/Driver/unsupported-option.c index 39f135e683..d0611977a9 100644 --- a/test/Driver/unsupported-option.c +++ b/test/Driver/unsupported-option.c @@ -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'? diff --git a/test/Frontend/unknown-arg.c b/test/Frontend/unknown-arg.c index 00f2da6242..eb2fb1aee2 100644 --- a/test/Frontend/unknown-arg.c +++ b/test/Frontend/unknown-arg.c @@ -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'? -- 2.50.1