31
31
#include " swift/Basic/STLExtras.h"
32
32
#include " swift/Basic/SourceManager.h"
33
33
#include " swift/Basic/Statistic.h"
34
+ #include " swift/Basic/TopCollection.h"
34
35
#include " swift/Parse/Lexer.h"
35
36
#include " swift/Parse/LocalContext.h"
36
37
#include " swift/Syntax/TokenKinds.h"
@@ -787,6 +788,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
787
788
788
789
Stmt *visitBreakStmt (BreakStmt *S) {
789
790
LabeledStmt *Target = nullptr ;
791
+ TopCollection<unsigned , LabeledStmt *> labelCorrections (3 );
790
792
// Pick the nearest break target that matches the specified name.
791
793
if (S->getTargetName ().empty ()) {
792
794
for (auto I = ActiveLabeledStmts.rbegin (), E = ActiveLabeledStmts.rend ();
@@ -806,31 +808,39 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
806
808
if (S->getTargetName () == (*I)->getLabelInfo ().Name ) {
807
809
Target = *I;
808
810
break ;
811
+ } else {
812
+ unsigned distance =
813
+ TC.getCallEditDistance (S->getTargetName (), (*I)->getLabelInfo ().Name ,
814
+ TypeChecker::UnreasonableCallEditDistance);
815
+ if (distance < TypeChecker::UnreasonableCallEditDistance)
816
+ labelCorrections.insert (distance, std::move (*I));
809
817
}
810
818
}
819
+ labelCorrections.filterMaxScoreRange (
820
+ TypeChecker::MaxCallEditDistanceFromBestCandidate);
811
821
}
812
822
813
823
if (!Target) {
814
824
// If we're in a defer, produce a tailored diagnostic.
815
825
if (isInDefer ()) {
816
826
TC.diagnose (S->getLoc (), diag::jump_out_of_defer, " break" );
817
- return nullptr ;
818
- }
819
-
820
- auto diagid = diag::break_outside_loop;
821
-
822
- // If someone is using an unlabeled break inside of an 'if' or 'do'
823
- // statement, produce a more specific error.
824
- if (S->getTargetName ().empty () &&
825
- std::any_of (ActiveLabeledStmts.rbegin (),
826
- ActiveLabeledStmts.rend (),
827
- [&](Stmt *S) -> bool {
828
- return isa<IfStmt>(S) || isa<DoStmt>(S);
829
- })) {
830
- diagid = diag::unlabeled_break_outside_loop;
827
+ } else if (S->getTargetName ().empty ()) {
828
+ // If we're dealing with an unlabeled break inside of an 'if' or 'do'
829
+ // statement, produce a more specific error.
830
+ if (std::any_of (ActiveLabeledStmts.rbegin (),
831
+ ActiveLabeledStmts.rend (),
832
+ [&](Stmt *S) -> bool {
833
+ return isa<IfStmt>(S) || isa<DoStmt>(S);
834
+ })) {
835
+ TC.diagnose (S->getLoc (), diag::unlabeled_break_outside_loop);
836
+ } else {
837
+ // Otherwise produce a generic error.
838
+ TC.diagnose (S->getLoc (), diag::break_outside_loop);
839
+ }
840
+ } else {
841
+ emitUnresolvedLabelDiagnostics (TC, S->getTargetLoc (), S->getTargetName (),
842
+ labelCorrections);
831
843
}
832
-
833
- TC.diagnose (S->getLoc (), diagid);
834
844
return nullptr ;
835
845
}
836
846
S->setTarget (Target);
@@ -839,6 +849,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
839
849
840
850
Stmt *visitContinueStmt (ContinueStmt *S) {
841
851
LabeledStmt *Target = nullptr ;
852
+ TopCollection<unsigned , LabeledStmt *> labelCorrections (3 );
842
853
// Scan to see if we are in any non-switch labeled statements (loops). Scan
843
854
// inside out.
844
855
if (S->getTargetName ().empty ()) {
@@ -858,31 +869,68 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
858
869
if (S->getTargetName () == (*I)->getLabelInfo ().Name ) {
859
870
Target = *I;
860
871
break ;
872
+ } else {
873
+ unsigned distance =
874
+ TC.getCallEditDistance (S->getTargetName (), (*I)->getLabelInfo ().Name ,
875
+ TypeChecker::UnreasonableCallEditDistance);
876
+ if (distance < TypeChecker::UnreasonableCallEditDistance)
877
+ labelCorrections.insert (distance, std::move (*I));
861
878
}
862
879
}
880
+ labelCorrections.filterMaxScoreRange (
881
+ TypeChecker::MaxCallEditDistanceFromBestCandidate);
863
882
}
864
883
865
- if (!Target) {
884
+ if (Target) {
885
+ // Continue cannot be used to repeat switches, use fallthrough instead.
886
+ if (!Target->isPossibleContinueTarget ()) {
887
+ TC.diagnose (S->getLoc (), diag::continue_not_in_this_stmt,
888
+ isa<SwitchStmt>(Target) ? " switch" : " if" );
889
+ return nullptr ;
890
+ }
891
+ } else {
866
892
// If we're in a defer, produce a tailored diagnostic.
867
893
if (isInDefer ()) {
868
894
TC.diagnose (S->getLoc (), diag::jump_out_of_defer, " break" );
869
- return nullptr ;
895
+ } else if (S->getTargetName ().empty ()) {
896
+ // If we're dealing with an unlabeled continue, produce a generic error.
897
+ TC.diagnose (S->getLoc (), diag::continue_outside_loop);
898
+ } else {
899
+ emitUnresolvedLabelDiagnostics (TC, S->getTargetLoc (), S->getTargetName (),
900
+ labelCorrections);
870
901
}
871
-
872
- TC.diagnose (S->getLoc (), diag::continue_outside_loop);
873
- return nullptr ;
874
- }
875
-
876
- // Continue cannot be used to repeat switches, use fallthrough instead.
877
- if (!Target->isPossibleContinueTarget ()) {
878
- TC.diagnose (S->getLoc (), diag::continue_not_in_this_stmt,
879
- isa<SwitchStmt>(Target) ? " switch" : " if" );
880
902
return nullptr ;
881
903
}
882
-
883
904
S->setTarget (Target);
884
905
return S;
885
906
}
907
+
908
+ static void
909
+ emitUnresolvedLabelDiagnostics (TypeChecker &tc, SourceLoc targetLoc, Identifier targetName,
910
+ TopCollection<unsigned , LabeledStmt *> corrections) {
911
+ // If an unresolved label was used, but we have a single correction,
912
+ // produce the specific diagnostic and fixit.
913
+ if (corrections.size () == 1 ) {
914
+ tc.diagnose (targetLoc, diag::unresolved_label_corrected,
915
+ targetName, corrections.begin ()->Value ->getLabelInfo ().Name )
916
+ .highlight (SourceRange (targetLoc))
917
+ .fixItReplace (SourceRange (targetLoc),
918
+ corrections.begin ()->Value ->getLabelInfo ().Name .str ());
919
+ tc.diagnose (corrections.begin ()->Value ->getLabelInfo ().Loc ,
920
+ diag::identifier_declared_here,
921
+ corrections.begin ()->Value ->getLabelInfo ().Name );
922
+ } else {
923
+ // If we have multiple corrections or none, produce a generic diagnostic
924
+ // and all corrections available.
925
+ tc.diagnose (targetLoc, diag::unresolved_label, targetName)
926
+ .highlight (SourceRange (targetLoc));
927
+ for (auto &entry : corrections)
928
+ tc.diagnose (entry.Value ->getLabelInfo ().Loc , diag::note_typo_candidate,
929
+ entry.Value ->getLabelInfo ().Name .str ())
930
+ .fixItReplace (SourceRange (targetLoc),
931
+ entry.Value ->getLabelInfo ().Name .str ());
932
+ }
933
+ }
886
934
887
935
Stmt *visitFallthroughStmt (FallthroughStmt *S) {
888
936
if (!SwitchLevel) {
0 commit comments