From d15753fc92f3fe3a84ad955d4aa4e76e1165b2cc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 10 Jul 2017 11:04:41 -0700 Subject: [PATCH 1/2] Introduce a command-line option to limit the # of typo corrections. Typo correction can be particularly expensive, so introduce a command-line flag to limit the number of typo corrections we will perform per type-checker instance. Default this limit to 10. Addresses rdar://problem/28469270 to some extent. (cherry picked from commit f03685b6d08872ce95a497fd0125d05317486aa9) --- include/swift/Basic/LangOptions.h | 4 ++-- include/swift/Frontend/Frontend.h | 3 ++- include/swift/Option/Options.td | 5 +++++ lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 16 +++++++++++++++- lib/Sema/TypeCheckNameLookup.cpp | 7 +++++-- lib/Sema/TypeChecker.h | 3 +++ test/Sema/typo_correction.swift | 3 ++- test/Sema/typo_correction_limit.swift | 12 ++++++++++++ tools/swift-ide-test/swift-ide-test.cpp | 2 +- 10 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 test/Sema/typo_correction_limit.swift diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index ef888358cd9ad..9e426f32acec4 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -79,8 +79,8 @@ namespace swift { /// \brief Disable API availability checking. bool DisableAvailabilityChecking = false; - /// \brief Disable typo correction. - bool DisableTypoCorrection = false; + /// \brief Maximum number of typo corrections we are allowed to perform. + unsigned TypoCorrectionLimit = 10; /// Should access control be respected? bool EnableAccessControl = true; diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index d877ec74854f6..61fc40e92ea87 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -271,7 +271,8 @@ class CompilerInvocation { CodeCompletionBuffer = Buf; CodeCompletionOffset = Offset; // We don't need typo-correction for code-completion. - LangOpts.DisableTypoCorrection = true; + // FIXME: This isn't really true, but is a performance issue. + LangOpts.TypoCorrectionLimit = 0; } std::pair getCodeCompletionPoint() const { diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 2bdbe29739f66..88713679a375b 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -295,6 +295,11 @@ def warn_swift3_objc_inference_minimal : Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, HelpText<"Warn about deprecated @objc inference in Swift 3 based on direct uses of the Objective-C entrypoint">; +def typo_correction_limit : Separate<["-"], "typo-correction-limit">, + Flags<[FrontendOption, HelpHidden]>, + MetaVarName<"">, + HelpText<"Limit the number of times the compiler will attempt typo correction to ">; + def warn_swift3_objc_inference : Flag<["-"], "warn-swift3-objc-inference">, Alias, Flags<[FrontendOption, DoesNotAffectIncrementalBuild, HelpHidden]>; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index c2fd1d8154325..d97b761decfdf 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -129,6 +129,7 @@ static void addCommonFrontendArgs(const ToolChain &TC, inputArgs.AddLastArg(arguments, options::OPT_warn_swift3_objc_inference_minimal, options::OPT_warn_swift3_objc_inference_complete); + inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit); inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension); inputArgs.AddLastArg(arguments, options::OPT_enable_testing); inputArgs.AddLastArg(arguments, options::OPT_g_Group); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 826f24e6bc867..7b2c303852bfa 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -981,7 +981,21 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, = A->getOption().matches(OPT_enable_access_control); } - Opts.DisableTypoCorrection |= Args.hasArg(OPT_disable_typo_correction); + if (auto A = Args.getLastArg(OPT_disable_typo_correction, + OPT_typo_correction_limit)) { + if (A->getOption().matches(OPT_disable_typo_correction)) + Opts.TypoCorrectionLimit = 0; + else { + unsigned limit; + if (StringRef(A->getValue()).getAsInteger(10, limit)) { + Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value, + A->getAsString(Args), A->getValue()); + return true; + } + + Opts.TypoCorrectionLimit = limit; + } + } Opts.CodeCompleteInitsInPostfixExpr |= Args.hasArg(OPT_code_complete_inits_in_postfix_expr); diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 327c49bae3af7..8f73302654bd1 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -554,12 +554,15 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind, LookupResult &result, GenericSignatureBuilder *gsb, unsigned maxResults) { - // Disable typo-correction if we won't show the diagnostic anyway. - if (getLangOpts().DisableTypoCorrection || + // Disable typo-correction if we won't show the diagnostic anyway or if + // we've hit our typo correction limit. + if (NumTypoCorrections >= getLangOpts().TypoCorrectionLimit || (Diags.hasFatalErrorOccurred() && !Diags.getShowDiagnosticsAfterFatalError())) return; + ++NumTypoCorrections; + // Fill in a collection of the most reasonable entries. TopCollection entries(maxResults); auto consumer = makeDeclConsumer([&](ValueDecl *decl, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 5cc4bcbed1d77..e3cb974232232 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -741,6 +741,9 @@ class TypeChecker final : public LazyResolver { llvm::DenseMap> FunctionAsEscapingArg; + /// The # of times we have performed typo correction. + unsigned NumTypoCorrections = 0; + public: /// Record an occurrence of a function that captures inout values as an /// argument. diff --git a/test/Sema/typo_correction.swift b/test/Sema/typo_correction.swift index 2816552cd642e..a6fa5743e8587 100644 --- a/test/Sema/typo_correction.swift +++ b/test/Sema/typo_correction.swift @@ -1,5 +1,6 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -typo-correction-limit 20 // RUN: not %target-swift-frontend -typecheck -disable-typo-correction %s 2>&1 | %FileCheck %s -check-prefix=DISABLED +// RUN: not %target-swift-frontend -typecheck -typo-correction-limit 0 %s 2>&1 | %FileCheck %s -check-prefix=DISABLED // RUN: not %target-swift-frontend -typecheck -DIMPORT_FAIL %s 2>&1 | %FileCheck %s -check-prefix=DISABLED // DISABLED-NOT: did you mean diff --git a/test/Sema/typo_correction_limit.swift b/test/Sema/typo_correction_limit.swift new file mode 100644 index 0000000000000..36944a74e2e66 --- /dev/null +++ b/test/Sema/typo_correction_limit.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift -typo-correction-limit 5 + +// This is close enough to get typo-correction. +func test_short_and_close() { + let foo = 4 // expected-note 5 {{did you mean 'foo'?}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} + let _ = fob + 1 // expected-error {{use of unresolved identifier}} +} diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 03218ef005b9b..7978c02a92d8a 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -2762,7 +2762,7 @@ static int doPrintIndexedSymbols(const CompilerInvocation &InitInvok, CompilerInvocation Invocation(InitInvok); Invocation.addInputFilename(SourceFileName); Invocation.getLangOptions().DisableAvailabilityChecking = false; - Invocation.getLangOptions().DisableTypoCorrection = true; + Invocation.getLangOptions().TypoCorrectionLimit = 0; CompilerInstance CI; From 011468b131a2b872d43cff924f46ab9d054d19ac Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 10 Jul 2017 13:14:07 -0700 Subject: [PATCH 2/2] Update test with a higher typo correction limit (cherry picked from commit 7deafb262db176b515b23eb8f8eb1989b555d397) --- test/NameBinding/name_lookup.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NameBinding/name_lookup.swift b/test/NameBinding/name_lookup.swift index 947adc83deabf..2b1a6e8c34c48 100644 --- a/test/NameBinding/name_lookup.swift +++ b/test/NameBinding/name_lookup.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -typo-correction-limit 100 class ThisBase1 { init() { }