mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Implement unused pattern bindings, continued (#6518)
Implementation of unused pattern bindings #2022, continued. Whereas previous PR #6460 took care of parsing, and PR #6479 prepared the stage by using _ in some test cases, this PR has the the actual implementation, using a simple dataflow analysis. --------- Co-authored-by: Burak Emir <bqe@google.com> Co-authored-by: jonmeow <jperkins@google.com>
This commit is contained in:
co-authored by
Burak Emir
jonmeow
parent
bea24a8bee
commit
fdb188ccfd
@@ -2,6 +2,9 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/facet_type.h"
|
||||
@@ -13,6 +16,7 @@
|
||||
#include "toolchain/check/return.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/check/unused.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
@@ -102,7 +106,8 @@ static auto IsValidParamForIntroducer(Context& context, Parse::NodeId node_id,
|
||||
|
||||
// TODO: make this function shorter by factoring pieces out.
|
||||
static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
Parse::NodeKind node_kind) -> bool {
|
||||
Parse::NodeKind node_kind,
|
||||
bool is_unused = false) -> bool {
|
||||
// TODO: split this into smaller, more focused functions.
|
||||
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto [cast_type_inst_id, cast_type_id] =
|
||||
@@ -134,9 +139,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
auto make_binding_pattern = [&]() -> SemIR::InstId {
|
||||
// TODO: Eventually the name will need to support associations with other
|
||||
// scopes, but right now we don't support qualified names here.
|
||||
auto binding =
|
||||
AddBindingPattern(context, name_node, name_id, cast_type_id,
|
||||
type_expr_region_id, pattern_inst_kind, is_template);
|
||||
auto binding = AddBindingPattern(context, name_node, name_id, cast_type_id,
|
||||
type_expr_region_id, pattern_inst_kind,
|
||||
is_template, is_unused);
|
||||
|
||||
// TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst
|
||||
// the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`)
|
||||
@@ -279,7 +284,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
.bind_name_id;
|
||||
RegisterReturnedVar(context,
|
||||
introducer.modifier_node_id(ModifierOrder::Decl),
|
||||
type_node, cast_type_id, bind_id);
|
||||
type_node, cast_type_id, bind_id, name_id);
|
||||
}
|
||||
}
|
||||
context.node_stack().Push(node_id, binding_pattern_id);
|
||||
@@ -328,7 +333,7 @@ auto HandleParseNode(Context& context,
|
||||
Parse::CompileTimeBindingPatternId node_id) -> bool {
|
||||
// Pop the `.Self` facet value name introduced by the
|
||||
// CompileTimeBindingPatternStart.
|
||||
context.scope_stack().Pop();
|
||||
context.scope_stack().Pop(/*check_unused=*/true);
|
||||
|
||||
auto node_kind = Parse::NodeKind::CompileTimeBindingPattern;
|
||||
const DeclIntroducerState& introducer =
|
||||
@@ -449,8 +454,68 @@ auto HandleParseNode(Context& context, Parse::TemplateBindingNameId node_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Within a pattern with an unused modifier, sets the is_unused on all
|
||||
// entity names and also returns whether any names were found. The result
|
||||
// is needed to emit a diagnostic when the unused modifier is
|
||||
// unnecessary.
|
||||
static auto MarkPatternUnused(Context& context, SemIR::InstId inst_id) -> bool {
|
||||
bool found_name = false;
|
||||
llvm::SmallVector<SemIR::InstId> worklist;
|
||||
worklist.push_back(inst_id);
|
||||
while (!worklist.empty()) {
|
||||
auto current_inst_id = worklist.pop_back_val();
|
||||
auto inst = context.insts().Get(current_inst_id);
|
||||
CARBON_KIND_SWITCH(inst) {
|
||||
case SemIR::OutParamPattern::Kind:
|
||||
case SemIR::RefParamPattern::Kind:
|
||||
case SemIR::ValueParamPattern::Kind:
|
||||
case SemIR::VarParamPattern::Kind: {
|
||||
auto param = inst.As<SemIR::AnyParamPattern>();
|
||||
worklist.push_back(param.subpattern_id);
|
||||
break;
|
||||
}
|
||||
case SemIR::RefBindingPattern::Kind:
|
||||
case SemIR::SymbolicBindingPattern::Kind:
|
||||
case SemIR::ValueBindingPattern::Kind: {
|
||||
auto bind = inst.As<SemIR::AnyBindingPattern>();
|
||||
auto& name = context.entity_names().Get(bind.entity_name_id);
|
||||
name.is_unused = true;
|
||||
// We treat `_` as not marking the pattern as unused for the purpose of
|
||||
// deciding whether to issue a warning for `unused` on a pattern that
|
||||
// doesn't contain any bindings. `_` is implicitly unused, so marking it
|
||||
// `unused` is redundant but harmless.
|
||||
if (name.name_id != SemIR::NameId::Underscore) {
|
||||
found_name = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::TuplePattern tuple): {
|
||||
for (auto elem_id : context.inst_blocks().Get(tuple.elements_id)) {
|
||||
worklist.push_back(elem_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::VarPattern var): {
|
||||
worklist.push_back(var.subpattern_id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found_name;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::UnusedPatternId node_id) -> bool {
|
||||
return context.TODO(node_id, "unused");
|
||||
auto [child_node, child_inst_id] =
|
||||
context.node_stack().PopPatternWithNodeId();
|
||||
if (!MarkPatternUnused(context, child_inst_id)) {
|
||||
CARBON_DIAGNOSTIC(UnusedPatternNoBindings, Warning,
|
||||
"`unused` modifier on pattern without bindings");
|
||||
context.emitter().Emit(node_id, UnusedPatternNoBindings);
|
||||
}
|
||||
context.node_stack().Push(node_id, child_inst_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
Reference in New Issue
Block a user