Skip to content

Commit 1965f96

Browse files
authored
Merge pull request #77554 from hamishknight/statement-misc-diags
[Sema] A couple more MiscDiagnostics cleanups/fixes
2 parents 6d4db0e + b644cd8 commit 1965f96

14 files changed

+344
-137
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5105,7 +5105,8 @@ ERROR(unknown_case_must_be_last,none,
51055105
"'@unknown' can only be applied to the last case in a switch", ())
51065106

51075107
WARNING(where_on_one_item, none,
5108-
"'where' only applies to the second pattern match in this case", ())
5108+
"'where' only applies to the second pattern match in this "
5109+
"'%select{case|catch}0'", (bool))
51095110

51105111
NOTE(add_where_newline, none,
51115112
"disambiguate by adding a line break between them if this is desired", ())

lib/Sema/CSSyntacticElement.cpp

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,20 +1733,6 @@ class SyntacticElementSolutionApplication
17331733
return Type();
17341734
}
17351735

1736-
ASTNode visit(Stmt *S, bool performSyntacticDiagnostics = true) {
1737-
auto rewritten = ASTVisitor::visit(S);
1738-
if (!rewritten)
1739-
return {};
1740-
1741-
if (performSyntacticDiagnostics) {
1742-
if (auto *stmt = getAsStmt(rewritten)) {
1743-
performStmtDiagnostics(stmt, context.getAsDeclContext());
1744-
}
1745-
}
1746-
1747-
return rewritten;
1748-
}
1749-
17501736
bool visitPatternBindingDecl(PatternBindingDecl *PBD) {
17511737
// If this is a placeholder variable with an initializer, we just need to
17521738
// set the inferred type.
@@ -2274,7 +2260,7 @@ class ResultBuilderRewriter : public SyntacticElementSolutionApplication {
22742260
private:
22752261
ASTNode visitDoStmt(DoStmt *doStmt) override {
22762262
if (auto transformed = transformDo(doStmt)) {
2277-
return visit(transformed.get(), /*performSyntacticDiagnostics=*/false);
2263+
return visit(transformed.get());
22782264
}
22792265

22802266
auto newBody = visit(doStmt->getBody());

lib/Sema/MiscDiagnostics.cpp

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4419,61 +4419,57 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
44194419
}
44204420
}
44214421

4422-
// Perform MiscDiagnostics on Switch Statements.
4423-
static void checkSwitch(ASTContext &ctx, const SwitchStmt *stmt,
4424-
DeclContext *DC) {
4425-
// We want to warn about "case .Foo, .Bar where 1 != 100:" since the where
4426-
// clause only applies to the second case, and this is surprising.
4427-
for (auto cs : stmt->getCases()) {
4428-
TypeChecker::checkExistentialTypes(ctx, cs, DC);
4429-
4430-
// The case statement can have multiple case items, each can have a where.
4431-
// If we find a "where", and there is a preceding item without a where, and
4432-
// if they are on the same source line, then warn.
4433-
auto items = cs->getCaseLabelItems();
4434-
4435-
// Don't do any work for the vastly most common case.
4436-
if (items.size() == 1) continue;
4437-
4438-
// Ignore the first item, since it can't have preceding ones.
4439-
for (unsigned i = 1, e = items.size(); i != e; ++i) {
4440-
// Must have a where clause.
4441-
auto where = items[i].getGuardExpr();
4442-
if (!where)
4443-
continue;
4444-
4445-
// Preceding item must not.
4446-
if (items[i-1].getGuardExpr())
4447-
continue;
4448-
4449-
// Must be on the same source line.
4450-
auto prevLoc = items[i-1].getStartLoc();
4451-
auto thisLoc = items[i].getStartLoc();
4452-
if (prevLoc.isInvalid() || thisLoc.isInvalid())
4453-
continue;
4454-
4455-
auto &SM = ctx.SourceMgr;
4456-
auto prevLineCol = SM.getLineAndColumnInBuffer(prevLoc);
4457-
if (SM.getLineAndColumnInBuffer(thisLoc).first != prevLineCol.first)
4458-
continue;
4422+
static void diagnoseCaseStmtAmbiguousWhereClause(const CaseStmt *CS,
4423+
ASTContext &ctx) {
4424+
// The case statement can have multiple case items, each can have a where.
4425+
// If we find a "where", and there is a preceding item without a where, and
4426+
// if they are on the same source line, e.g
4427+
// "case .Foo, .Bar where 1 != 100:" then warn since it may be unexpected.
4428+
auto items = CS->getCaseLabelItems();
4429+
4430+
// Don't do any work for the vastly most common case.
4431+
if (items.size() == 1)
4432+
return;
44594433

4460-
ctx.Diags.diagnose(items[i].getWhereLoc(), diag::where_on_one_item)
4434+
// Ignore the first item, since it can't have preceding ones.
4435+
for (unsigned i = 1, e = items.size(); i != e; ++i) {
4436+
// Must have a where clause.
4437+
auto where = items[i].getGuardExpr();
4438+
if (!where)
4439+
continue;
4440+
4441+
// Preceding item must not.
4442+
if (items[i - 1].getGuardExpr())
4443+
continue;
4444+
4445+
// Must be on the same source line.
4446+
auto prevLoc = items[i - 1].getStartLoc();
4447+
auto thisLoc = items[i].getStartLoc();
4448+
if (prevLoc.isInvalid() || thisLoc.isInvalid())
4449+
continue;
4450+
4451+
auto &SM = ctx.SourceMgr;
4452+
auto prevLineCol = SM.getLineAndColumnInBuffer(prevLoc);
4453+
if (SM.getLineAndColumnInBuffer(thisLoc).first != prevLineCol.first)
4454+
continue;
4455+
4456+
ctx.Diags
4457+
.diagnose(items[i].getWhereLoc(), diag::where_on_one_item,
4458+
CS->getParentKind() == CaseParentKind::DoCatch)
44614459
.highlight(items[i].getPattern()->getSourceRange())
44624460
.highlight(where->getSourceRange());
4463-
4464-
// Whitespace it out to the same column as the previous item.
4465-
std::string whitespace(prevLineCol.second-1, ' ');
4466-
ctx.Diags.diagnose(thisLoc, diag::add_where_newline)
4467-
.fixItInsert(thisLoc, "\n"+whitespace);
4468-
4469-
auto whereRange = SourceRange(items[i].getWhereLoc(),
4470-
where->getEndLoc());
4471-
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, whereRange);
4472-
auto whereText = SM.extractText(charRange);
4473-
ctx.Diags.diagnose(prevLoc, diag::duplicate_where)
4474-
.fixItInsertAfter(items[i-1].getEndLoc(), " " + whereText.str())
4475-
.highlight(items[i-1].getSourceRange());
4476-
}
4461+
4462+
// Whitespace it out to the same column as the previous item.
4463+
std::string whitespace(prevLineCol.second - 1, ' ');
4464+
ctx.Diags.diagnose(thisLoc, diag::add_where_newline)
4465+
.fixItInsert(thisLoc, "\n" + whitespace);
4466+
4467+
auto whereRange = SourceRange(items[i].getWhereLoc(), where->getEndLoc());
4468+
auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, whereRange);
4469+
auto whereText = SM.extractText(charRange);
4470+
ctx.Diags.diagnose(prevLoc, diag::duplicate_where)
4471+
.fixItInsertAfter(items[i - 1].getEndLoc(), " " + whereText.str())
4472+
.highlight(items[i - 1].getSourceRange());
44774473
}
44784474
}
44794475

@@ -6194,8 +6190,8 @@ void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
61946190

61956191
TypeChecker::checkExistentialTypes(ctx, const_cast<Stmt *>(S), DC);
61966192

6197-
if (auto switchStmt = dyn_cast<SwitchStmt>(S))
6198-
checkSwitch(ctx, switchStmt, DC);
6193+
if (auto *CS = dyn_cast<CaseStmt>(S))
6194+
diagnoseCaseStmtAmbiguousWhereClause(CS, ctx);
61996195

62006196
checkStmtConditionTrailingClosure(ctx, S);
62016197

lib/Sema/MiscDiagnostics.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,11 @@ namespace swift {
135135

136136
class BaseDiagnosticWalker : public ASTWalker {
137137
PreWalkAction walkToDeclPre(Decl *D) override {
138-
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
138+
// We don't walk into any nested local decls, except PatternBindingDecls,
139+
// which are type-checked along with the parent, and MacroExpansionDecl,
140+
// which needs to be visited to visit the macro arguments.
141+
return Action::VisitNodeIf(isa<PatternBindingDecl>(D) ||
142+
isa<MacroExpansionDecl>(D));
139143
}
140144

141145
MacroWalking getMacroWalkingBehavior() const override {

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3560,7 +3560,7 @@ bool diagnoseExplicitUnavailability(
35603560
}
35613561

35623562
namespace {
3563-
class ExprAvailabilityWalker : public ASTWalker {
3563+
class ExprAvailabilityWalker : public BaseDiagnosticWalker {
35643564
/// Models how member references will translate to accessor usage. This is
35653565
/// used to diagnose the availability of individual accessors that may be
35663566
/// called by the expression being checked.
@@ -3595,11 +3595,6 @@ class ExprAvailabilityWalker : public ASTWalker {
35953595
explicit ExprAvailabilityWalker(const ExportContext &Where)
35963596
: Context(Where.getDeclContext()->getASTContext()), Where(Where) {}
35973597

3598-
MacroWalking getMacroWalkingBehavior() const override {
3599-
// Expanded source should be type checked and diagnosed separately.
3600-
return MacroWalking::Arguments;
3601-
}
3602-
36033598
PreWalkAction walkToArgumentPre(const Argument &Arg) override {
36043599
// Arguments should be walked in their own member access context which
36053600
// starts out read-only by default.
@@ -3720,10 +3715,8 @@ class ExprAvailabilityWalker : public ASTWalker {
37203715
CE->getResultType(), E->getLoc(), Where);
37213716
}
37223717
if (AbstractClosureExpr *closure = dyn_cast<AbstractClosureExpr>(E)) {
3723-
if (shouldWalkIntoClosure(closure)) {
3724-
walkAbstractClosure(closure);
3725-
return Action::SkipChildren(E);
3726-
}
3718+
walkAbstractClosure(closure);
3719+
return Action::SkipChildren(E);
37273720
}
37283721

37293722
if (auto CE = dyn_cast<ExplicitCastExpr>(E)) {
@@ -3789,13 +3782,22 @@ class ExprAvailabilityWalker : public ASTWalker {
37893782
}
37903783

37913784
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
3792-
3793-
// We end up here when checking the output of the result builder transform,
3794-
// which includes closures that are not "separately typechecked" and yet
3795-
// contain statements and declarations. We need to walk them recursively,
3796-
// since these availability for these statements is not diagnosed from
3797-
// typeCheckStmt() as usual.
3798-
diagnoseStmtAvailability(S, Where.getDeclContext(), /*walkRecursively=*/true);
3785+
// We need to recursively call diagnoseExprAvailability for any
3786+
// sub-expressions in the statement since the availability context may
3787+
// differ, e.g for things like `guard #available(...)`.
3788+
class StmtRecurseWalker : public BaseDiagnosticWalker {
3789+
DeclContext *DC;
3790+
3791+
public:
3792+
StmtRecurseWalker(DeclContext *DC) : DC(DC) {}
3793+
3794+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
3795+
diagnoseExprAvailability(E, DC);
3796+
return Action::SkipNode(E);
3797+
}
3798+
};
3799+
StmtRecurseWalker W(Where.getDeclContext());
3800+
S->walk(W);
37993801
return Action::SkipNode(S);
38003802
}
38013803

@@ -3942,10 +3944,6 @@ class ExprAvailabilityWalker : public ASTWalker {
39423944
walkInContext(E->getSubExpr(), accessContext);
39433945
}
39443946

3945-
bool shouldWalkIntoClosure(AbstractClosureExpr *closure) const {
3946-
return true;
3947-
}
3948-
39493947
/// Walk an abstract closure expression, checking for availability
39503948
void walkAbstractClosure(AbstractClosureExpr *closure) {
39513949
// Do the walk with the closure set as the decl context of the 'where'
@@ -4395,26 +4393,29 @@ void swift::diagnoseExprAvailability(const Expr *E, DeclContext *DC) {
43954393
namespace {
43964394

43974395
class StmtAvailabilityWalker : public BaseDiagnosticWalker {
4396+
const Stmt *TopLevelStmt;
43984397
DeclContext *DC;
4399-
bool WalkRecursively;
44004398

44014399
public:
4402-
explicit StmtAvailabilityWalker(DeclContext *dc, bool walkRecursively)
4403-
: DC(dc), WalkRecursively(walkRecursively) {}
4400+
explicit StmtAvailabilityWalker(const Stmt *S, DeclContext *dc)
4401+
: TopLevelStmt(S), DC(dc) {}
44044402

44054403
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
4406-
if (!WalkRecursively && isa<BraceStmt>(S))
4407-
return Action::SkipNode(S);
4408-
4409-
return Action::Continue(S);
4404+
// `diagnoseStmtAvailability` is called for every statement, so we don't
4405+
// want to walk into any nested statements.
4406+
return Action::VisitNodeIf(S == TopLevelStmt, S);
44104407
}
44114408

44124409
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
4413-
if (WalkRecursively)
4414-
diagnoseExprAvailability(E, DC);
4410+
// Handled by ExprAvailabilityWalker.
44154411
return Action::SkipNode(E);
44164412
}
44174413

4414+
PreWalkAction walkToDeclPre(Decl *D) override {
4415+
// Handled by DeclAvailabilityChecker.
4416+
return Action::SkipNode();
4417+
}
4418+
44184419
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
44194420
auto where = ExportContext::forFunctionBody(DC, T->getStartLoc());
44204421
diagnoseTypeReprAvailability(T, where);
@@ -4433,13 +4434,8 @@ class StmtAvailabilityWalker : public BaseDiagnosticWalker {
44334434
};
44344435
}
44354436

4436-
void swift::diagnoseStmtAvailability(const Stmt *S, DeclContext *DC,
4437-
bool walkRecursively) {
4438-
// We'll visit the individual statements when we check them.
4439-
if (!walkRecursively && isa<BraceStmt>(S))
4440-
return;
4441-
4442-
StmtAvailabilityWalker walker(DC, walkRecursively);
4437+
void swift::diagnoseStmtAvailability(const Stmt *S, DeclContext *DC) {
4438+
StmtAvailabilityWalker walker(S, DC);
44434439
const_cast<Stmt*>(S)->walk(walker);
44444440
}
44454441

lib/Sema/TypeCheckAvailability.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,8 @@ bool isExported(const Decl *D);
211211
void diagnoseExprAvailability(const Expr *E, DeclContext *DC);
212212

213213
/// Diagnose uses of unavailable declarations in statements (via patterns, etc)
214-
/// but not expressions, unless \p walkRecursively was specified.
215-
///
216-
/// \param walkRecursively Whether nested statements and expressions should
217-
/// be visited, too.
218-
void diagnoseStmtAvailability(const Stmt *S, DeclContext *DC,
219-
bool walkRecursively=false);
214+
/// but not expressions.
215+
void diagnoseStmtAvailability(const Stmt *S, DeclContext *DC);
220216

221217
/// Checks both a TypeRepr and a Type, but avoids emitting duplicate
222218
/// diagnostics by only checking the Type if the TypeRepr succeeded.

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,10 @@ void ParentConditionalConformance::diagnoseConformanceStack(
327327

328328
namespace {
329329
/// Produce any additional syntactic diagnostics for a SyntacticElementTarget.
330-
class SyntacticDiagnosticWalker final : public ASTWalker {
330+
class SyntacticDiagnosticWalker final : public BaseDiagnosticWalker {
331331
const SyntacticElementTarget &Target;
332332
bool IsTopLevelExprStmt;
333+
unsigned ExprDepth = 0;
333334

334335
SyntacticDiagnosticWalker(const SyntacticElementTarget &target,
335336
bool isExprStmt)
@@ -341,27 +342,30 @@ class SyntacticDiagnosticWalker final : public ASTWalker {
341342
target.walk(walker);
342343
}
343344

344-
MacroWalking getMacroWalkingBehavior() const override {
345-
// We only want to walk macro arguments. Expansions will be walked when
346-
// they're type-checked, not as part of the surrounding code.
347-
return MacroWalking::Arguments;
345+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
346+
// We only want to call the diagnostic logic for the top-level expression,
347+
// since the underlying logic will visit each sub-expression. We want to
348+
// continue walking however to diagnose any child statements in e.g
349+
// closures.
350+
if (ExprDepth == 0) {
351+
auto isExprStmt = (E == Target.getAsExpr()) ? IsTopLevelExprStmt : false;
352+
performSyntacticExprDiagnostics(E, Target.getDeclContext(), isExprStmt);
353+
}
354+
ExprDepth += 1;
355+
return Action::Continue(E);
348356
}
349357

350-
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
351-
auto isExprStmt = (expr == Target.getAsExpr()) ? IsTopLevelExprStmt : false;
352-
performSyntacticExprDiagnostics(expr, Target.getDeclContext(), isExprStmt);
353-
return Action::SkipNode(expr);
358+
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
359+
assert(ExprDepth > 0);
360+
ExprDepth -= 1;
361+
return Action::Continue(E);
354362
}
355363

356364
PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
357365
performStmtDiagnostics(stmt, Target.getDeclContext());
358366
return Action::Continue(stmt);
359367
}
360368

361-
PreWalkAction walkToDeclPre(Decl *D) override {
362-
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
363-
}
364-
365369
PreWalkAction walkToTypeReprPre(TypeRepr *typeRepr) override {
366370
return Action::SkipNode();
367371
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,10 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
16071607
BraceStmt *body = caseBlock->getBody();
16081608
limitExhaustivityChecks |= typeCheckStmt(body);
16091609
caseBlock->setBody(body);
1610+
1611+
// CaseStmts don't go through typeCheckStmt, so manually call into
1612+
// performStmtDiagnostics.
1613+
performStmtDiagnostics(caseBlock, DC);
16101614
}
16111615
}
16121616

0 commit comments

Comments
 (0)