Skip to content

Commit 3612754

Browse files
committed
Reimplement continue statement target determination using ASTScope.
Merge the logic paths for target determination for both 'break' and 'continue', based on the ASTScope implementation.
1 parent 6862809 commit 3612754

File tree

1 file changed

+39
-74
lines changed

1 file changed

+39
-74
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 39 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -347,36 +347,47 @@ emitUnresolvedLabelDiagnostics(DiagnosticEngine &DE,
347347
}
348348
}
349349

350-
/// Find the target of a break statement.
350+
/// Find the target of a break or continue statement.
351351
///
352352
/// \returns the target, if one was found, or \c nullptr if no such target
353353
/// exists.
354-
static LabeledStmt *findBreakStmtTarget(
355-
ASTContext &ctx, SourceFile *sourceFile, BreakStmt *breakStmt,
354+
static LabeledStmt *findBreakOrContinueStmtTarget(
355+
ASTContext &ctx, SourceFile *sourceFile,
356+
SourceLoc loc, Identifier targetName, SourceLoc targetLoc,
357+
bool isContinue,
356358
Optional<AnyFunctionRef> enclosingFunc) {
357359
TopCollection<unsigned, LabeledStmt *> labelCorrections(3);
358360

359361
// Pick the nearest break target that matches the specified name.
360-
auto activeLabeledStmts = ASTScope::lookupLabeledStmts(
361-
sourceFile, breakStmt->getStartLoc());
362-
if (breakStmt->getTargetName().empty()) {
362+
auto activeLabeledStmts = ASTScope::lookupLabeledStmts(sourceFile, loc);
363+
if (targetName.empty()) {
363364
for (auto labeledStmt : activeLabeledStmts) {
364365
// 'break' with no label looks through non-loop structures
365366
// except 'switch'.
366-
if (!labeledStmt->requiresLabelOnJump()) {
367+
// 'continue' ignores non-loop structures.
368+
if (!labeledStmt->requiresLabelOnJump() &&
369+
(!isContinue || labeledStmt->isPossibleContinueTarget())) {
367370
return labeledStmt;
368371
}
369372
}
370373
} else {
371374
// Scan inside out until we find something with the right label.
372375
for (auto labeledStmt : activeLabeledStmts) {
373-
if (breakStmt->getTargetName() == labeledStmt->getLabelInfo().Name) {
376+
if (targetName == labeledStmt->getLabelInfo().Name) {
377+
// Continue cannot be used to repeat switches, use fallthrough instead.
378+
if (isContinue && !labeledStmt->isPossibleContinueTarget()) {
379+
ctx.Diags.diagnose(
380+
loc, diag::continue_not_in_this_stmt,
381+
isa<SwitchStmt>(labeledStmt) ? "switch" : "if");
382+
return nullptr;
383+
}
384+
374385
return labeledStmt;
375386
}
376387

377388
unsigned distance =
378389
TypeChecker::getCallEditDistance(
379-
DeclNameRef(breakStmt->getTargetName()),
390+
DeclNameRef(targetName),
380391
labeledStmt->getLabelInfo().Name,
381392
TypeChecker::UnreasonableCallEditDistance);
382393
if (distance < TypeChecker::UnreasonableCallEditDistance)
@@ -389,31 +400,33 @@ static LabeledStmt *findBreakStmtTarget(
389400
// If we're in a defer, produce a tailored diagnostic.
390401
if (isDefer(enclosingFunc)) {
391402
ctx.Diags.diagnose(
392-
breakStmt->getLoc(), diag::jump_out_of_defer, "break");
403+
loc, diag::jump_out_of_defer, isContinue ? "continue": "break");
393404
return nullptr;
394405
}
395406

396-
if (breakStmt->getTargetName().empty()) {
407+
if (targetName.empty()) {
397408
// If we're dealing with an unlabeled break inside of an 'if' or 'do'
398409
// statement, produce a more specific error.
399-
if (llvm::any_of(activeLabeledStmts,
410+
if (!isContinue &&
411+
llvm::any_of(activeLabeledStmts,
400412
[&](Stmt *S) -> bool {
401413
return isa<IfStmt>(S) || isa<DoStmt>(S);
402414
})) {
403415
ctx.Diags.diagnose(
404-
breakStmt->getLoc(), diag::unlabeled_break_outside_loop);
416+
loc, diag::unlabeled_break_outside_loop);
405417
return nullptr;
406418
}
407419

408420
// Otherwise produce a generic error.
409-
ctx.Diags.diagnose(breakStmt->getLoc(), diag::break_outside_loop);
421+
ctx.Diags.diagnose(
422+
loc,
423+
isContinue ? diag::continue_outside_loop : diag::break_outside_loop);
410424
return nullptr;
411425
}
412426

413427
// Provide potential corrections for an incorrect label.
414428
emitUnresolvedLabelDiagnostics(
415-
ctx.Diags, breakStmt->getTargetLoc(), breakStmt->getTargetName(),
416-
labelCorrections);
429+
ctx.Diags, targetLoc, targetName, labelCorrections);
417430
return nullptr;
418431
}
419432

@@ -842,72 +855,24 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
842855
}
843856

844857
Stmt *visitBreakStmt(BreakStmt *S) {
845-
if (auto target = findBreakStmtTarget(getASTContext(), DC->getParentSourceFile(), S, TheFunc))
858+
if (auto target = findBreakOrContinueStmtTarget(
859+
getASTContext(), DC->getParentSourceFile(), S->getLoc(),
860+
S->getTargetName(), S->getTargetLoc(), /*isContinue=*/false,
861+
TheFunc)) {
846862
S->setTarget(target);
863+
}
847864

848865
return S;
849866
}
850867

851868
Stmt *visitContinueStmt(ContinueStmt *S) {
852-
LabeledStmt *Target = nullptr;
853-
TopCollection<unsigned, LabeledStmt *> labelCorrections(3);
854-
// Scan to see if we are in any non-switch labeled statements (loops). Scan
855-
// inside out.
856-
if (S->getTargetName().empty()) {
857-
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
858-
I != E; ++I) {
859-
// 'continue' with no label ignores non-loop structures.
860-
if (!(*I)->requiresLabelOnJump() &&
861-
(*I)->isPossibleContinueTarget()) {
862-
Target = *I;
863-
break;
864-
}
865-
}
866-
} else {
867-
// Scan inside out until we find something with the right label.
868-
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
869-
I != E; ++I) {
870-
if (S->getTargetName() == (*I)->getLabelInfo().Name) {
871-
Target = *I;
872-
break;
873-
} else {
874-
unsigned distance =
875-
TypeChecker::getCallEditDistance(
876-
DeclNameRef(S->getTargetName()), (*I)->getLabelInfo().Name,
877-
TypeChecker::UnreasonableCallEditDistance);
878-
if (distance < TypeChecker::UnreasonableCallEditDistance)
879-
labelCorrections.insert(distance, std::move(*I));
880-
}
881-
}
882-
labelCorrections.filterMaxScoreRange(
883-
TypeChecker::MaxCallEditDistanceFromBestCandidate);
869+
if (auto target = findBreakOrContinueStmtTarget(
870+
getASTContext(), DC->getParentSourceFile(), S->getLoc(),
871+
S->getTargetName(), S->getTargetLoc(), /*isContinue=*/true,
872+
TheFunc)) {
873+
S->setTarget(target);
884874
}
885875

886-
if (Target) {
887-
// Continue cannot be used to repeat switches, use fallthrough instead.
888-
if (!Target->isPossibleContinueTarget()) {
889-
getASTContext().Diags.diagnose(
890-
S->getLoc(), diag::continue_not_in_this_stmt,
891-
isa<SwitchStmt>(Target) ? "switch" : "if");
892-
return nullptr;
893-
}
894-
} else {
895-
// If we're in a defer, produce a tailored diagnostic.
896-
if (isInDefer()) {
897-
getASTContext().Diags.diagnose(S->getLoc(),
898-
diag::jump_out_of_defer, "break");
899-
} else if (S->getTargetName().empty()) {
900-
// If we're dealing with an unlabeled continue, produce a generic error.
901-
getASTContext().Diags.diagnose(S->getLoc(),
902-
diag::continue_outside_loop);
903-
} else {
904-
emitUnresolvedLabelDiagnostics(getASTContext().Diags,
905-
S->getTargetLoc(), S->getTargetName(),
906-
labelCorrections);
907-
}
908-
return nullptr;
909-
}
910-
S->setTarget(Target);
911876
return S;
912877
}
913878

0 commit comments

Comments
 (0)