Skip to content

Commit f262a2b

Browse files
authored
Merge pull request #10846 from DougGregor/errors-and-typo-corrections
2 parents 47d9de9 + 7deafb2 commit f262a2b

File tree

15 files changed

+80
-18
lines changed

15 files changed

+80
-18
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,10 @@ NOTE(declared_protocol_conformance_here,none,
15911591
"%0 implicitly conforms to protocol %2}1 here",
15921592
(Type, unsigned, DeclName, DeclName))
15931593

1594-
WARNING(witness_unavailable,none,
1594+
WARNING(witness_unavailable_warn,none,
1595+
"unavailable %0 %1 was used to satisfy a requirement of protocol %2",
1596+
(DescriptiveDeclKind, DeclName, DeclName))
1597+
ERROR(witness_unavailable,none,
15951598
"unavailable %0 %1 was used to satisfy a requirement of protocol %2",
15961599
(DescriptiveDeclKind, DeclName, DeclName))
15971600

include/swift/Basic/LangOptions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ namespace swift {
7979
/// \brief Disable API availability checking.
8080
bool DisableAvailabilityChecking = false;
8181

82-
/// \brief Disable typo correction.
83-
bool DisableTypoCorrection = false;
82+
/// \brief Maximum number of typo corrections we are allowed to perform.
83+
unsigned TypoCorrectionLimit = 10;
8484

8585
/// Should access control be respected?
8686
bool EnableAccessControl = true;

include/swift/Frontend/Frontend.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ class CompilerInvocation {
271271
CodeCompletionBuffer = Buf;
272272
CodeCompletionOffset = Offset;
273273
// We don't need typo-correction for code-completion.
274-
LangOpts.DisableTypoCorrection = true;
274+
// FIXME: This isn't really true, but is a performance issue.
275+
LangOpts.TypoCorrectionLimit = 0;
275276
}
276277

277278
std::pair<llvm::MemoryBuffer *, unsigned> getCodeCompletionPoint() const {

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ def warn_swift3_objc_inference_minimal :
295295
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,
296296
HelpText<"Warn about deprecated @objc inference in Swift 3 based on direct uses of the Objective-C entrypoint">;
297297

298+
def typo_correction_limit : Separate<["-"], "typo-correction-limit">,
299+
Flags<[FrontendOption, HelpHidden]>,
300+
MetaVarName<"<n>">,
301+
HelpText<"Limit the number of times the compiler will attempt typo correction to <n>">;
302+
298303
def warn_swift3_objc_inference : Flag<["-"], "warn-swift3-objc-inference">,
299304
Alias<warn_swift3_objc_inference_complete>,
300305
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, HelpHidden]>;

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ static void addCommonFrontendArgs(const ToolChain &TC,
129129
inputArgs.AddLastArg(arguments,
130130
options::OPT_warn_swift3_objc_inference_minimal,
131131
options::OPT_warn_swift3_objc_inference_complete);
132+
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
132133
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
133134
inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
134135
inputArgs.AddLastArg(arguments, options::OPT_g_Group);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,21 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
981981
= A->getOption().matches(OPT_enable_access_control);
982982
}
983983

984-
Opts.DisableTypoCorrection |= Args.hasArg(OPT_disable_typo_correction);
984+
if (auto A = Args.getLastArg(OPT_disable_typo_correction,
985+
OPT_typo_correction_limit)) {
986+
if (A->getOption().matches(OPT_disable_typo_correction))
987+
Opts.TypoCorrectionLimit = 0;
988+
else {
989+
unsigned limit;
990+
if (StringRef(A->getValue()).getAsInteger(10, limit)) {
991+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
992+
A->getAsString(Args), A->getValue());
993+
return true;
994+
}
995+
996+
Opts.TypoCorrectionLimit = limit;
997+
}
998+
}
985999

9861000
Opts.CodeCompleteInitsInPostfixExpr |=
9871001
Args.hasArg(OPT_code_complete_inits_in_postfix_expr);

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,12 +564,15 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind,
564564
LookupResult &result,
565565
GenericSignatureBuilder *gsb,
566566
unsigned maxResults) {
567-
// Disable typo-correction if we won't show the diagnostic anyway.
568-
if (getLangOpts().DisableTypoCorrection ||
567+
// Disable typo-correction if we won't show the diagnostic anyway or if
568+
// we've hit our typo correction limit.
569+
if (NumTypoCorrections >= getLangOpts().TypoCorrectionLimit ||
569570
(Diags.hasFatalErrorOccurred() &&
570571
!Diags.getShowDiagnosticsAfterFatalError()))
571572
return;
572573

574+
++NumTypoCorrections;
575+
573576
// Fill in a collection of the most reasonable entries.
574577
TopCollection<unsigned, ValueDecl*> entries(maxResults);
575578
auto consumer = makeDeclConsumer([&](ValueDecl *decl,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3003,11 +3003,15 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
30033003

30043004
break;
30053005

3006-
case CheckKind::WitnessUnavailable:
3007-
diagnoseOrDefer(requirement, /*isError=*/false,
3008-
[witness, requirement](NormalProtocolConformance *conformance) {
3006+
case CheckKind::WitnessUnavailable: {
3007+
bool emitError = !witness->getASTContext().LangOpts.isSwiftVersion3();
3008+
diagnoseOrDefer(requirement, /*isError=*/emitError,
3009+
[witness, requirement, emitError](
3010+
NormalProtocolConformance *conformance) {
30093011
auto &diags = witness->getASTContext().Diags;
3010-
diags.diagnose(witness, diag::witness_unavailable,
3012+
diags.diagnose(witness,
3013+
emitError ? diag::witness_unavailable
3014+
: diag::witness_unavailable_warn,
30113015
witness->getDescriptiveKind(),
30123016
witness->getFullName(),
30133017
conformance->getProtocol()->getFullName());
@@ -3016,6 +3020,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
30163020
});
30173021
break;
30183022
}
3023+
}
30193024

30203025
ClassDecl *classDecl = Adoptee->getClassOrBoundGenericClass();
30213026

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,9 @@ class TypeChecker final : public LazyResolver {
747747
llvm::DenseMap<AbstractFunctionDecl *, llvm::DenseSet<ApplyExpr *>>
748748
FunctionAsEscapingArg;
749749

750+
/// The # of times we have performed typo correction.
751+
unsigned NumTypoCorrections = 0;
752+
750753
public:
751754
/// Record an occurrence of a function that captures inout values as an
752755
/// argument.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
class Foo { }
4+
5+
// Complain about unavailable witnesses (error in Swift 4, warning in Swift 3)
6+
protocol P {
7+
func foo(bar: Foo) // expected-note{{requirement 'foo(bar:)' declared here}}
8+
}
9+
10+
struct ConformsToP : P {
11+
@available(*, unavailable)
12+
func foo(bar: Foo) { } // expected-warning{{unavailable instance method 'foo(bar:)' was used to satisfy a requirement of protocol 'P'}}
13+
}
14+

test/NameBinding/name_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -typo-correction-limit 100
22

33
class ThisBase1 {
44
init() { }

test/Sema/typo_correction.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -typo-correction-limit 20
22
// RUN: not %target-swift-frontend -typecheck -disable-typo-correction %s 2>&1 | %FileCheck %s -check-prefix=DISABLED
3+
// RUN: not %target-swift-frontend -typecheck -typo-correction-limit 0 %s 2>&1 | %FileCheck %s -check-prefix=DISABLED
34
// RUN: not %target-swift-frontend -typecheck -DIMPORT_FAIL %s 2>&1 | %FileCheck %s -check-prefix=DISABLED
45
// DISABLED-NOT: did you mean
56

test/Sema/typo_correction_limit.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-typecheck-verify-swift -typo-correction-limit 5
2+
3+
// This is close enough to get typo-correction.
4+
func test_short_and_close() {
5+
let foo = 4 // expected-note 5 {{did you mean 'foo'?}}
6+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
7+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
8+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
9+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
10+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
11+
let _ = fob + 1 // expected-error {{use of unresolved identifier}}
12+
}

test/decl/protocol/req/unavailable.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
22

33
// An @objc protocol can have 'unavailable'
44
// methods. They are treated as if they
@@ -24,12 +24,12 @@ class Bar : NonObjCProto { // expected-error {{type 'Bar' does not conform to pr
2424
}
2525

2626

27-
// Warn about unavailable witnesses.
27+
// Complain about unavailable witnesses (error in Swift 4, warning in Swift 3)
2828
protocol P {
2929
func foo(bar: Foo) // expected-note{{requirement 'foo(bar:)' declared here}}
3030
}
3131

32-
struct ConformsToP : P {
32+
struct ConformsToP : P { // expected-error{{type 'ConformsToP' does not conform to protocol 'P'}}
3333
@available(*, unavailable)
34-
func foo(bar: Foo) { } // expected-warning{{unavailable instance method 'foo(bar:)' was used to satisfy a requirement of protocol 'P'}}
34+
func foo(bar: Foo) { } // expected-error{{unavailable instance method 'foo(bar:)' was used to satisfy a requirement of protocol 'P'}}
3535
}

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2762,7 +2762,7 @@ static int doPrintIndexedSymbols(const CompilerInvocation &InitInvok,
27622762
CompilerInvocation Invocation(InitInvok);
27632763
Invocation.addInputFilename(SourceFileName);
27642764
Invocation.getLangOptions().DisableAvailabilityChecking = false;
2765-
Invocation.getLangOptions().DisableTypoCorrection = true;
2765+
Invocation.getLangOptions().TypoCorrectionLimit = 0;
27662766

27672767
CompilerInstance CI;
27682768

0 commit comments

Comments
 (0)