From c991daa5320d57bb7b0b53dee0cd5bd97aaa60a6 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 1 May 2019 16:45:15 +0000 Subject: [PATCH] Option spell checking: Penalize delimiter flags if input has no argument If the user passes a flag like `-version` to a program, it's more likely they mean `--version` than `-version:`, since there's no parameter passed. Hence, give delimited arguments a penalty of 1 if the user input doesn't contain the delimiter or no data after it. The motivation is that with this, lld-link can suggest "--version" instead of "-version:" for "-version" and "-nodefaultlib" instead of "-nodefaultlib:" for "-nodefaultlibs". Differential Revision: https://reviews.llvm.org/D61382 llvm-svn: 359701 --- llvm/lib/Option/OptTable.cpp | 9 +++++++++ llvm/unittests/Option/OptionParsingTest.cpp | 17 +++++++++++++---- llvm/unittests/Option/Opts.td | 5 ++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp index 47c5f80c7d70..5833d03069f8 100644 --- a/llvm/lib/Option/OptTable.cpp +++ b/llvm/lib/Option/OptTable.cpp @@ -301,6 +301,15 @@ unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, unsigned Distance = CandidateRef.edit_distance(NormalizedName, /*AllowReplacements=*/true, /*MaxEditDistance=*/BestDistance); + if (RHS.empty() && CandidateHasDelimiter) { + // The Candidate ends with a = or : delimiter, but the option passed in + // didn't contain the delimiter (or doesn't have anything after it). + // In that case, penalize the correction: `-nodefaultlibs` is more + // likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even + // though both have an unmodified editing distance of 1, since the + // latter would need an argument. + ++Distance; + } if (Distance < BestDistance) { BestDistance = Distance; NearestString = (Candidate + RHS).str(); diff --git a/llvm/unittests/Option/OptionParsingTest.cpp b/llvm/unittests/Option/OptionParsingTest.cpp index ee741c25d059..e1d7a473ee7f 100644 --- a/llvm/unittests/Option/OptionParsingTest.cpp +++ b/llvm/unittests/Option/OptionParsingTest.cpp @@ -298,10 +298,19 @@ TEST(Option, FindNearest) { EXPECT_EQ(1U, T.findNearest("/framb:foo", Nearest)); EXPECT_EQ(Nearest, "/cramb:foo"); - // `--glormp` should have an editing distance of 1 to `--glormp=`. - EXPECT_EQ(1U, T.findNearest("--glormp", Nearest)); - EXPECT_EQ(Nearest, "--glormp="); - EXPECT_EQ(0U, T.findNearest("--glormp=foo", Nearest)); + // `--glormp` should have an editing distance > 0 from `--glormp=`. + EXPECT_GT(T.findNearest("--glorrmp", Nearest), 0U); + EXPECT_EQ(Nearest, "--glorrmp="); + EXPECT_EQ(0U, T.findNearest("--glorrmp=foo", Nearest)); + + // `--blurmps` should correct to `--blurmp`, not `--blurmp=`, even though + // both naively have an editing distance of 1. + EXPECT_EQ(1U, T.findNearest("--blurmps", Nearest)); + EXPECT_EQ(Nearest, "--blurmp"); + + // ...but `--blurmps=foo` should correct to `--blurmp=foo`. + EXPECT_EQ(1U, T.findNearest("--blurmps=foo", Nearest)); + EXPECT_EQ(Nearest, "--blurmp=foo"); // Flags should be included and excluded as specified. EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, /*FlagsToInclude=*/OptFlag2)); diff --git a/llvm/unittests/Option/Opts.td b/llvm/unittests/Option/Opts.td index 231af4914d85..70920a6a76b4 100644 --- a/llvm/unittests/Option/Opts.td +++ b/llvm/unittests/Option/Opts.td @@ -37,6 +37,9 @@ def Doopf2 : Flag<["-"], "doopf2">, HelpText<"The doopf2 option">, Flags<[OptFla def Ermgh : Joined<["--"], "ermgh">, HelpText<"The ermgh option">, MetaVarName<"ERMGH">, Flags<[OptFlag1]>; def Fjormp : Flag<["--"], "fjormp">, HelpText<"The fjormp option">, Flags<[OptFlag1]>; -def Glormp_eq : Flag<["--"], "glormp=">; +def Glorrmp_eq : Flag<["--"], "glorrmp=">; + +def Blurmpq : Flag<["--"], "blurmp">; +def Blurmpq_eq : Flag<["--"], "blurmp=">; def DashDash : Option<["--"], "", KIND_REMAINING_ARGS>;