Skip to content

Commit 4ef79dd

Browse files
committed
Make "unused" a pattern operator
1 parent c65c57d commit 4ef79dd

25 files changed

+260
-87
lines changed

toolchain/check/handle_binding_pattern.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ auto HandleParseNode(Context& context, Parse::UnderscoreNameId node_id)
3030

3131
// TODO: make this function shorter by factoring pieces out.
3232
static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
33-
Parse::NodeKind node_kind) -> bool {
33+
Parse::NodeKind node_kind,
34+
bool is_unused = false) -> bool {
3435
// TODO: split this into smaller, more focused functions.
3536
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
3637
auto [cast_type_inst_id, cast_type_id] =
@@ -52,11 +53,6 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
5253
context.node_stack()
5354
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::RefBindingName>();
5455

55-
// The name in a binding may be wrapped in `unused`.
56-
bool is_unused =
57-
context.node_stack()
58-
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::UnusedBindingName>();
59-
6056
SemIR::InstKind pattern_inst_kind;
6157
switch (node_kind) {
6258
case Parse::NodeKind::CompileTimeBindingPattern:
@@ -419,9 +415,52 @@ auto HandleParseNode(Context& context, Parse::TemplateBindingNameId node_id)
419415
return true;
420416
}
421417

422-
auto HandleParseNode(Context& context, Parse::UnusedBindingNameId node_id)
423-
-> bool {
424-
context.node_stack().Push(node_id);
418+
static auto MarkPatternUnused(Context& context, SemIR::InstId inst_id) -> bool {
419+
bool any_marked = false;
420+
auto inst = context.insts().Get(inst_id);
421+
if (auto bind = inst.TryAs<SemIR::AnyBindingPattern>()) {
422+
auto& name = context.entity_names().Get(bind->entity_name_id);
423+
name.is_unused = true;
424+
if (name.name_id != SemIR::NameId::Underscore) {
425+
any_marked = true;
426+
}
427+
} else if (auto tuple = inst.TryAs<SemIR::TuplePattern>()) {
428+
for (auto elem_id : context.inst_blocks().Get(tuple->elements_id)) {
429+
if (MarkPatternUnused(context, elem_id)) {
430+
any_marked = true;
431+
}
432+
}
433+
} else if (auto var = inst.TryAs<SemIR::VarPattern>()) {
434+
if (MarkPatternUnused(context, var->subpattern_id)) {
435+
any_marked = true;
436+
}
437+
} else if (auto var_param = inst.TryAs<SemIR::VarParamPattern>()) {
438+
if (MarkPatternUnused(context, var_param->subpattern_id)) {
439+
any_marked = true;
440+
}
441+
} else if (auto ref_param = inst.TryAs<SemIR::RefParamPattern>()) {
442+
if (MarkPatternUnused(context, ref_param->subpattern_id)) {
443+
any_marked = true;
444+
}
445+
} else if (auto val_param = inst.TryAs<SemIR::ValueParamPattern>()) {
446+
if (MarkPatternUnused(context, val_param->subpattern_id)) {
447+
any_marked = true;
448+
}
449+
}
450+
// TODO: Handle other patterns if necessary.
451+
return any_marked;
452+
}
453+
454+
auto HandleParseNode(Context& context, Parse::UnusedPatternId node_id) -> bool {
455+
auto [child_node, child_inst_id] =
456+
context.node_stack().PopPatternWithNodeId();
457+
if (!MarkPatternUnused(context, child_inst_id)) {
458+
CARBON_DIAGNOSTIC(
459+
UnusedPatternNoBindings, Warning,
460+
"`unused` modifier has no effect on pattern without bindings");
461+
context.emitter().Emit(node_id, UnusedPatternNoBindings);
462+
}
463+
context.node_stack().Push(node_id, child_inst_id);
425464
return true;
426465
}
427466

toolchain/check/handle_function.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,60 @@ static auto BuildFunctionDecl(Context& context,
482482
return {function_decl.function_id, decl_id};
483483
}
484484

485+
CARBON_DIAGNOSTIC(
486+
UnusedParamInDeclaration, Error,
487+
"`unused` modifier cannot be used on a declaration that is not a "
488+
"definition");
489+
490+
static auto CheckUnusedBindingsInPattern(Context& context,
491+
SemIR::InstId pattern_id) -> void {
492+
auto inst = context.insts().Get(pattern_id);
493+
if (auto var_pattern = inst.TryAs<SemIR::VarPattern>()) {
494+
CheckUnusedBindingsInPattern(context, var_pattern->subpattern_id);
495+
} else if (auto var_param = inst.TryAs<SemIR::VarParamPattern>()) {
496+
CheckUnusedBindingsInPattern(context, var_param->subpattern_id);
497+
} else if (auto ref_param = inst.TryAs<SemIR::RefParamPattern>()) {
498+
CheckUnusedBindingsInPattern(context, ref_param->subpattern_id);
499+
} else if (auto val_param = inst.TryAs<SemIR::ValueParamPattern>()) {
500+
CheckUnusedBindingsInPattern(context, val_param->subpattern_id);
501+
} else if (auto ref_bind = inst.TryAs<SemIR::RefBindingPattern>()) {
502+
auto& entity_name = context.entity_names().Get(ref_bind->entity_name_id);
503+
if (entity_name.is_unused &&
504+
entity_name.name_id != SemIR::NameId::Underscore) {
505+
context.emitter().Emit(LocIdForDiagnostics(pattern_id),
506+
UnusedParamInDeclaration);
507+
}
508+
} else if (auto val_bind = inst.TryAs<SemIR::ValueBindingPattern>()) {
509+
auto& entity_name = context.entity_names().Get(val_bind->entity_name_id);
510+
if (entity_name.is_unused &&
511+
entity_name.name_id != SemIR::NameId::Underscore) {
512+
context.emitter().Emit(LocIdForDiagnostics(pattern_id),
513+
UnusedParamInDeclaration);
514+
}
515+
} else if (auto tuple_pattern = inst.TryAs<SemIR::TuplePattern>()) {
516+
auto elements = context.inst_blocks().Get(tuple_pattern->elements_id);
517+
for (auto element_id : elements) {
518+
CheckUnusedBindingsInPattern(context, element_id);
519+
}
520+
}
521+
}
522+
523+
static auto DiagnoseUnusedParamsInDeclaration(Context& context,
524+
SemIR::FunctionId function_id)
525+
-> void {
526+
const auto& function = context.functions().Get(function_id);
527+
if (function.param_patterns_id.has_value()) {
528+
for (auto pattern_id :
529+
context.inst_blocks().Get(function.param_patterns_id)) {
530+
CheckUnusedBindingsInPattern(context, pattern_id);
531+
}
532+
}
533+
}
534+
485535
auto HandleParseNode(Context& context, Parse::FunctionDeclId node_id) -> bool {
486-
BuildFunctionDecl(context, node_id, /*is_definition=*/false);
536+
auto [function_id, decl_id] =
537+
BuildFunctionDecl(context, node_id, /*is_definition=*/false);
538+
DiagnoseUnusedParamsInDeclaration(context, function_id);
487539
context.decl_name_stack().PopScope();
488540
return true;
489541
}

toolchain/check/merge.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,14 +452,14 @@ static auto CheckRedeclParamSyntax(Context& context,
452452
auto new_node_id = *new_iter;
453453
auto new_node_kind = context.parse_tree().node_kind(new_node_id);
454454
// Skip over "unused" markers.
455-
if (new_node_kind == Parse::NodeKind::UnusedBindingName) {
455+
if (new_node_kind == Parse::NodeKind::UnusedPattern) {
456456
++new_iter;
457457
new_node_id = *new_iter;
458458
new_node_kind = context.parse_tree().node_kind(new_node_id);
459459
}
460460
auto prev_node_id = *prev_iter;
461461
auto prev_node_kind = context.parse_tree().node_kind(prev_node_id);
462-
if (prev_node_kind == Parse::NodeKind::UnusedBindingName) {
462+
if (prev_node_kind == Parse::NodeKind::UnusedPattern) {
463463
++prev_iter;
464464
prev_node_id = *prev_iter;
465465
prev_node_kind = context.parse_tree().node_kind(prev_node_id);

toolchain/check/node_stack.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,6 @@ class NodeStack {
473473
case Parse::NodeKind::TemplateBindingName:
474474
case Parse::NodeKind::TupleLiteralStart:
475475
case Parse::NodeKind::TuplePatternStart:
476-
case Parse::NodeKind::UnusedBindingName:
477476
case Parse::NodeKind::VariableInitializer:
478477
case Parse::NodeKind::VariableIntroducer:
479478
return Id::Kind::None;

toolchain/check/testdata/class/fail_self.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ class Class {
2020
fn G() -> Self;
2121
}
2222

23-
// CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:12: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList]
23+
// CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:19: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList]
2424
// CHECK:STDERR: fn Class.F(unused self: Self) {
25-
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
25+
// CHECK:STDERR: ^~~~~~~~~~
2626
// CHECK:STDERR:
2727
fn Class.F(unused self: Self) {
2828
}

toolchain/check/testdata/class/virtual_modifiers.carbon

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,12 @@ base class Base(T:! type) {
416416

417417
class D1 {
418418
extend base: Base(T1);
419-
// CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE+7]]:29: error: type `<pattern for T2*>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T1*>` [RedeclParamDiffersType]
419+
// CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE+7]]:36: error: type `<pattern for T2*>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T1*>` [RedeclParamDiffersType]
420420
// CHECK:STDERR: override fn F[self: Self](unused t: T2*) { }
421-
// CHECK:STDERR: ^~~~~~~~~~~~~
422-
// CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE-8]]:28: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
421+
// CHECK:STDERR: ^~~~~~
422+
// CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE-8]]:35: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
423423
// CHECK:STDERR: virtual fn F[self: Self](unused t: T1*) { }
424-
// CHECK:STDERR: ^~~~~~~~~~~~~
424+
// CHECK:STDERR: ^~~~~~
425425
// CHECK:STDERR:
426426
override fn F[self: Self](unused t: T2*) { }
427427
}
@@ -435,12 +435,12 @@ abstract class Base(T:! type) {
435435
}
436436
class Derived(T:! type) {
437437
extend base: Base(T);
438-
// CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE+7]]:29: error: type `<pattern for T>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T*>` [RedeclParamDiffersType]
438+
// CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE+7]]:36: error: type `<pattern for T>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T*>` [RedeclParamDiffersType]
439439
// CHECK:STDERR: override fn F[self: Self](unused t: T) { }
440-
// CHECK:STDERR: ^~~~~~~~~~~
441-
// CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE-7]]:28: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
440+
// CHECK:STDERR: ^~~~
441+
// CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE-7]]:35: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
442442
// CHECK:STDERR: virtual fn F[self: Self](unused t: T*) { }
443-
// CHECK:STDERR: ^~~~~~~~~~~~
443+
// CHECK:STDERR: ^~~~~
444444
// CHECK:STDERR:
445445
override fn F[self: Self](unused t: T) { }
446446
}

toolchain/check/testdata/deduce/array.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ fn G() -> i32 {
9393
// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+7]]:12: note: type `array(D, 3)` does not implement interface `Core.ImplicitAs(array(C, 3))` [MissingImplInMemberAccessNote]
9494
// CHECK:STDERR: return F(a);
9595
// CHECK:STDERR: ^
96-
// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE-11]]:29: note: initializing function parameter [InCallToFunctionParam]
96+
// CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE-11]]:36: note: initializing function parameter [InCallToFunctionParam]
9797
// CHECK:STDERR: fn F[N:! Core.IntLiteral()](unused a: array(C, N)) -> i32 { return N; }
98-
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
98+
// CHECK:STDERR: ^~~~~~~~~~~~~~
9999
// CHECK:STDERR:
100100
return F(a);
101101
}

toolchain/check/testdata/deduce/binding_pattern.carbon

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ fn F(U:! type, V:! type) {
2727
// CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE+7]]:15: note: type `{}` does not implement interface `Core.ImplicitAs(V)` [MissingImplInMemberAccessNote]
2828
// CHECK:STDERR: C(V).Create({});
2929
// CHECK:STDERR: ^~
30-
// CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE-10]]:13: note: initializing function parameter [InCallToFunctionParam]
30+
// CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE-10]]:20: note: initializing function parameter [InCallToFunctionParam]
3131
// CHECK:STDERR: fn Create(unused value: T) {}
32-
// CHECK:STDERR: ^~~~~~~~~~~~~~~
32+
// CHECK:STDERR: ^~~~~~~~
3333
// CHECK:STDERR:
3434
C(V).Create({});
3535
}
@@ -50,9 +50,9 @@ fn F(U:! type, V:! type where {} impls Core.ImplicitAs(.Self)) {
5050
// CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE+7]]:15: note: type `{}` does not implement interface `Core.ImplicitAs(V)` [MissingImplInMemberAccessNote]
5151
// CHECK:STDERR: C(V).Create({});
5252
// CHECK:STDERR: ^~
53-
// CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE-11]]:13: note: initializing function parameter [InCallToFunctionParam]
53+
// CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE-11]]:20: note: initializing function parameter [InCallToFunctionParam]
5454
// CHECK:STDERR: fn Create(unused value: T) {}
55-
// CHECK:STDERR: ^~~~~~~~~~~~~~~
55+
// CHECK:STDERR: ^~~~~~~~
5656
// CHECK:STDERR:
5757
C(V).Create({});
5858
}

toolchain/check/testdata/facet/fail_namespace_type.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ fn F() {
4242
// CHECK:STDERR: fail_namespace_argument.carbon:[[@LINE+7]]:5: error: expression cannot be used as a value [UseOfNonExprAsValue]
4343
// CHECK:STDERR: G(N);
4444
// CHECK:STDERR: ^
45-
// CHECK:STDERR: fail_namespace_argument.carbon:[[@LINE-6]]:13: note: initializing function parameter [InCallToFunctionParam]
45+
// CHECK:STDERR: fail_namespace_argument.carbon:[[@LINE-6]]:20: note: initializing function parameter [InCallToFunctionParam]
4646
// CHECK:STDERR: fn G[T:! Z](unused a: T) {}
47-
// CHECK:STDERR: ^~~~~~~~~~~
47+
// CHECK:STDERR: ^~~~
4848
// CHECK:STDERR:
4949
G(N);
5050
}

toolchain/check/testdata/function/call/fail_param_type.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ fn F() {
2121
// CHECK:STDERR: fail_param_type.carbon:[[@LINE+7]]:5: note: type `Core.FloatLiteral` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote]
2222
// CHECK:STDERR: G(1.0);
2323
// CHECK:STDERR: ^~~
24-
// CHECK:STDERR: fail_param_type.carbon:[[@LINE-9]]:6: note: initializing function parameter [InCallToFunctionParam]
24+
// CHECK:STDERR: fail_param_type.carbon:[[@LINE-9]]:13: note: initializing function parameter [InCallToFunctionParam]
2525
// CHECK:STDERR: fn G(unused a: i32) {}
26-
// CHECK:STDERR: ^~~~~~~~~~~~~
26+
// CHECK:STDERR: ^~~~~~
2727
// CHECK:STDERR:
2828
G(1.0);
2929
}

0 commit comments

Comments
 (0)