mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:50:14 +01:00
Implement the toolchain side of proposal #7254, removing the `:!` binding syntax for generic and template parameters in favor of the keywords `generic`, `template`, and `runtime` plus contextual defaults for phase. For valid programs this is semantics-preserving: each binding resolves to the same phase, and produces the same SemIR, as it did under `:!`/`:`. The parser derives a binding's phase from its syntactic context plus any explicit phase keyword; new diagnostics and error recovery for misused keywords are described below. Implementation details for each component: - Lexer: remove the `:!` (`ColonExclaim`) token, move its virtual parse-node budget onto `:`, and add the `generic` and `runtime` keywords. - Parser: thread a `BindingContext` (`ExplicitParam`, `DeducedParam`, or `CompileTimeEntityParam`) from declaration introducers down through parameter lists to each binding pattern, using a one-token lookahead to distinguish a name-qualifier parameter list from a declaration's own final list. Parameters of a compile-time entity (`class`, `interface`, `constraint`, `choice`, `alias`, `export`, `namespace`) and deduced `[]` parameters default to checked generic; explicit function parameters and local bindings default to runtime. `HandleBindingPattern` resolves the phase from that context plus the keyword: a `generic` keyword needs no node of its own (the phase is carried by the binding's node kind), while a `runtime` keyword is preserved as a `RuntimeBindingName` node so `check` can name it in a diagnostic. A phase keyword that is merely redundant with the contextual default is diagnosed here, without invalidating the parse tree. - Check: a phase keyword that is invalid for its context (for example `runtime` on a checked-generic parameter) is diagnosed here, and recovers by building an error binding that still introduces the name so that later uses of it do not produce cascading errors. The removed `:!` syntax is now rejected as an ordinary parse error. The `form`/`:?`/`->?` ("extended types") portion of proposal #7254 is left for a separate change. Assisted-by: Claude Code
138 lines
4.0 KiB
C++
138 lines
4.0 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/format/formatter.h"
|
|
|
|
namespace Carbon::Format {
|
|
|
|
auto Formatter::Run() -> bool {
|
|
if (tokens_->has_errors()) {
|
|
// TODO: Error recovery.
|
|
return false;
|
|
}
|
|
|
|
// If there are no tokens or comments, format as empty.
|
|
if (tokens_->size() == 0 && next_comment_ == comments_end_) {
|
|
*out_ << "\n";
|
|
return true;
|
|
}
|
|
|
|
for (auto token : tokens_->tokens()) {
|
|
auto token_kind = tokens_->GetKind(token);
|
|
|
|
// Emit any comments that come before this token in the source. Trailing
|
|
// comments are attached to the still-open current line; full-line comments
|
|
// are emitted on their own line.
|
|
while (next_comment_ != comments_end_ &&
|
|
tokens_->IsAfterComment(token, *next_comment_)) {
|
|
EmitComment();
|
|
}
|
|
|
|
switch (token_kind) {
|
|
case Lex::TokenKind::FileStart:
|
|
break;
|
|
|
|
case Lex::TokenKind::FileEnd:
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
case Lex::TokenKind::OpenCurlyBrace:
|
|
PrepareForSpacedContent();
|
|
*out_ << "{";
|
|
// Check for `{}`.
|
|
if (NextToken(token) != tokens_->GetMatchedClosingToken(token)) {
|
|
RequireEmptyLine();
|
|
}
|
|
indent_ += 2;
|
|
break;
|
|
|
|
case Lex::TokenKind::CloseCurlyBrace:
|
|
indent_ -= 2;
|
|
PrepareForPackedContent();
|
|
*out_ << "}";
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
case Lex::TokenKind::Semi:
|
|
PrepareForPackedContent();
|
|
*out_ << ";";
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
default:
|
|
if (token_kind.IsOneOf({Lex::TokenKind::CloseParen,
|
|
Lex::TokenKind::Colon,
|
|
Lex::TokenKind::Comma})) {
|
|
PrepareForPackedContent();
|
|
} else {
|
|
PrepareForSpacedContent();
|
|
}
|
|
*out_ << tokens_->GetTokenText(token);
|
|
line_state_ = token_kind.is_opening_symbol()
|
|
? LineState::HasSeparator
|
|
: LineState::NeedsSeparator;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Materialize any newline deferred by the final line.
|
|
if (line_state_ == LineState::EndOfLine) {
|
|
*out_ << "\n";
|
|
line_state_ = LineState::Empty;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto Formatter::EmitComment() -> void {
|
|
auto comment = *next_comment_;
|
|
++next_comment_;
|
|
|
|
if (tokens_->IsTrailingComment(comment) && line_state_ != LineState::Empty) {
|
|
// Keep the trailing comment on the current line, separated by a space. The
|
|
// line still has content because its newline was deferred (`EndOfLine`) or
|
|
// not yet required.
|
|
*out_ << " " << tokens_->GetCommentText(comment);
|
|
} else {
|
|
// A full-line comment (or a trailing comment with nothing left to attach
|
|
// to) is emitted on its own line.
|
|
RequireEmptyLine();
|
|
PrepareForSpacedContent();
|
|
// TODO: We do need to adjust the indent of multi-line comments.
|
|
*out_ << tokens_->GetCommentText(comment);
|
|
}
|
|
// Comment text includes a terminating newline, so just update the state.
|
|
line_state_ = LineState::Empty;
|
|
}
|
|
|
|
auto Formatter::PrepareForPackedContent() -> void {
|
|
// Materialize a deferred newline before starting to fill a fresh line.
|
|
if (line_state_ == LineState::EndOfLine) {
|
|
*out_ << "\n";
|
|
line_state_ = LineState::Empty;
|
|
}
|
|
if (line_state_ == LineState::Empty) {
|
|
out_->indent(indent_);
|
|
line_state_ = LineState::HasSeparator;
|
|
}
|
|
}
|
|
|
|
auto Formatter::RequireEmptyLine() -> void {
|
|
// Defer the newline so a trailing comment can still attach to this line; it
|
|
// is materialized by the next content or at end of file.
|
|
if (line_state_ != LineState::Empty) {
|
|
line_state_ = LineState::EndOfLine;
|
|
}
|
|
}
|
|
|
|
auto Formatter::PrepareForSpacedContent() -> void {
|
|
if (line_state_ == LineState::NeedsSeparator) {
|
|
*out_ << " ";
|
|
line_state_ = LineState::HasSeparator;
|
|
} else {
|
|
PrepareForPackedContent();
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Format
|