Skip to content

Commit 870a7de

Browse files
committed
Custom error for const expression parsed as trait object
1 parent 2d57b1a commit 870a7de

File tree

5 files changed

+123
-55
lines changed

5 files changed

+123
-55
lines changed

src/librustc_ast_lowering/lib.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use rustc_ast_pretty::pprust;
4949
use rustc_data_structures::captures::Captures;
5050
use rustc_data_structures::fx::FxHashSet;
5151
use rustc_data_structures::sync::Lrc;
52-
use rustc_errors::struct_span_err;
52+
use rustc_errors::{struct_span_err, Applicability};
5353
use rustc_hir as hir;
5454
use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
5555
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
@@ -1136,6 +1136,71 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
11361136
}
11371137
}
11381138
}
1139+
// Possible `a + b` expression that should be surrounded in braces but was parsed
1140+
// as trait bounds in a trait object. Suggest surrounding with braces.
1141+
if let TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) = ty.kind {
1142+
// We cannot disambiguate multi-segment paths right now as that requires type
1143+
// checking.
1144+
let const_expr_without_braces = bounds.iter().all(|bound| match bound {
1145+
GenericBound::Trait(
1146+
PolyTraitRef {
1147+
bound_generic_params,
1148+
trait_ref: TraitRef { path, .. },
1149+
..
1150+
},
1151+
TraitBoundModifier::None,
1152+
) if bound_generic_params.is_empty()
1153+
&& path.segments.len() == 1
1154+
&& path.segments[0].args.is_none() =>
1155+
{
1156+
let part_res = self.resolver.get_partial_res(path.segments[0].id);
1157+
match part_res.map(|r| r.base_res()) {
1158+
Some(res) => {
1159+
!res.matches_ns(Namespace::TypeNS)
1160+
&& res.matches_ns(Namespace::ValueNS)
1161+
}
1162+
None => true,
1163+
}
1164+
}
1165+
_ => false,
1166+
});
1167+
if const_expr_without_braces {
1168+
self.sess.struct_span_err(ty.span, "likely `const` expression parsed as trait bounds")
1169+
.span_label(ty.span, "parsed as trait bounds but traits weren't found")
1170+
.multipart_suggestion(
1171+
"if you meant to write a `const` expression, surround the expression with braces",
1172+
vec![
1173+
(ty.span.shrink_to_lo(), "{ ".to_string()),
1174+
(ty.span.shrink_to_hi(), " }".to_string()),
1175+
],
1176+
Applicability::MachineApplicable,
1177+
)
1178+
.emit();
1179+
1180+
let parent_def_id = self.current_hir_id_owner.last().unwrap().0;
1181+
let node_id = self.resolver.next_node_id();
1182+
// Add a definition for the in-band const def.
1183+
self.resolver.definitions().create_def_with_parent(
1184+
parent_def_id,
1185+
node_id,
1186+
DefPathData::AnonConst,
1187+
ExpnId::root(),
1188+
ty.span,
1189+
);
1190+
1191+
let path_expr = Expr {
1192+
id: ty.id,
1193+
kind: ExprKind::Err,
1194+
span: ty.span,
1195+
attrs: AttrVec::new(),
1196+
};
1197+
let value = self.with_new_scopes(|this| hir::AnonConst {
1198+
hir_id: this.lower_node_id(node_id),
1199+
body: this.lower_const_body(path_expr.span, Some(&path_expr)),
1200+
});
1201+
return GenericArg::Const(ConstArg { value, span: ty.span });
1202+
}
1203+
}
11391204
GenericArg::Type(self.lower_ty_direct(&ty, itctx))
11401205
}
11411206
ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg {

src/librustc_middle/ty/sty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,7 @@ impl<'tcx> Const<'tcx> {
22752275
let name = tcx.hir().name(hir_id);
22762276
ty::ConstKind::Param(ty::ParamConst::new(index, name))
22772277
}
2278+
ExprKind::Err => ty::ConstKind::Error,
22782279
_ => ty::ConstKind::Unevaluated(
22792280
def_id.to_def_id(),
22802281
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),

src/librustc_resolve/late.rs

+48-19
Original file line numberDiff line numberDiff line change
@@ -558,25 +558,22 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
558558
let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true);
559559
match arg {
560560
GenericArg::Type(ref ty) => {
561-
// We parse const arguments as path types as we cannot distinguish them during
562-
// parsing. We try to resolve that ambiguity by attempting resolution the type
563-
// namespace first, and if that fails we try again in the value namespace. If
564-
// resolution in the value namespace succeeds, we have an generic const argument on
565-
// our hands.
566-
if let TyKind::Path(ref qself, ref path) = ty.kind {
567-
// We cannot disambiguate multi-segment paths right now as that requires type
568-
// checking.
569-
if path.segments.len() == 1 && path.segments[0].args.is_none() {
570-
let mut check_ns = |ns| {
571-
self.resolve_ident_in_lexical_scope(
572-
path.segments[0].ident,
573-
ns,
574-
None,
575-
path.span,
576-
)
577-
.is_some()
578-
};
579-
if !check_ns(TypeNS) && check_ns(ValueNS) {
561+
let mut check_ns = |path: &Path, ns| {
562+
self.resolve_ident_in_lexical_scope(path.segments[0].ident, ns, None, path.span)
563+
.is_some()
564+
&& path.segments.len() == 1
565+
&& path.segments[0].args.is_none()
566+
};
567+
match ty.kind {
568+
// We parse const arguments as path types as we cannot distinguish them during
569+
// parsing. We try to resolve that ambiguity by attempting resolution the type
570+
// namespace first, and if that fails we try again in the value namespace. If
571+
// resolution in the value namespace succeeds, we have an generic const argument
572+
// on our hands.
573+
TyKind::Path(ref qself, ref path) => {
574+
// We cannot disambiguate multi-segment paths right now as that requires type
575+
// checking.
576+
if !check_ns(path, TypeNS) && check_ns(path, ValueNS) {
580577
// This must be equivalent to `visit_anon_const`, but we cannot call it
581578
// directly due to visitor lifetimes so we have to copy-paste some code.
582579
self.with_constant_rib(|this| {
@@ -597,6 +594,38 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
597594
return;
598595
}
599596
}
597+
598+
// Possible `a + b` expression that should be surrounded in braces but was
599+
// parsed as trait bounds in a trait object. Suggest surrounding with braces.
600+
TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) => {
601+
// We cannot disambiguate multi-segment paths right now as that requires
602+
// type checking.
603+
let const_expr_without_braces = bounds.iter().all(|bound| match bound {
604+
GenericBound::Trait(
605+
PolyTraitRef {
606+
bound_generic_params,
607+
trait_ref: TraitRef { path, .. },
608+
..
609+
},
610+
TraitBoundModifier::None,
611+
) if bound_generic_params.is_empty() => {
612+
!check_ns(path, TypeNS) && check_ns(path, ValueNS)
613+
}
614+
_ => false,
615+
});
616+
if const_expr_without_braces {
617+
// This will be handled and emit an appropriate error in
618+
// `rustc_ast_lowering::LoweringContext::lower_generic_arg`. We do not
619+
// `visit_ty` in this case to avoid extra unnecessary output.
620+
self.r.session.delay_span_bug(
621+
ty.span,
622+
"`const` expression parsed as trait bounds",
623+
);
624+
self.diagnostic_metadata.currently_processing_generics = prev;
625+
return;
626+
}
627+
}
628+
_ => {}
600629
}
601630

602631
self.visit_ty(ty);

src/test/ui/const-generics/const-expression-missing-braces.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ fn a() {
1111
fn b() {
1212
let bar = 3;
1313
foo::<bar + bar>();
14-
//~^ ERROR expected trait, found local variable `bar`
15-
//~| ERROR expected trait, found local variable `bar`
16-
//~| ERROR wrong number of const arguments: expected 1, found 0
17-
//~| ERROR wrong number of type arguments: expected 0, found 1
18-
//~| WARNING trait objects without an explicit `dyn` are deprecated
14+
//~^ ERROR likely `const` expression parsed as trait bounds
1915
}
2016
fn c() {
2117
let bar = 3;

src/test/ui/const-generics/const-expression-missing-braces.stderr

+7-30
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,21 @@ LL | foo::<bar + 3>();
55
| ^ expected one of 8 possible tokens
66

77
error: expected one of `,` or `>`, found `+`
8-
--> $DIR/const-expression-missing-braces.rs:22:13
8+
--> $DIR/const-expression-missing-braces.rs:18:13
99
|
1010
LL | foo::<3 + 3>();
1111
| ^ expected one of `,` or `>`
1212

13-
error[E0404]: expected trait, found local variable `bar`
13+
error: likely `const` expression parsed as trait bounds
1414
--> $DIR/const-expression-missing-braces.rs:13:11
1515
|
1616
LL | foo::<bar + bar>();
17-
| ^^^ not a trait
18-
19-
error[E0404]: expected trait, found local variable `bar`
20-
--> $DIR/const-expression-missing-braces.rs:13:17
17+
| ^^^^^^^^^ parsed as trait bounds but traits weren't found
2118
|
22-
LL | foo::<bar + bar>();
23-
| ^^^ not a trait
24-
25-
warning: trait objects without an explicit `dyn` are deprecated
26-
--> $DIR/const-expression-missing-braces.rs:13:11
19+
help: if you meant to write a `const` expression, surround the expression with braces
2720
|
28-
LL | foo::<bar + bar>();
29-
| ^^^^^^^^^ help: use `dyn`: `dyn bar + bar`
30-
|
31-
= note: `#[warn(bare_trait_objects)]` on by default
32-
33-
error[E0107]: wrong number of const arguments: expected 1, found 0
34-
--> $DIR/const-expression-missing-braces.rs:13:5
35-
|
36-
LL | foo::<bar + bar>();
37-
| ^^^^^^^^^^^^^^^^ expected 1 const argument
38-
39-
error[E0107]: wrong number of type arguments: expected 0, found 1
40-
--> $DIR/const-expression-missing-braces.rs:13:11
41-
|
42-
LL | foo::<bar + bar>();
43-
| ^^^^^^^^^ unexpected type argument
21+
LL | foo::<{ bar + bar }>();
22+
| ^ ^
4423

45-
error: aborting due to 6 previous errors; 1 warning emitted
24+
error: aborting due to 3 previous errors
4625

47-
Some errors have detailed explanations: E0107, E0404.
48-
For more information about an error, try `rustc --explain E0107`.

0 commit comments

Comments
 (0)