]> granicus.if.org Git - clang/commitdiff
lld-link, clang: Treat non-existent input files as possible spellos for option flags
authorNico Weber <nicolasweber@gmx.de>
Thu, 23 May 2019 17:58:33 +0000 (17:58 +0000)
committerNico Weber <nicolasweber@gmx.de>
Thu, 23 May 2019 17:58:33 +0000 (17:58 +0000)
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
include/clang/Driver/Driver.h
lib/Driver/Driver.cpp
test/Driver/unknown-arg.c
test/Driver/unsupported-option.c
test/Frontend/unknown-arg.c

index 9ce7def88e5229150661a56ead89164c3a8e378b..2ece1f0f5789061b6ffbe9024d49315ed459efd9 100644 (file)
@@ -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<
index 03e6458a5e5d5197830e89afd1743d884ec21b17..244a0f3276c69e8695416c0e20ad075ce8e8bf2b 100644 (file)
@@ -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.
   ///
index 06c0e3bdb3777dcd075fdaa01c41e97b34a10532..0f7bbb6eb0fbc336f94fd4f8734af482e0805cf9 100644 (file)
@@ -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));
       }
index 4ea43278b7ea8d3c8ff5facd9acb2e392470ad33..074a57bb6f8ef5ee42416948c5f89f1c9783e0a7 100644 (file)
@@ -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'
 // 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
index 39f135e683485486631f73570fb31a64b4691e59..d0611977a99e1f143f941ed3f1bc8e9ed60dec46 100644 (file)
@@ -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'?
index 00f2da6242c7c7427e9f93a1e60c99439e705d82..eb2fb1aee2dd5789a799bdc569436229086ad5a4 100644 (file)
@@ -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'?