mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Parse support for simple friend declarations (#7889)
This change only supports `friend` with an identifier operand, because there are a lot of open questions about the desired syntax beyond that.
This commit is contained in:
@@ -443,6 +443,15 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FriendIntroducerId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "friend decl not supported in check");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FriendDeclId node_id) -> bool {
|
||||
return context.TODO(node_id, "friend decl not supported in check");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)
|
||||
-> bool {
|
||||
auto class_id =
|
||||
|
||||
@@ -507,6 +507,7 @@ class NodeStack {
|
||||
case Parse::NodeKind::Forall:
|
||||
case Parse::NodeKind::FormLiteralKeyword:
|
||||
case Parse::NodeKind::FormLiteralOpenParen:
|
||||
case Parse::NodeKind::FriendIntroducer:
|
||||
case Parse::NodeKind::IdentifierNameQualifierWithParams:
|
||||
case Parse::NodeKind::IdentifierNameQualifierWithoutParams:
|
||||
case Parse::NodeKind::IdentifierPackageName:
|
||||
|
||||
@@ -200,6 +200,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedAssociatedConstantIdentifier)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedAssociatedConstantColon)
|
||||
CARBON_DIAGNOSTIC_KIND(PhaseKeywordInAssociatedConstant)
|
||||
CARBON_DIAGNOSTIC_KIND(RequireExpectedImpls)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierForFriend)
|
||||
|
||||
// Alias diagnostics.
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedAliasInitializer)
|
||||
|
||||
@@ -123,6 +123,8 @@ static constexpr auto DeclIntroducers = [] {
|
||||
set(Lex::TokenKind::Export, NodeKind::ExportIntroducer,
|
||||
StateKind::ExportName);
|
||||
// TODO: Treat `extend` as a declaration introducer.
|
||||
set(Lex::TokenKind::Friend, NodeKind::FriendIntroducer,
|
||||
StateKind::FriendDecl);
|
||||
set(Lex::TokenKind::Fn, NodeKind::FunctionIntroducer,
|
||||
StateKind::FunctionIntroducer);
|
||||
set(Lex::TokenKind::Impl, NodeKind::ImplIntroducer,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
|
||||
namespace Carbon::Parse {
|
||||
|
||||
// Handles processing of a complete `friend X` declaration.
|
||||
auto HandleFriendDecl(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
if (state.has_error) {
|
||||
context.RecoverFromDeclError(state, NodeKind::FriendDecl,
|
||||
/*skip_past_likely_end=*/true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier,
|
||||
NodeKind::IdentifierNameExpr)) {
|
||||
state.has_error = true;
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierForFriend, Error,
|
||||
"expected identifier for friend declaration");
|
||||
context.emitter().Emit(*context.position(), ExpectedIdentifierForFriend);
|
||||
context.RecoverFromDeclError(state, NodeKind::FriendDecl,
|
||||
/*skip_past_likely_end=*/true);
|
||||
return;
|
||||
}
|
||||
context.AddNodeExpectingDeclSemi(state, NodeKind::FriendDecl,
|
||||
Lex::TokenKind::Friend,
|
||||
/*is_def_allowed=*/false);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Parse
|
||||
@@ -439,6 +439,9 @@ CARBON_PARSE_NODE_KIND_DECLARATION(BaseIntroducer)
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(BaseColon)
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(BaseDecl)
|
||||
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(FriendIntroducer)
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(FriendDecl)
|
||||
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(InterfaceIntroducer)
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(InterfaceDefinitionStart)
|
||||
CARBON_PARSE_NODE_KIND_DECLARATION(InterfaceDefinition)
|
||||
|
||||
@@ -378,6 +378,10 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterParams)
|
||||
// ^~
|
||||
// 1. FunctionIntroducer
|
||||
//
|
||||
// friend ...
|
||||
// ^~~~~~
|
||||
// 1. FriendDecl
|
||||
//
|
||||
// impl ...
|
||||
// ^~~~
|
||||
// 1. ImplAfterIntroducer
|
||||
@@ -753,6 +757,15 @@ CARBON_PARSE_STATE(WhereFinish)
|
||||
// (state done)
|
||||
CARBON_PARSE_STATE(ExprStatementFinish)
|
||||
|
||||
// Handles a `friend` declaration.
|
||||
//
|
||||
// friend <identifier> ;
|
||||
// ^~~~~~~~~~~~~~
|
||||
// friend ??? ;
|
||||
// ^~~~~
|
||||
// (state done)
|
||||
CARBON_PARSE_STATE(FriendDecl)
|
||||
|
||||
// Handles a function's introducer.
|
||||
//
|
||||
// fn ...
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/class/friend.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/class/friend.carbon
|
||||
|
||||
// --- friend.carbon
|
||||
|
||||
class Foo {
|
||||
friend Bar;
|
||||
}
|
||||
|
||||
// --- fail_todo_qualified.carbon
|
||||
|
||||
class Baz {
|
||||
// CHECK:STDERR: fail_todo_qualified.carbon:[[@LINE+4]]:11: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: friend P.Q;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
friend P.Q;
|
||||
}
|
||||
|
||||
// --- fail_bad_syntax.carbon
|
||||
|
||||
class A {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:9: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR: friend;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
friend;
|
||||
}
|
||||
|
||||
class B {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:12: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: friend X extra_token;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
friend X extra_token;
|
||||
}
|
||||
|
||||
class C {
|
||||
friend
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:1: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR: }
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
class D {
|
||||
friend X
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:1: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: }
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
class E {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:10: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR: friend class;
|
||||
// CHECK:STDERR: ^~~~~
|
||||
// CHECK:STDERR:
|
||||
friend class;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: friend.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'Foo'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'Bar'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';'
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: fail_todo_qualified.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'Baz'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'P'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: fail_bad_syntax.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'A'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'B'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'X'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'C'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl 'friend' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'D'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'X'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl 'X' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: │ ╭─ClassIntroducer 'class'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'E'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
@@ -1650,6 +1650,25 @@ struct BaseDecl {
|
||||
Lex::SemiTokenIndex token;
|
||||
};
|
||||
|
||||
// Friend declaration
|
||||
// ------------------
|
||||
|
||||
using FriendIntroducer =
|
||||
LeafNode<NodeKind::FriendIntroducer, Lex::FriendTokenIndex>;
|
||||
|
||||
struct FriendDecl {
|
||||
static constexpr auto Kind = NodeKind::FriendDecl.Define(
|
||||
{.category = NodeCategory::Decl, .bracketed_by = FriendIntroducer::Kind});
|
||||
|
||||
FriendIntroducerId introducer;
|
||||
|
||||
// TODO: figure out the more general syntax for the name part of a friend
|
||||
// declaration.
|
||||
IdentifierNameExprId name;
|
||||
|
||||
Lex::SemiTokenIndex token;
|
||||
};
|
||||
|
||||
// Interface declarations and definitions
|
||||
// --------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user