mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Sema] Move SingleValueStmtUsageChecker into pre-checking
These diagnostics are better suited for pre-checking since we ought to be emitting them even if there's some other error with the expression.
This commit is contained in:
@@ -4378,206 +4378,6 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class SingleValueStmtUsageChecker final : public ASTWalker {
|
||||
ASTContext &Ctx;
|
||||
DiagnosticEngine &Diags;
|
||||
llvm::DenseSet<SingleValueStmtExpr *> ValidSingleValueStmtExprs;
|
||||
|
||||
public:
|
||||
SingleValueStmtUsageChecker(
|
||||
ASTContext &ctx, ASTNode root,
|
||||
std::optional<ContextualTypePurpose> contextualPurpose)
|
||||
: Ctx(ctx), Diags(ctx.Diags) {
|
||||
assert(!root.is<Expr *>() || contextualPurpose &&
|
||||
"Must provide contextual purpose for expr");
|
||||
|
||||
// If we have a contextual purpose, this is for an expression. Check if it's
|
||||
// an expression in a valid position.
|
||||
if (contextualPurpose) {
|
||||
markAnyValidTopLevelSingleValueStmt(root.get<Expr *>(),
|
||||
*contextualPurpose);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Mark a given expression as a valid position for a SingleValueStmtExpr.
|
||||
void markValidSingleValueStmt(Expr *E) {
|
||||
if (!E)
|
||||
return;
|
||||
|
||||
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E))
|
||||
ValidSingleValueStmtExprs.insert(SVE);
|
||||
}
|
||||
|
||||
/// Mark a valid top-level expression with a given contextual purpose.
|
||||
void markAnyValidTopLevelSingleValueStmt(Expr *E, ContextualTypePurpose ctp) {
|
||||
// Allowed in returns, throws, and bindings.
|
||||
switch (ctp) {
|
||||
case CTP_ReturnStmt:
|
||||
case CTP_ThrowStmt:
|
||||
case CTP_Initialization:
|
||||
markValidSingleValueStmt(E);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MacroWalking getMacroWalkingBehavior() const override {
|
||||
return MacroWalking::ArgumentsAndExpansion;
|
||||
}
|
||||
|
||||
AssignExpr *findAssignment(Expr *E) const {
|
||||
// Don't consider assignments if we have a parent expression (as otherwise
|
||||
// this would be effectively allowing it in an arbitrary expression
|
||||
// position).
|
||||
if (Parent.getAsExpr())
|
||||
return nullptr;
|
||||
|
||||
// Look through optional exprs, which are present for e.g x?.y = z, as
|
||||
// we wrap the entire assign in the optional evaluation of the destination.
|
||||
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(E)) {
|
||||
E = OEE->getSubExpr();
|
||||
while (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(E))
|
||||
E = IIO->getSubExpr();
|
||||
}
|
||||
return dyn_cast<AssignExpr>(E);
|
||||
}
|
||||
|
||||
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
|
||||
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E)) {
|
||||
// Diagnose a SingleValueStmtExpr in a context that we do not currently
|
||||
// support. If we start allowing these in arbitrary places, we'll need
|
||||
// to ensure that autoclosures correctly contextualize them.
|
||||
if (!ValidSingleValueStmtExprs.contains(SVE)) {
|
||||
Diags.diagnose(SVE->getLoc(), diag::single_value_stmt_out_of_place,
|
||||
SVE->getStmt()->getKind());
|
||||
}
|
||||
|
||||
// Diagnose invalid SingleValueStmtExprs. This should only happen for
|
||||
// expressions in positions that we didn't support before
|
||||
// (e.g assignment or *explicit* return).
|
||||
auto *S = SVE->getStmt();
|
||||
auto mayProduceSingleValue = S->mayProduceSingleValue(Ctx);
|
||||
switch (mayProduceSingleValue.getKind()) {
|
||||
case IsSingleValueStmtResult::Kind::Valid:
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::UnterminatedBranches: {
|
||||
for (auto *branch : mayProduceSingleValue.getUnterminatedBranches()) {
|
||||
if (auto *BS = dyn_cast<BraceStmt>(branch)) {
|
||||
if (BS->empty()) {
|
||||
Diags.diagnose(branch->getStartLoc(),
|
||||
diag::single_value_stmt_branch_empty,
|
||||
S->getKind());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// TODO: The wording of this diagnostic will need tweaking if either
|
||||
// implicit last expressions or 'then' statements are enabled by
|
||||
// default.
|
||||
Diags.diagnose(branch->getEndLoc(),
|
||||
diag::single_value_stmt_branch_must_end_in_result,
|
||||
S->getKind(), isa<SwitchStmt>(S));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NonExhaustiveIf: {
|
||||
Diags.diagnose(S->getStartLoc(),
|
||||
diag::if_expr_must_be_syntactically_exhaustive);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NonExhaustiveDoCatch: {
|
||||
Diags.diagnose(S->getStartLoc(),
|
||||
diag::do_catch_expr_must_be_syntactically_exhaustive);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::HasLabel: {
|
||||
// FIXME: We should offer a fix-it to remove (currently we don't track
|
||||
// the colon SourceLoc).
|
||||
auto label = cast<LabeledStmt>(S)->getLabelInfo();
|
||||
Diags.diagnose(label.Loc,
|
||||
diag::single_value_stmt_must_be_unlabeled, S->getKind())
|
||||
.highlight(label.Loc);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::InvalidJumps: {
|
||||
// Diagnose each invalid jump.
|
||||
for (auto *jump : mayProduceSingleValue.getInvalidJumps()) {
|
||||
Diags.diagnose(jump->getStartLoc(),
|
||||
diag::cannot_jump_in_single_value_stmt,
|
||||
jump->getKind(), S->getKind())
|
||||
.highlight(jump->getSourceRange());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NoResult:
|
||||
// This is fine, we will have typed the expression as Void (we verify
|
||||
// as such in the ASTVerifier).
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::CircularReference:
|
||||
// Already diagnosed.
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::UnhandledStmt:
|
||||
break;
|
||||
}
|
||||
return Action::Continue(E);
|
||||
}
|
||||
|
||||
// Valid as the source of an assignment.
|
||||
if (auto *AE = findAssignment(E))
|
||||
markValidSingleValueStmt(AE->getSrc());
|
||||
|
||||
// Valid as a single expression body of a closure. This is needed in
|
||||
// addition to ReturnStmt checking, as we will remove the return if the
|
||||
// expression is inferred to be Never.
|
||||
if (auto *ACE = dyn_cast<ClosureExpr>(E)) {
|
||||
if (ACE->hasSingleExpressionBody())
|
||||
markValidSingleValueStmt(ACE->getSingleExpressionBody());
|
||||
}
|
||||
return Action::Continue(E);
|
||||
}
|
||||
|
||||
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
|
||||
// Valid in a return/throw/then.
|
||||
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
|
||||
if (RS->hasResult())
|
||||
markValidSingleValueStmt(RS->getResult());
|
||||
}
|
||||
if (auto *TS = dyn_cast<ThrowStmt>(S))
|
||||
markValidSingleValueStmt(TS->getSubExpr());
|
||||
|
||||
if (auto *TS = dyn_cast<ThenStmt>(S))
|
||||
markValidSingleValueStmt(TS->getResult());
|
||||
|
||||
return Action::Continue(S);
|
||||
}
|
||||
|
||||
PreWalkAction walkToDeclPre(Decl *D) override {
|
||||
// Valid as an initializer of a pattern binding.
|
||||
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
|
||||
for (auto idx : range(PBD->getNumPatternEntries()))
|
||||
markValidSingleValueStmt(PBD->getInit(idx));
|
||||
|
||||
return Action::Continue();
|
||||
}
|
||||
// We don't want to walk into any other decl, we will visit them as part of
|
||||
// typeCheckDecl.
|
||||
return Action::SkipNode();
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
void swift::diagnoseOutOfPlaceExprs(
|
||||
ASTContext &ctx, ASTNode root,
|
||||
std::optional<ContextualTypePurpose> contextualPurpose) {
|
||||
// TODO: We ought to consider moving this into pre-checking such that we can
|
||||
// still diagnose on invalid code, and don't have to traverse over implicit
|
||||
// exprs. We need to first separate out SequenceExpr folding though.
|
||||
SingleValueStmtUsageChecker sveChecker(ctx, root, contextualPurpose);
|
||||
root.walk(sveChecker);
|
||||
}
|
||||
|
||||
/// Apply the warnings managed by VarDeclUsageChecker to the top level
|
||||
/// code declarations that haven't been checked yet.
|
||||
void swift::
|
||||
@@ -6426,10 +6226,9 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E,
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Emit diagnostics for syntactic restrictions on a given expression.
|
||||
void swift::performSyntacticExprDiagnostics(
|
||||
const Expr *E, const DeclContext *DC,
|
||||
std::optional<ContextualTypePurpose> contextualPurpose, bool isExprStmt,
|
||||
bool disableOutOfPlaceExprChecking) {
|
||||
void swift::performSyntacticExprDiagnostics(const Expr *E,
|
||||
const DeclContext *DC,
|
||||
bool isExprStmt) {
|
||||
auto &ctx = DC->getASTContext();
|
||||
TypeChecker::diagnoseSelfAssignment(E);
|
||||
diagSyntacticUseRestrictions(E, DC, isExprStmt);
|
||||
@@ -6448,8 +6247,6 @@ void swift::performSyntacticExprDiagnostics(
|
||||
diagnoseConstantArgumentRequirement(E, DC);
|
||||
diagUnqualifiedAccessToMethodNamedSelf(E, DC);
|
||||
diagnoseDictionaryLiteralDuplicateKeyEntries(E, DC);
|
||||
if (!disableOutOfPlaceExprChecking)
|
||||
diagnoseOutOfPlaceExprs(ctx, const_cast<Expr *>(E), contextualPurpose);
|
||||
}
|
||||
|
||||
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
|
||||
|
||||
@@ -38,21 +38,9 @@ namespace swift {
|
||||
class ValueDecl;
|
||||
class ForEachStmt;
|
||||
|
||||
/// Diagnose any expressions that appear in an unsupported position. If visiting
|
||||
/// an expression directly, its \p contextualPurpose should be provided to
|
||||
/// evaluate its position.
|
||||
void diagnoseOutOfPlaceExprs(
|
||||
ASTContext &ctx, ASTNode root,
|
||||
std::optional<ContextualTypePurpose> contextualPurpose);
|
||||
|
||||
/// Emit diagnostics for syntactic restrictions on a given expression.
|
||||
///
|
||||
/// Note: \p contextualPurpose must be non-nil, unless
|
||||
/// \p disableOutOfPlaceExprChecking is set to \c true.
|
||||
void performSyntacticExprDiagnostics(
|
||||
const Expr *E, const DeclContext *DC,
|
||||
std::optional<ContextualTypePurpose> contextualPurpose, bool isExprStmt,
|
||||
bool disableOutOfPlaceExprChecking = false);
|
||||
void performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC,
|
||||
bool isExprStmt);
|
||||
|
||||
/// Emit diagnostics for a given statement.
|
||||
void performStmtDiagnostics(const Stmt *S, DeclContext *DC);
|
||||
|
||||
@@ -1054,6 +1054,11 @@ class PreCheckTarget final : public ASTWalker {
|
||||
/// Keep track of acceptable DiscardAssignmentExpr's.
|
||||
llvm::SmallPtrSet<DiscardAssignmentExpr *, 2> CorrectDiscardAssignmentExprs;
|
||||
|
||||
/// Keep track of any out-of-place SingleValueStmtExprs. We populate this as
|
||||
/// we encounter SingleValueStmtExprs, and erase them as we walk up to a
|
||||
/// valid parent in the post walk.
|
||||
llvm::SetVector<SingleValueStmtExpr *> OutOfPlaceSingleValueStmtExprs;
|
||||
|
||||
/// Simplify expressions which are type sugar productions that got parsed
|
||||
/// as expressions due to the parser not knowing which identifiers are
|
||||
/// type names.
|
||||
@@ -1100,9 +1105,36 @@ class PreCheckTarget final : public ASTWalker {
|
||||
/// in simple pattern-like expressions, so we reject anything complex here.
|
||||
void markAcceptableDiscardExprs(Expr *E);
|
||||
|
||||
public:
|
||||
/// Check and diagnose an invalid SingleValueStmtExpr.
|
||||
void checkSingleValueStmtExpr(SingleValueStmtExpr *SVE);
|
||||
|
||||
/// Diagnose any SingleValueStmtExprs in an unsupported position.
|
||||
void
|
||||
diagnoseOutOfPlaceSingleValueStmtExprs(const SyntacticElementTarget &target);
|
||||
|
||||
/// Mark a given expression as a valid position for a SingleValueStmtExpr.
|
||||
void markValidSingleValueStmt(Expr *E);
|
||||
|
||||
/// For the given expr, mark any valid SingleValueStmtExpr children.
|
||||
void markAnyValidSingleValueStmts(Expr *E);
|
||||
|
||||
/// For the given statement, mark any valid SingleValueStmtExpr children.
|
||||
void markAnyValidSingleValueStmts(Stmt *S);
|
||||
|
||||
PreCheckTarget(DeclContext *dc) : Ctx(dc->getASTContext()), DC(dc) {}
|
||||
|
||||
public:
|
||||
static std::optional<SyntacticElementTarget>
|
||||
check(const SyntacticElementTarget &target) {
|
||||
PreCheckTarget checker(target.getDeclContext());
|
||||
auto newTarget = target.walk(checker);
|
||||
if (!newTarget)
|
||||
return std::nullopt;
|
||||
// Diagnose any remaining out-of-place SingleValueStmtExprs.
|
||||
checker.diagnoseOutOfPlaceSingleValueStmtExprs(*newTarget);
|
||||
return *newTarget;
|
||||
}
|
||||
|
||||
ASTContext &getASTContext() const { return Ctx; }
|
||||
|
||||
bool walkToClosureExprPre(ClosureExpr *expr);
|
||||
@@ -1275,6 +1307,9 @@ public:
|
||||
if (auto *assignment = dyn_cast<AssignExpr>(expr))
|
||||
markAcceptableDiscardExprs(assignment->getDest());
|
||||
|
||||
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr))
|
||||
checkSingleValueStmtExpr(SVE);
|
||||
|
||||
return finish(true, expr);
|
||||
}
|
||||
|
||||
@@ -1283,6 +1318,9 @@ public:
|
||||
assert(ExprStack.back() == expr);
|
||||
ExprStack.pop_back();
|
||||
|
||||
// Mark any valid SingleValueStmtExpr children.
|
||||
markAnyValidSingleValueStmts(expr);
|
||||
|
||||
// Type check the type parameters in an UnresolvedSpecializeExpr.
|
||||
if (auto *us = dyn_cast<UnresolvedSpecializeExpr>(expr)) {
|
||||
if (auto *typeExpr = simplifyUnresolvedSpecializeExpr(us))
|
||||
@@ -1451,8 +1489,22 @@ public:
|
||||
return Action::Continue(stmt);
|
||||
}
|
||||
|
||||
PostWalkResult<Stmt *> walkToStmtPost(Stmt *S) override {
|
||||
markAnyValidSingleValueStmts(S);
|
||||
return Action::Continue(S);
|
||||
}
|
||||
|
||||
PreWalkAction walkToDeclPre(Decl *D) override {
|
||||
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
|
||||
return Action::VisitChildrenIf(isa<PatternBindingDecl>(D));
|
||||
}
|
||||
|
||||
PostWalkAction walkToDeclPost(Decl *D) override {
|
||||
// Mark any valid SingleValueStmtExprs for initializations.
|
||||
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
|
||||
for (auto idx : range(PBD->getNumPatternEntries()))
|
||||
markValidSingleValueStmt(PBD->getInit(idx));
|
||||
}
|
||||
return Action::Continue();
|
||||
}
|
||||
|
||||
PreWalkResult<Pattern *> walkToPatternPre(Pattern *pattern) override {
|
||||
@@ -1487,6 +1539,145 @@ bool PreCheckTarget::walkToClosureExprPre(ClosureExpr *closure) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void PreCheckTarget::markValidSingleValueStmt(Expr *E) {
|
||||
if (!E)
|
||||
return;
|
||||
|
||||
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E))
|
||||
OutOfPlaceSingleValueStmtExprs.remove(SVE);
|
||||
}
|
||||
|
||||
void PreCheckTarget::checkSingleValueStmtExpr(SingleValueStmtExpr *SVE) {
|
||||
// We add all SingleValueStmtExprs we see to the out-of-place list, then
|
||||
// erase them as we walk up to valid parents. We do this instead of populating
|
||||
// valid positions during the pre-walk to ensure we're looking at the AST
|
||||
// after e.g folding SequenceExprs.
|
||||
OutOfPlaceSingleValueStmtExprs.insert(SVE);
|
||||
|
||||
// Diagnose invalid SingleValueStmtExprs. This should only happen for
|
||||
// expressions in positions that we didn't support prior to their introduction
|
||||
// (e.g assignment or *explicit* return).
|
||||
auto &Diags = Ctx.Diags;
|
||||
auto *S = SVE->getStmt();
|
||||
auto mayProduceSingleValue = S->mayProduceSingleValue(Ctx);
|
||||
switch (mayProduceSingleValue.getKind()) {
|
||||
case IsSingleValueStmtResult::Kind::Valid:
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::UnterminatedBranches: {
|
||||
for (auto *branch : mayProduceSingleValue.getUnterminatedBranches()) {
|
||||
if (auto *BS = dyn_cast<BraceStmt>(branch)) {
|
||||
if (BS->empty()) {
|
||||
Diags.diagnose(branch->getStartLoc(),
|
||||
diag::single_value_stmt_branch_empty, S->getKind());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// TODO: The wording of this diagnostic will need tweaking if either
|
||||
// implicit last expressions or 'then' statements are enabled by
|
||||
// default.
|
||||
Diags.diagnose(branch->getEndLoc(),
|
||||
diag::single_value_stmt_branch_must_end_in_result,
|
||||
S->getKind(), isa<SwitchStmt>(S));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NonExhaustiveIf: {
|
||||
Diags.diagnose(S->getStartLoc(),
|
||||
diag::if_expr_must_be_syntactically_exhaustive);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NonExhaustiveDoCatch: {
|
||||
Diags.diagnose(S->getStartLoc(),
|
||||
diag::do_catch_expr_must_be_syntactically_exhaustive);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::HasLabel: {
|
||||
// FIXME: We should offer a fix-it to remove (currently we don't track
|
||||
// the colon SourceLoc).
|
||||
auto label = cast<LabeledStmt>(S)->getLabelInfo();
|
||||
Diags.diagnose(label.Loc,
|
||||
diag::single_value_stmt_must_be_unlabeled, S->getKind())
|
||||
.highlight(label.Loc);
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::InvalidJumps: {
|
||||
// Diagnose each invalid jump.
|
||||
for (auto *jump : mayProduceSingleValue.getInvalidJumps()) {
|
||||
Diags.diagnose(jump->getStartLoc(),
|
||||
diag::cannot_jump_in_single_value_stmt,
|
||||
jump->getKind(), S->getKind())
|
||||
.highlight(jump->getSourceRange());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IsSingleValueStmtResult::Kind::NoResult:
|
||||
// This is fine, we will have typed the expression as Void (we verify
|
||||
// as such in the ASTVerifier).
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::CircularReference:
|
||||
// Already diagnosed.
|
||||
break;
|
||||
case IsSingleValueStmtResult::Kind::UnhandledStmt:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PreCheckTarget::markAnyValidSingleValueStmts(Expr *E) {
|
||||
auto findAssignment = [&]() -> AssignExpr * {
|
||||
// Don't consider assignments if we have a parent expression (as otherwise
|
||||
// this would be effectively allowing it in an arbitrary expression
|
||||
// position).
|
||||
if (Parent.getAsExpr())
|
||||
return nullptr;
|
||||
|
||||
// Look through optional exprs, which are present for e.g x?.y = z, as
|
||||
// we wrap the entire assign in the optional evaluation of the destination.
|
||||
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(E)) {
|
||||
E = OEE->getSubExpr();
|
||||
while (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(E))
|
||||
E = IIO->getSubExpr();
|
||||
}
|
||||
return dyn_cast<AssignExpr>(E);
|
||||
};
|
||||
|
||||
if (auto *AE = findAssignment())
|
||||
markValidSingleValueStmt(AE->getSrc());
|
||||
}
|
||||
|
||||
void PreCheckTarget::markAnyValidSingleValueStmts(Stmt *S) {
|
||||
// Valid in a return/throw/then.
|
||||
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
|
||||
if (RS->hasResult())
|
||||
markValidSingleValueStmt(RS->getResult());
|
||||
}
|
||||
if (auto *TS = dyn_cast<ThrowStmt>(S))
|
||||
markValidSingleValueStmt(TS->getSubExpr());
|
||||
|
||||
if (auto *TS = dyn_cast<ThenStmt>(S))
|
||||
markValidSingleValueStmt(TS->getResult());
|
||||
}
|
||||
|
||||
void PreCheckTarget::diagnoseOutOfPlaceSingleValueStmtExprs(
|
||||
const SyntacticElementTarget &target) {
|
||||
// Top-level SingleValueStmtExprs are allowed in returns, throws, and
|
||||
// bindings.
|
||||
if (auto *E = target.getAsExpr()) {
|
||||
switch (target.getExprContextualTypePurpose()) {
|
||||
case CTP_ReturnStmt:
|
||||
case CTP_ThrowStmt:
|
||||
case CTP_Initialization:
|
||||
markValidSingleValueStmt(E);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto *SVE : OutOfPlaceSingleValueStmtExprs) {
|
||||
Ctx.Diags.diagnose(SVE->getLoc(), diag::single_value_stmt_out_of_place,
|
||||
SVE->getStmt()->getKind());
|
||||
}
|
||||
}
|
||||
|
||||
TypeExpr *PreCheckTarget::simplifyNestedTypeExpr(UnresolvedDotExpr *UDE) {
|
||||
if (!UDE->getName().isSimpleName() ||
|
||||
UDE->getName().isSpecial())
|
||||
@@ -2427,9 +2618,7 @@ bool ConstraintSystem::preCheckTarget(SyntacticElementTarget &target) {
|
||||
auto &ctx = DC->getASTContext();
|
||||
|
||||
FrontendStatsTracer StatsTracer(ctx.Stats, "precheck-target");
|
||||
PreCheckTarget preCheck(DC);
|
||||
|
||||
auto newTarget = target.walk(preCheck);
|
||||
auto newTarget = PreCheckTarget::check(target);
|
||||
if (!newTarget)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -340,10 +340,7 @@ public:
|
||||
}
|
||||
|
||||
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
|
||||
// We skip out-of-place expr checking here since we've already performed it.
|
||||
performSyntacticExprDiagnostics(expr, dcStack.back(), /*ctp*/ std::nullopt,
|
||||
/*isExprStmt=*/false,
|
||||
/*disableOutOfPlaceExprChecking*/ true);
|
||||
performSyntacticExprDiagnostics(expr, dcStack.back(), /*isExprStmt=*/false);
|
||||
|
||||
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
|
||||
if (closure->isSeparatelyTypeChecked()) {
|
||||
@@ -389,9 +386,7 @@ void constraints::performSyntacticDiagnosticsForTarget(
|
||||
switch (target.kind) {
|
||||
case SyntacticElementTarget::Kind::expression: {
|
||||
// First emit diagnostics for the main expression.
|
||||
performSyntacticExprDiagnostics(target.getAsExpr(), dc,
|
||||
target.getExprContextualTypePurpose(),
|
||||
isExprStmt);
|
||||
performSyntacticExprDiagnostics(target.getAsExpr(), dc, isExprStmt);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -400,22 +395,15 @@ void constraints::performSyntacticDiagnosticsForTarget(
|
||||
|
||||
// First emit diagnostics for the main expression.
|
||||
performSyntacticExprDiagnostics(stmt->getTypeCheckedSequence(), dc,
|
||||
CTP_ForEachSequence, isExprStmt);
|
||||
isExprStmt);
|
||||
|
||||
if (auto *whereExpr = stmt->getWhere())
|
||||
performSyntacticExprDiagnostics(whereExpr, dc, CTP_Condition,
|
||||
/*isExprStmt*/ false);
|
||||
performSyntacticExprDiagnostics(whereExpr, dc, /*isExprStmt*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
case SyntacticElementTarget::Kind::function: {
|
||||
// Check for out of place expressions. This needs to be done on the entire
|
||||
// function body rather than on individual expressions since we need the
|
||||
// context of the parent nodes.
|
||||
auto *body = target.getFunctionBody();
|
||||
diagnoseOutOfPlaceExprs(dc->getASTContext(), body,
|
||||
/*contextualPurpose*/ std::nullopt);
|
||||
|
||||
FunctionSyntacticDiagnosticWalker walker(dc);
|
||||
body->walk(walker);
|
||||
return;
|
||||
|
||||
@@ -1158,6 +1158,8 @@ func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) {
|
||||
// expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Bool'}}
|
||||
// expected-error@-2 {{closure passed to parameter of type 'Bool?' that does not accept a closure}}
|
||||
// expected-error@-3 {{cannot convert value of type 'Void' to expected condition type 'Bool'}}
|
||||
// expected-error@-4 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-5 {{'if' must have an unconditional 'else' to be used as expression}}
|
||||
} // expected-error {{expected '{' after 'if' condition}}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,8 +384,10 @@ func testVoidConversion() {
|
||||
|
||||
func testReturnMismatch() {
|
||||
let _ = if .random() {
|
||||
return 1 // expected-error {{unexpected non-void return value in void function}}
|
||||
// expected-note@-1 {{did you mean to add a return type?}}
|
||||
return 1
|
||||
// expected-error@-1 {{cannot use 'return' to transfer control out of 'if' expression}}
|
||||
// expected-error@-2 {{unexpected non-void return value in void function}}
|
||||
// expected-note@-3 {{did you mean to add a return type?}}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -433,7 +435,9 @@ func testAssignment() {
|
||||
}
|
||||
|
||||
struct TestBadReturn {
|
||||
var y = if .random() { return } else { 0 } // expected-error {{return invalid outside of a func}}
|
||||
var y = if .random() { return } else { 0 }
|
||||
// expected-error@-1 {{return invalid outside of a func}}
|
||||
// expected-error@-2 {{cannot use 'return' to transfer control out of 'if' expression}}
|
||||
}
|
||||
|
||||
struct SomeError: Error {}
|
||||
|
||||
@@ -23,10 +23,10 @@ func baz() -> String? {
|
||||
var x: Int? = {
|
||||
print( // expected-error {{cannot convert value of type '()' to closure result type 'Int?'}}
|
||||
// expected-note@-1 {{to match this opening '('}}
|
||||
switch baz() {
|
||||
switch baz() { // expected-error {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
case ""?:
|
||||
return nil
|
||||
return nil // expected-error {{cannot use 'return' to transfer control out of 'switch' expression}}
|
||||
default:
|
||||
return nil
|
||||
return nil // expected-error {{cannot use 'return' to transfer control out of 'switch' expression}}
|
||||
}
|
||||
}() // expected-error {{expected ')' in expression list}}
|
||||
|
||||
@@ -191,7 +191,9 @@ func testAssignment() {
|
||||
}
|
||||
|
||||
struct TestBadReturn {
|
||||
var y = switch Bool.random() { case true: return case false: 0 } // expected-error {{return invalid outside of a func}}
|
||||
var y = switch Bool.random() { case true: return case false: 0 }
|
||||
// expected-error@-1 {{return invalid outside of a func}}
|
||||
// expected-error@-2 {{cannot use 'return' to transfer control out of 'switch' expression}}
|
||||
}
|
||||
|
||||
func testNil1(_ x: Bool) {
|
||||
|
||||
@@ -82,6 +82,8 @@ func missingControllingExprInIf() {
|
||||
if { // expected-error {{missing condition in 'if' statement}}
|
||||
} // expected-error {{expected '{' after 'if' condition}}
|
||||
// expected-error@-2 {{cannot convert value of type 'Void' to expected condition type 'Bool'}}
|
||||
// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-4 {{'if' must have an unconditional 'else' to be used as expression}}
|
||||
|
||||
if // expected-error {{missing condition in 'if' statement}}
|
||||
{
|
||||
@@ -239,6 +241,7 @@ func missingControllingExprInSwitch() {
|
||||
|
||||
switch { // expected-error {{expected expression in 'switch' statement}}
|
||||
} // expected-error {{expected '{' after 'switch' subject expression}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
switch // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' statement body must have at least one 'case' or 'default' block}}
|
||||
{
|
||||
|
||||
@@ -116,12 +116,14 @@ takesValue(if .random() { 0 } else { 1 })
|
||||
do {
|
||||
takesValue(x: if .random() { 0 } else { 1 })
|
||||
// expected-error@-1 {{extraneous argument label 'x:' in call}}
|
||||
// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
takesValue(_: x: if .random() { 0 } else { 1 })
|
||||
// expected-error@-1 {{expected argument label before colon}}
|
||||
// expected-error@-2 {{expected ',' separator}}
|
||||
// expected-error@-3 {{cannot find 'x' in scope}}
|
||||
// expected-error@-4 {{extra argument in call}}
|
||||
// expected-error@-5 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
func takesValueWithLabel<T>(x: T) {}
|
||||
do {
|
||||
@@ -133,6 +135,7 @@ do {
|
||||
// expected-error@-2 {{expected ',' separator}}
|
||||
// expected-error@-3 {{cannot find 'y' in scope}}
|
||||
// expected-error@-4 {{extra argument in call}}
|
||||
// expected-error@-5 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
func takesValueAndTrailingClosure<T>(_ x: T, _ fn: () -> Int) {}
|
||||
takesValueAndTrailingClosure(if .random() { 0 } else { 1 }) { 2 }
|
||||
@@ -141,6 +144,7 @@ takesValueAndTrailingClosure(if .random() { 0 } else { 1 }) { 2 }
|
||||
func takesInOut<T>(_ x: inout T) {}
|
||||
takesInOut(&if .random() { 1 } else { 2 })
|
||||
// expected-error@-1 {{cannot pass immutable value of type 'Int' as inout argument}}
|
||||
// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
struct HasSubscript {
|
||||
static subscript(x: Int) -> Void { () }
|
||||
@@ -462,6 +466,9 @@ let o = !if .random() { true } else { false } // expected-error {{'if' may only
|
||||
let p = if .random() { 1 } else { 2 } + // expected-error {{ambiguous use of operator '+'}}
|
||||
if .random() { 3 } else { 4 } +
|
||||
if .random() { 5 } else { 6 }
|
||||
// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-3 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
let p1 = if .random() { 1 } else { 2 } + 5
|
||||
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
@@ -504,6 +511,7 @@ do {
|
||||
// FIXME: The type error is likely due to not solving the conjunction before attempting default type var bindings.
|
||||
let _ = (if .random() { Int?.none } else { 1 as Int? })?.bitWidth
|
||||
// expected-error@-1 {{type of expression is ambiguous without a type annotation}}
|
||||
// expected-error@-2 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
do {
|
||||
let _ = if .random() { Int?.none } else { 1 as Int? }!
|
||||
@@ -578,6 +586,8 @@ func stmts() {
|
||||
for _ in if .random() { [true] } else { [false] } {}
|
||||
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
for _ in if .random() { [true] } else { [false] } {} // expected-error {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
// Make sure this doesn't parse as an if expr pattern with a label.
|
||||
let x = 0
|
||||
switch 0 {
|
||||
@@ -609,9 +619,9 @@ func returnBranches() -> Int {
|
||||
|
||||
func returnBranches1() -> Int {
|
||||
return if .random() { // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}}
|
||||
return 0
|
||||
return 0 // expected-error {{cannot use 'return' to transfer control out of 'if' expression}}
|
||||
} else {
|
||||
return 1
|
||||
return 1 // expected-error {{cannot use 'return' to transfer control out of 'if' expression}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,12 +163,14 @@ takesValue(switch Bool.random() { case true: 1 case false: 2 })
|
||||
do {
|
||||
takesValue(x: switch Bool.random() { case true: 1 case false: 2 })
|
||||
// expected-error@-1 {{extraneous argument label 'x:' in call}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
takesValue(_: x: switch Bool.random() { case true: 1 case false: 2 })
|
||||
// expected-error@-1 {{expected argument label before colon}}
|
||||
// expected-error@-2 {{expected ',' separator}}
|
||||
// expected-error@-3 {{cannot find 'x' in scope}}
|
||||
// expected-error@-4 {{extra argument in call}}
|
||||
// expected-error@-5 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
func takesValueWithLabel<T>(x: T) {}
|
||||
do {
|
||||
@@ -180,6 +182,7 @@ do {
|
||||
// expected-error@-2 {{expected ',' separator}}
|
||||
// expected-error@-3 {{cannot find 'y' in scope}}
|
||||
// expected-error@-4 {{extra argument in call}}
|
||||
// expected-error@-5 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
func takesValueAndTrailingClosure<T>(_ x: T, _ fn: () -> Int) {}
|
||||
takesValueAndTrailingClosure(switch Bool.random() { case true: 0 case false: 1 }) { 2 }
|
||||
@@ -188,6 +191,7 @@ takesValueAndTrailingClosure(switch Bool.random() { case true: 0 case false: 1 }
|
||||
func takesInOut<T>(_ x: inout T) {}
|
||||
takesInOut(&switch Bool.random() { case true: 1 case false: 2 })
|
||||
// expected-error@-1 {{cannot pass immutable value of type 'Int' as inout argument}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
struct HasSubscript {
|
||||
static subscript(x: Int) -> Void { () }
|
||||
@@ -496,9 +500,11 @@ do {
|
||||
do {
|
||||
_ = (switch fatalError() {}, 1) // expected-error {{expected '{' after 'switch' subject expression}}
|
||||
// expected-error@-1 {{extra trailing closure passed in call}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
_ = (switch fatalError() { #if FOO
|
||||
// expected-error@-1 {{extra trailing closure passed in call}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
#endif
|
||||
}, 0) // expected-error {{expected '{' after 'switch' subject expression}}
|
||||
|
||||
@@ -507,6 +513,7 @@ do {
|
||||
// expected-error@-2 {{type '() -> ()' cannot conform to 'RandomNumberGenerator'}}
|
||||
// expected-note@-3 {{required by static method 'random(using:)' where 'T' = '() -> ()'}}
|
||||
// expected-note@-4 {{only concrete types such as structs, enums and classes can conform to protocols}}
|
||||
// expected-error@-5 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
case true: // expected-error {{'case' label can only appear inside a 'switch' statement}}
|
||||
1
|
||||
case false: // expected-error {{'case' label can only appear inside a 'switch' statement}}
|
||||
@@ -519,6 +526,7 @@ do {
|
||||
// expected-error@-2 {{type '() -> ()' cannot conform to 'RandomNumberGenerator'}}
|
||||
// expected-note@-3 {{required by static method 'random(using:)' where 'T' = '() -> ()'}}
|
||||
// expected-note@-4 {{only concrete types such as structs, enums and classes can conform to protocols}}
|
||||
// expected-error@-5 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
case true: // expected-error {{'case' label can only appear inside a 'switch' statement}}
|
||||
1
|
||||
case false: // expected-error {{'case' label can only appear inside a 'switch' statement}}
|
||||
@@ -618,6 +626,9 @@ let m = !switch Bool.random() { case true: true case false: true }
|
||||
let n = switch Bool.random() { case true: 1 case false: 2 } + // expected-error {{ambiguous use of operator '+'}}
|
||||
switch Bool.random() { case true: 3 case false: 4 } +
|
||||
switch Bool.random() { case true: 5 case false: 6 }
|
||||
// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
// expected-error@-3 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
|
||||
let n1 = switch Bool.random() { case true: 1 case false: 2 } + 5
|
||||
// expected-error@-1 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
@@ -660,6 +671,7 @@ do {
|
||||
// FIXME: The type error is likely due to not solving the conjunction before attempting default type var bindings.
|
||||
let _ = (switch Bool.random() { case true: Int?.none case false: 1 })?.bitWidth
|
||||
// expected-error@-1 {{type of expression is ambiguous without a type annotation}}
|
||||
// expected-error@-2 {{'switch' may only be used as expression in return, throw, or as the source of an assignment}}
|
||||
}
|
||||
do {
|
||||
let _ = switch Bool.random() { case true: Int?.none case false: 1 }!
|
||||
@@ -749,9 +761,9 @@ func returnBranches() -> Int {
|
||||
func returnBranches1() -> Int {
|
||||
return switch Bool.random() { // expected-error {{cannot convert return expression of type 'Void' to return type 'Int'}}
|
||||
case true:
|
||||
return 0
|
||||
return 0 // expected-error {{cannot use 'return' to transfer control out of 'switch' expression}}
|
||||
case false:
|
||||
return 1
|
||||
return 1 // expected-error {{cannot use 'return' to transfer control out of 'switch' expression}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@ let x = if .random() {
|
||||
then 0
|
||||
// expected-error@-1 {{cannot find 'then' in scope}}
|
||||
// expected-error@-2 {{consecutive statements on a line must be separated by ';'}}
|
||||
} else {
|
||||
} else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}}
|
||||
1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user