[Sema] Walk patterns for syntactic diagnostics

Use `SyntacticElementTarget::walk`, ensuring we
walk to any ExprPatterns.
This commit is contained in:
Hamish Knight
2024-08-31 12:47:46 +01:00
parent 614e8aa79a
commit cbeb4bfbc8
3 changed files with 75 additions and 46 deletions

View File

@@ -127,10 +127,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
return MacroWalking::Expansion; return MacroWalking::Expansion;
} }
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
return Action::SkipNode(P);
}
PreWalkAction walkToTypeReprPre(TypeRepr *T) override { PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
return Action::Continue(); return Action::Continue();
} }

View File

@@ -327,20 +327,31 @@ void ParentConditionalConformance::diagnoseConformanceStack(
} }
namespace { namespace {
/// Produce any additional syntactic diagnostics for the body of a function /// Produce any additional syntactic diagnostics for a SyntacticElementTarget.
/// that had a result builder applied. class SyntacticDiagnosticWalker final : public ASTWalker {
class FunctionSyntacticDiagnosticWalker : public ASTWalker {
SmallVector<DeclContext *, 4> dcStack; SmallVector<DeclContext *, 4> dcStack;
const SyntacticElementTarget &Target;
bool IsTopLevelExprStmt;
SyntacticDiagnosticWalker(const SyntacticElementTarget &target,
bool isExprStmt)
: Target(target), IsTopLevelExprStmt(isExprStmt) {
dcStack.push_back(target.getDeclContext());
}
public: public:
FunctionSyntacticDiagnosticWalker(DeclContext *dc) { dcStack.push_back(dc); } static void check(const SyntacticElementTarget &target, bool isExprStmt) {
auto walker = SyntacticDiagnosticWalker(target, isExprStmt);
target.walk(walker);
}
MacroWalking getMacroWalkingBehavior() const override { MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::Expansion; return MacroWalking::Expansion;
} }
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override { PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
performSyntacticExprDiagnostics(expr, dcStack.back(), /*isExprStmt=*/false); auto isExprStmt = (expr == Target.getAsExpr()) ? IsTopLevelExprStmt : false;
performSyntacticExprDiagnostics(expr, dcStack.back(), isExprStmt);
if (auto closure = dyn_cast<ClosureExpr>(expr)) { if (auto closure = dyn_cast<ClosureExpr>(expr)) {
if (closure->isSeparatelyTypeChecked()) { if (closure->isSeparatelyTypeChecked()) {
@@ -368,9 +379,10 @@ public:
return Action::Continue(stmt); return Action::Continue(stmt);
} }
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pattern) override { PreWalkAction walkToDeclPre(Decl *D) override {
return Action::SkipNode(pattern); return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
} }
PreWalkAction walkToTypeReprPre(TypeRepr *typeRepr) override { PreWalkAction walkToTypeReprPre(TypeRepr *typeRepr) override {
return Action::SkipNode(); return Action::SkipNode();
} }
@@ -382,41 +394,7 @@ public:
void constraints::performSyntacticDiagnosticsForTarget( void constraints::performSyntacticDiagnosticsForTarget(
const SyntacticElementTarget &target, bool isExprStmt) { const SyntacticElementTarget &target, bool isExprStmt) {
auto *dc = target.getDeclContext(); SyntacticDiagnosticWalker::check(target, isExprStmt);
switch (target.kind) {
case SyntacticElementTarget::Kind::expression: {
// First emit diagnostics for the main expression.
performSyntacticExprDiagnostics(target.getAsExpr(), dc, isExprStmt);
return;
}
case SyntacticElementTarget::Kind::forEachPreamble: {
auto *stmt = target.getAsForEachStmt();
// First emit diagnostics for the main expression.
performSyntacticExprDiagnostics(stmt->getTypeCheckedSequence(), dc,
isExprStmt);
if (auto *whereExpr = stmt->getWhere())
performSyntacticExprDiagnostics(whereExpr, dc, /*isExprStmt*/ false);
return;
}
case SyntacticElementTarget::Kind::function: {
auto *body = target.getFunctionBody();
FunctionSyntacticDiagnosticWalker walker(dc);
body->walk(walker);
return;
}
case SyntacticElementTarget::Kind::closure:
case SyntacticElementTarget::Kind::stmtCondition:
case SyntacticElementTarget::Kind::caseLabelItem:
case SyntacticElementTarget::Kind::patternBinding:
case SyntacticElementTarget::Kind::uninitializedVar:
// Nothing to do for these.
return;
}
llvm_unreachable("Unhandled case in switch!");
} }
#pragma mark High-level entry points #pragma mark High-level entry points

View File

@@ -0,0 +1,55 @@
// RUN: %target-typecheck-verify-swift
// https://github.com/swiftlang/swift/issues/75389
@available(*, unavailable)
func unavailableFn() -> Int { 0 }
// expected-note@-1 5{{'unavailableFn()' has been explicitly marked unavailable here}}
if case unavailableFn() = 0 {}
// expected-error@-1 {{'unavailableFn()' is unavailable}}
switch Bool.random() {
case true where unavailableFn() == 1:
// expected-error@-1 {{'unavailableFn()' is unavailable}}
break
default:
break
}
switch 0 {
case unavailableFn():
// expected-error@-1 {{'unavailableFn()' is unavailable}}
break
default:
break
}
let _ = {
switch Bool.random() {
case true where unavailableFn() == 1:
// expected-error@-1 {{'unavailableFn()' is unavailable}}
break
default:
break
}
switch 0 {
case unavailableFn():
// expected-error@-1 {{'unavailableFn()' is unavailable}}
break
default:
break
}
}
struct S {}
func ~= (lhs: S.Type, rhs: S.Type) -> Bool { true }
if case (S) = S.self {}
// expected-error@-1 {{expected member name or initializer call after type name}}
// expected-note@-2 {{add arguments after the type to construct a value of the type}}
// expected-note@-3 {{use '.self' to reference the type object}}
if case ({ if case (S) = S.self { true } else { false } }()) = true {}
// expected-error@-1 {{expected member name or initializer call after type name}}
// expected-note@-2 {{add arguments after the type to construct a value of the type}}
// expected-note@-3 {{use '.self' to reference the type object}}