<commit_before>bool is_divigering_stmt(Ast *stmt) {
	if (stmt->kind != Ast_ExprStmt) {
		return false;
	}
	Ast *expr = unparen_expr(stmt->ExprStmt.expr);
	if (expr->kind != Ast_CallExpr) {
		return false;
	}
	if (expr->CallExpr.proc->kind == Ast_BasicDirective) {
		String name = expr->CallExpr.proc->BasicDirective.name;
		return name == "panic";
	}
	Type *t = type_of_expr(expr->CallExpr.proc);
	t = base_type(t);
	return t->kind == Type_Proc && t->Proc.diverging;
}

void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) {
	if (stmts.count == 0) {
		return;
	}

	if (flags&Stmt_CheckScopeDecls) {
		check_scope_decls(ctx, stmts, cast(isize)(1.2*stmts.count));
	}

	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
	flags &= ~Stmt_FallthroughAllowed;

	isize max = stmts.count;
	for (isize i = stmts.count-1; i >= 0; i--) {
		if (stmts[i]->kind != Ast_EmptyStmt) {
			break;
		}
		max--;
	}
	isize max_non_constant_declaration = stmts.count;
	for (isize i = stmts.count-1; i >= 0; i--) {
		if (stmts[i]->kind == Ast_EmptyStmt) {
			// Okay
		} else if (stmts[i]->kind == Ast_ValueDecl && !stmts[i]->ValueDecl.is_mutable) {
			// Okay
		} else {
			break;
		}
		max_non_constant_declaration--;
	}

	for (isize i = 0; i < max; i++) {
		Ast *n = stmts[i];
		if (n->kind == Ast_EmptyStmt) {
			continue;
		}
		u32 new_flags = flags;
		if (ft_ok && i+1 == max) {
			new_flags |= Stmt_FallthroughAllowed;
		}

		check_stmt(ctx, n, new_flags);

		if (i+1 < max_non_constant_declaration) {
			switch (n->kind) {
			case Ast_ReturnStmt:
				error(n, "Statements after this 'return' are never executed");
				break;

			case Ast_BranchStmt:
				error(n, "Statements after this '%.*s' are never executed", LIT(n->BranchStmt.token.string));
				break;

			case Ast_ExprStmt:
				if (is_divigering_stmt(n)) {
					error(n, "Statements after a diverging procedure call are never executed");
				}
				break;
			}
		}
	}
}

bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) {
	// Iterate backwards
	for (isize n = stmts.count-1; n >= 0; n--) {
		Ast *stmt = stmts[n];
		if (stmt->kind == Ast_EmptyStmt) {
			// Okay
		} else if (stmt->kind == Ast_ValueDecl && !stmt->ValueDecl.is_mutable) {
			// Okay
		} else if (is_divigering_stmt(stmt)) {
			return true;
		} else {
			return check_is_terminating(stmt, label);
		}
	}

	return false;
}

bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
	for_array(i, stmts) {
		Ast *stmt = stmts[i];
		if (check_has_break(stmt, label, implicit)) {
			return true;
		}
	}<commit_msg>Fix `is_diverging_stmt` for invalid statements<commit_after>bool is_diverging_stmt(Ast *stmt) {
	if (stmt->kind != Ast_ExprStmt) {
		return false;
	}
	Ast *expr = unparen_expr(stmt->ExprStmt.expr);
	if (expr->kind != Ast_CallExpr) {
		return false;
	}
	if (expr->CallExpr.proc->kind == Ast_BasicDirective) {
		String name = expr->CallExpr.proc->BasicDirective.name;
		return name == "panic";
	}
	Type *t = type_of_expr(expr->CallExpr.proc);
	t = base_type(t);
	return t != nullptr && t->kind == Type_Proc && t->Proc.diverging;
}

void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) {
	if (stmts.count == 0) {
		return;
	}

	if (flags&Stmt_CheckScopeDecls) {
		check_scope_decls(ctx, stmts, cast(isize)(1.2*stmts.count));
	}

	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
	flags &= ~Stmt_FallthroughAllowed;

	isize max = stmts.count;
	for (isize i = stmts.count-1; i >= 0; i--) {
		if (stmts[i]->kind != Ast_EmptyStmt) {
			break;
		}
		max--;
	}
	isize max_non_constant_declaration = stmts.count;
	for (isize i = stmts.count-1; i >= 0; i--) {
		if (stmts[i]->kind == Ast_EmptyStmt) {
			// Okay
		} else if (stmts[i]->kind == Ast_ValueDecl && !stmts[i]->ValueDecl.is_mutable) {
			// Okay
		} else {
			break;
		}
		max_non_constant_declaration--;
	}

	for (isize i = 0; i < max; i++) {
		Ast *n = stmts[i];
		if (n->kind == Ast_EmptyStmt) {
			continue;
		}
		u32 new_flags = flags;
		if (ft_ok && i+1 == max) {
			new_flags |= Stmt_FallthroughAllowed;
		}

		check_stmt(ctx, n, new_flags);

		if (i+1 < max_non_constant_declaration) {
			switch (n->kind) {
			case Ast_ReturnStmt:
				error(n, "Statements after this 'return' are never executed");
				break;

			case Ast_BranchStmt:
				error(n, "Statements after this '%.*s' are never executed", LIT(n->BranchStmt.token.string));
				break;

			case Ast_ExprStmt:
				if (is_diverging_stmt(n)) {
					error(n, "Statements after a diverging procedure call are never executed");
				}
				break;
			}
		}
	}
}

bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) {
	// Iterate backwards
	for (isize n = stmts.count-1; n >= 0; n--) {
		Ast *stmt = stmts[n];
		if (stmt->kind == Ast_EmptyStmt) {
			// Okay
		} else if (stmt->kind == Ast_ValueDecl && !stmt->ValueDecl.is_mutable) {
			// Okay
		} else if (is_diverging_stmt(stmt)) {
			return true;
		} else {
			return check_is_terminating(stmt, label);
		}
	}

	return false;
}

bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
	for_array(i, stmts) {
		Ast *stmt = stmts[i];
		if (check_has_break(stmt, label, implicit)) {
			return true;
		}
	}
{'fraction_non_alphanumeric': 0.15665399239543726, 'fraction_numerical': 0.006463878326996198, 'mean_word_length': 3.523645743766122, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2926635', 'n_tokens_mistral': 2301, 'n_tokens_neox': 2119, 'n_words': 456}