Don't assume extern fns parameters are patterns

Unlike normal fns, they should be bare identifiers.
This commit is contained in:
Chayim Refael Friedman 2026-02-12 23:17:55 +02:00
parent 2a16118416
commit b6388d5ea7
2 changed files with 52 additions and 3 deletions

View File

@ -32,8 +32,8 @@ use triomphe::Arc;
use tt::TextRange;
use crate::{
AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId,
MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
attrs::AttrFlags,
db::DefDatabase,
expr_store::{
@ -141,9 +141,19 @@ pub(super) fn lower_body(
source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
let is_extern = matches!(
owner,
DefWithBodyId::FunctionId(id)
if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)),
);
for param in param_list.params() {
if collector.check_cfg(&param) {
let param_pat = collector.collect_pat_top(param.pat());
let param_pat = if is_extern {
collector.collect_extern_fn_param(param.pat())
} else {
collector.collect_pat_top(param.pat())
};
params.push(param_pat);
}
}
@ -2248,6 +2258,32 @@ impl<'db> ExprCollector<'db> {
}
}
fn collect_extern_fn_param(&mut self, pat: Option<ast::Pat>) -> PatId {
// `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers
// in their parameters are always interpreted as bindings, even if in a normal function they
// won't be, because they would refer to a path pattern.
let Some(pat) = pat else { return self.missing_pat() };
match &pat {
ast::Pat::IdentPat(bp) => {
// FIXME: Emit an error if `!bp.is_simple_ident()`.
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let hygiene = bp
.name()
.map(|name| self.hygiene_id_for(name.syntax().text_range()))
.unwrap_or(HygieneId::ROOT);
let binding = self.alloc_binding(name, BindingAnnotation::Unannotated, hygiene);
let pat =
self.alloc_pat(Pat::Bind { id: binding, subpat: None }, AstPtr::new(&pat));
self.add_definition_to_binding(binding, pat);
pat
}
// FIXME: Emit an error.
_ => self.missing_pat(),
}
}
// region: patterns
fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {

View File

@ -2757,3 +2757,16 @@ where
"#]],
);
}
#[test]
fn extern_fns_cannot_have_param_patterns() {
check_no_mismatches(
r#"
pub(crate) struct Builder<'a>(&'a ());
unsafe extern "C" {
pub(crate) fn foo<'a>(Builder: &Builder<'a>);
}
"#,
);
}