find the correct lang item for ranges
This commit is contained in:
parent
5dd44d4d4c
commit
e747201ad8
5 changed files with 109 additions and 69 deletions
|
@ -133,8 +133,7 @@ hir_analysis_extern_crate_not_idiomatic =
|
||||||
.suggestion = convert it to a `{$msg_code}`
|
.suggestion = convert it to a `{$msg_code}`
|
||||||
|
|
||||||
hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
|
hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
|
||||||
hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
|
|
||||||
|
|
||||||
hir_analysis_missing_parentheses_in_range = `{$ty_str}` is not an iterator
|
hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
|
||||||
|
|
||||||
hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function
|
hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::{is_range_literal, ExprKind, Node, QPath};
|
use rustc_hir::{ExprKind, Node, QPath};
|
||||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_middle::traits::util::supertraits;
|
use rustc_middle::traits::util::supertraits;
|
||||||
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
|
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
|
||||||
|
@ -1214,17 +1214,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
ty_str: &str,
|
ty_str: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let SelfSource::MethodCall(expr) = source {
|
if let SelfSource::MethodCall(expr) = source {
|
||||||
let mut search_limit = 5;
|
for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
|
||||||
for (_, parent) in tcx.hir().parent_iter(expr.hir_id) {
|
if let Node::Expr(parent_expr) = parent {
|
||||||
search_limit -= 1;
|
let lang_item = match parent_expr.kind {
|
||||||
if search_limit == 0 {
|
ExprKind::Struct(ref qpath, _, _) => match **qpath {
|
||||||
break;
|
QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
|
||||||
|
QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
|
||||||
|
QPath::LangItem(LangItem::RangeToInclusive, ..) => {
|
||||||
|
Some(LangItem::RangeToInclusive)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
ExprKind::Call(ref func, _) => match func.kind {
|
||||||
|
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
|
||||||
|
ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => {
|
||||||
|
Some(LangItem::RangeInclusiveStruct)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if lang_item.is_none() {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Node::Expr(parent_expr) = parent && is_range_literal(parent_expr) {
|
|
||||||
let span_included = match parent_expr.kind {
|
let span_included = match parent_expr.kind {
|
||||||
hir::ExprKind::Struct(_, eps, _) =>
|
hir::ExprKind::Struct(_, eps, _) => {
|
||||||
eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)),
|
eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span))
|
||||||
|
}
|
||||||
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
|
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
|
||||||
hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
|
hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -1234,30 +1252,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let range_def_id = self.tcx.lang_items().range_struct().unwrap();
|
debug!("lang_item: {:?}", lang_item);
|
||||||
let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
|
let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None);
|
||||||
|
let range_ty =
|
||||||
// avoid suggesting when the method name is not implemented for a `range`
|
self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
|
||||||
let pick = self.lookup_probe(
|
|
||||||
span,
|
|
||||||
item_name,
|
|
||||||
range_ty,
|
|
||||||
expr,
|
|
||||||
ProbeScope::AllTraits
|
|
||||||
);
|
|
||||||
|
|
||||||
|
let pick =
|
||||||
|
self.lookup_probe(span, item_name, range_ty, expr, ProbeScope::AllTraits);
|
||||||
if pick.is_ok() {
|
if pick.is_ok() {
|
||||||
let range_span = parent_expr.span.with_hi(expr.span.hi());
|
let range_span = parent_expr.span.with_hi(expr.span.hi());
|
||||||
tcx.sess.emit_err(errors::MissingParentheseInRange {
|
tcx.sess.emit_err(errors::MissingParentheseInRange {
|
||||||
span: span,
|
span,
|
||||||
ty_str: ty_str.to_string(),
|
ty_str: ty_str.to_string(),
|
||||||
add_missing_parentheses: Some(
|
method_name: item_name.as_str().to_string(),
|
||||||
errors::AddMissingParenthesesInRange {
|
add_missing_parentheses: Some(errors::AddMissingParenthesesInRange {
|
||||||
func_name: item_name.name.as_str().to_string(),
|
func_name: item_name.name.as_str().to_string(),
|
||||||
left: range_span.shrink_to_lo(),
|
left: range_span.shrink_to_lo(),
|
||||||
right: range_span.shrink_to_hi(),
|
right: range_span.shrink_to_hi(),
|
||||||
}
|
}),
|
||||||
)
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,12 +348,13 @@ pub struct ExpectedUsedSymbol {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")]
|
#[diag(hir_analysis::missing_parentheses_in_range, code = "E0689")]
|
||||||
pub struct MissingParentheseInRange {
|
pub struct MissingParentheseInRange {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
#[label(hir_analysis::missing_parentheses_in_range)]
|
#[label(hir_analysis::missing_parentheses_in_range)]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub ty_str: String,
|
pub ty_str: String,
|
||||||
|
pub method_name: String,
|
||||||
|
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
|
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let arr = &[0, 1, 2, 3];
|
let arr = &[0, 1, 2, 3];
|
||||||
for _i in 0..arr.len().rev() {
|
for _i in 0..arr.len().rev() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| surround the range in parentheses
|
//~| surround the range in parentheses
|
||||||
// The above error used to say “the method `rev` exists for type `usize`”.
|
// The above error used to say “the method `rev` exists for type `usize`”.
|
||||||
// This regression test ensures it doesn't say that any more.
|
// This regression test ensures it doesn't say that any more.
|
||||||
|
@ -10,40 +10,40 @@ fn main() {
|
||||||
|
|
||||||
// Test for #102396
|
// Test for #102396
|
||||||
for i in 1..11.rev() {
|
for i in 1..11.rev() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
let end: usize = 10;
|
let end: usize = 10;
|
||||||
for i in 1..end.rev() {
|
for i in 1..end.rev() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 1..(end + 1).rev() {
|
for i in 1..(end + 1).rev() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
if 1..(end + 1).is_empty() {
|
if 1..(end + 1).is_empty() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| ERROR mismatched types [E0308]
|
//~| ERROR mismatched types [E0308]
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
if 1..(end + 1).is_sorted() {
|
if 1..(end + 1).is_sorted() {
|
||||||
//~^ ERROR mismatched types [E0308]
|
//~^ ERROR mismatched types [E0308]
|
||||||
//~| ERROR `usize` is not an iterator [E0599]
|
//~| ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
let _res: i32 = 3..6.take(2).sum();
|
let _res: i32 = 3..6.take(2).sum();
|
||||||
//~^ ERROR `{integer}` is not an iterator [E0599]
|
//~^ ERROR can't call method
|
||||||
//~| ERROR mismatched types [E0308]
|
//~| ERROR mismatched types [E0308]
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
|
|
||||||
let _sum: i32 = 3..6.sum();
|
let _sum: i32 = 3..6.sum();
|
||||||
//~^ ERROR `{integer}` is not an iterator [E0599]
|
//~^ ERROR can't call method
|
||||||
//~| ERROR mismatched types [E0308]
|
//~| ERROR mismatched types [E0308]
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
|
|
||||||
|
@ -51,12 +51,12 @@ fn main() {
|
||||||
let b = 10 as usize;
|
let b = 10 as usize;
|
||||||
|
|
||||||
for _a in a..=b.rev() {
|
for _a in a..=b.rev() {
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
let _res = ..10.contains(3);
|
let _res = ..10.contains(3);
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR can't call method
|
||||||
//~| HELP surround the range in parentheses
|
//~| HELP surround the range in parentheses
|
||||||
|
|
||||||
if 1..end.error_method() {
|
if 1..end.error_method() {
|
||||||
|
@ -66,5 +66,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _res = b.take(1)..a;
|
let _res = b.take(1)..a;
|
||||||
//~^ ERROR not an iterator
|
//~^ ERROR `usize` is not an iterator
|
||||||
|
|
||||||
|
let _res: i32 = ..6.take(2).sum();
|
||||||
|
//~^ can't call method `take` on ambiguous numeric type
|
||||||
|
//~| ERROR mismatched types [E0308]
|
||||||
|
//~| HELP you must specify a concrete type for this numeric value
|
||||||
|
// Won't suggest because `RangeTo` dest not implemented `take`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `rev` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:4:28
|
--> $DIR/issue-90315.rs:4:28
|
||||||
|
|
|
|
||||||
LL | for _i in 0..arr.len().rev() {
|
LL | for _i in 0..arr.len().rev() {
|
||||||
| ^^^ `usize` is not an iterator
|
| ^^^ can't call method `rev` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `rev` function
|
help: you must surround the range in parentheses to call the `rev` function
|
||||||
|
|
|
|
||||||
LL | for _i in (0..arr.len()).rev() {
|
LL | for _i in (0..arr.len()).rev() {
|
||||||
| + +
|
| + +
|
||||||
|
|
||||||
error[E0599]: `{integer}` is not an iterator
|
error[E0689]: can't call method `rev` on type `{integer}`
|
||||||
--> $DIR/issue-90315.rs:12:20
|
--> $DIR/issue-90315.rs:12:20
|
||||||
|
|
|
|
||||||
LL | for i in 1..11.rev() {
|
LL | for i in 1..11.rev() {
|
||||||
| ^^^ `{integer}` is not an iterator
|
| ^^^ can't call method `rev` on type `{integer}`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `rev` function
|
help: you must surround the range in parentheses to call the `rev` function
|
||||||
|
|
|
|
||||||
LL | for i in (1..11).rev() {
|
LL | for i in (1..11).rev() {
|
||||||
| + +
|
| + +
|
||||||
|
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `rev` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:18:21
|
--> $DIR/issue-90315.rs:18:21
|
||||||
|
|
|
|
||||||
LL | for i in 1..end.rev() {
|
LL | for i in 1..end.rev() {
|
||||||
| ^^^ `usize` is not an iterator
|
| ^^^ can't call method `rev` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `rev` function
|
help: you must surround the range in parentheses to call the `rev` function
|
||||||
|
|
|
|
||||||
LL | for i in (1..end).rev() {
|
LL | for i in (1..end).rev() {
|
||||||
| + +
|
| + +
|
||||||
|
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `rev` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:23:27
|
--> $DIR/issue-90315.rs:23:27
|
||||||
|
|
|
|
||||||
LL | for i in 1..(end + 1).rev() {
|
LL | for i in 1..(end + 1).rev() {
|
||||||
| ^^^ `usize` is not an iterator
|
| ^^^ can't call method `rev` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `rev` function
|
help: you must surround the range in parentheses to call the `rev` function
|
||||||
|
|
|
|
||||||
LL | for i in (1..(end + 1)).rev() {
|
LL | for i in (1..(end + 1)).rev() {
|
||||||
| + +
|
| + +
|
||||||
|
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `is_empty` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:28:21
|
--> $DIR/issue-90315.rs:28:21
|
||||||
|
|
|
|
||||||
LL | if 1..(end + 1).is_empty() {
|
LL | if 1..(end + 1).is_empty() {
|
||||||
| ^^^^^^^^ `usize` is not an iterator
|
| ^^^^^^^^ can't call method `is_empty` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `is_empty` function
|
help: you must surround the range in parentheses to call the `is_empty` function
|
||||||
|
|
|
|
||||||
|
@ -62,11 +62,11 @@ LL | if 1..(end + 1).is_empty() {
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found struct `std::ops::Range<{integer}>`
|
found struct `std::ops::Range<{integer}>`
|
||||||
|
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `is_sorted` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:34:21
|
--> $DIR/issue-90315.rs:34:21
|
||||||
|
|
|
|
||||||
LL | if 1..(end + 1).is_sorted() {
|
LL | if 1..(end + 1).is_sorted() {
|
||||||
| ^^^^^^^^^ `usize` is not an iterator
|
| ^^^^^^^^^ can't call method `is_sorted` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `is_sorted` function
|
help: you must surround the range in parentheses to call the `is_sorted` function
|
||||||
|
|
|
|
||||||
|
@ -82,11 +82,11 @@ LL | if 1..(end + 1).is_sorted() {
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found struct `std::ops::Range<{integer}>`
|
found struct `std::ops::Range<{integer}>`
|
||||||
|
|
||||||
error[E0599]: `{integer}` is not an iterator
|
error[E0689]: can't call method `take` on type `{integer}`
|
||||||
--> $DIR/issue-90315.rs:40:26
|
--> $DIR/issue-90315.rs:40:26
|
||||||
|
|
|
|
||||||
LL | let _res: i32 = 3..6.take(2).sum();
|
LL | let _res: i32 = 3..6.take(2).sum();
|
||||||
| ^^^^ `{integer}` is not an iterator
|
| ^^^^ can't call method `take` on type `{integer}`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `take` function
|
help: you must surround the range in parentheses to call the `take` function
|
||||||
|
|
|
|
||||||
|
@ -104,11 +104,11 @@ LL | let _res: i32 = 3..6.take(2).sum();
|
||||||
= note: expected type `i32`
|
= note: expected type `i32`
|
||||||
found struct `std::ops::Range<{integer}>`
|
found struct `std::ops::Range<{integer}>`
|
||||||
|
|
||||||
error[E0599]: `{integer}` is not an iterator
|
error[E0689]: can't call method `sum` on type `{integer}`
|
||||||
--> $DIR/issue-90315.rs:45:26
|
--> $DIR/issue-90315.rs:45:26
|
||||||
|
|
|
|
||||||
LL | let _sum: i32 = 3..6.sum();
|
LL | let _sum: i32 = 3..6.sum();
|
||||||
| ^^^ `{integer}` is not an iterator
|
| ^^^ can't call method `sum` on type `{integer}`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `sum` function
|
help: you must surround the range in parentheses to call the `sum` function
|
||||||
|
|
|
|
||||||
|
@ -126,22 +126,22 @@ LL | let _sum: i32 = 3..6.sum();
|
||||||
= note: expected type `i32`
|
= note: expected type `i32`
|
||||||
found struct `std::ops::Range<{integer}>`
|
found struct `std::ops::Range<{integer}>`
|
||||||
|
|
||||||
error[E0599]: `usize` is not an iterator
|
error[E0689]: can't call method `rev` on type `usize`
|
||||||
--> $DIR/issue-90315.rs:53:21
|
--> $DIR/issue-90315.rs:53:21
|
||||||
|
|
|
|
||||||
LL | for _a in a..=b.rev() {
|
LL | for _a in a..=b.rev() {
|
||||||
| ^^^ `usize` is not an iterator
|
| ^^^ can't call method `rev` on type `usize`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `rev` function
|
help: you must surround the range in parentheses to call the `rev` function
|
||||||
|
|
|
|
||||||
LL | for _a in (a..=b).rev() {
|
LL | for _a in (a..=b).rev() {
|
||||||
| + +
|
| + +
|
||||||
|
|
||||||
error[E0599]: `{integer}` is not an iterator
|
error[E0689]: can't call method `contains` on type `{integer}`
|
||||||
--> $DIR/issue-90315.rs:58:21
|
--> $DIR/issue-90315.rs:58:21
|
||||||
|
|
|
|
||||||
LL | let _res = ..10.contains(3);
|
LL | let _res = ..10.contains(3);
|
||||||
| ^^^^^^^^ `{integer}` is not an iterator
|
| ^^^^^^^^ can't call method `contains` on type `{integer}`
|
||||||
|
|
|
|
||||||
help: you must surround the range in parentheses to call the `contains` function
|
help: you must surround the range in parentheses to call the `contains` function
|
||||||
|
|
|
|
||||||
|
@ -173,7 +173,29 @@ LL | let _res = b.take(1)..a;
|
||||||
`usize: Iterator`
|
`usize: Iterator`
|
||||||
which is required by `&mut usize: Iterator`
|
which is required by `&mut usize: Iterator`
|
||||||
|
|
||||||
error: aborting due to 17 previous errors
|
error[E0689]: can't call method `take` on ambiguous numeric type `{integer}`
|
||||||
|
--> $DIR/issue-90315.rs:71:25
|
||||||
|
|
|
||||||
|
LL | let _res: i32 = ..6.take(2).sum();
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
help: you must specify a concrete type for this numeric value, like `i32`
|
||||||
|
|
|
||||||
|
LL | let _res: i32 = ..6_i32.take(2).sum();
|
||||||
|
| ~~~~~
|
||||||
|
|
||||||
Some errors have detailed explanations: E0308, E0599.
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-90315.rs:71:21
|
||||||
|
|
|
||||||
|
LL | let _res: i32 = ..6.take(2).sum();
|
||||||
|
| --- ^^^^^^^^^^^^^^^^^ expected `i32`, found struct `RangeTo`
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found struct `RangeTo<_>`
|
||||||
|
|
||||||
|
error: aborting due to 19 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0308, E0599, E0689.
|
||||||
For more information about an error, try `rustc --explain E0308`.
|
For more information about an error, try `rustc --explain E0308`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue