mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
Fix many of the clippy::pedantic warnings
This commit is contained in:
parent
f477b45a0a
commit
13e1eb2b87
11 changed files with 86 additions and 90 deletions
|
@ -42,7 +42,7 @@ impl DenoDir {
|
|||
pub fn new(
|
||||
reload: bool,
|
||||
custom_root: Option<&Path>,
|
||||
) -> std::io::Result<DenoDir> {
|
||||
) -> std::io::Result<Self> {
|
||||
// Only setup once.
|
||||
let home_dir = dirs::home_dir().expect("Could not get home directory.");
|
||||
let default = home_dir.join(".deno");
|
||||
|
@ -56,7 +56,7 @@ impl DenoDir {
|
|||
let deps_http = deps.join("http");
|
||||
let deps_https = deps.join("https");
|
||||
|
||||
let deno_dir = DenoDir {
|
||||
let deno_dir = Self {
|
||||
root,
|
||||
gen,
|
||||
deps,
|
||||
|
@ -80,7 +80,7 @@ impl DenoDir {
|
|||
|
||||
// https://github.com/denoland/deno/blob/golang/deno_dir.go#L32-L35
|
||||
pub fn cache_path(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
filename: &str,
|
||||
source_code: &str,
|
||||
) -> (PathBuf, PathBuf) {
|
||||
|
@ -92,7 +92,7 @@ impl DenoDir {
|
|||
}
|
||||
|
||||
fn load_cache(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
filename: &str,
|
||||
source_code: &str,
|
||||
) -> Result<(String, String), std::io::Error> {
|
||||
|
@ -108,7 +108,7 @@ impl DenoDir {
|
|||
}
|
||||
|
||||
pub fn code_cache(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
filename: &str,
|
||||
source_code: &str,
|
||||
output_code: &str,
|
||||
|
@ -130,7 +130,7 @@ impl DenoDir {
|
|||
|
||||
// Prototype https://github.com/denoland/deno/blob/golang/deno_dir.go#L37-L73
|
||||
fn fetch_remote_source(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
module_name: &str,
|
||||
filename: &str,
|
||||
) -> DenoResult<(String, msg::MediaType)> {
|
||||
|
@ -161,7 +161,7 @@ impl DenoDir {
|
|||
|
||||
// Prototype: https://github.com/denoland/deno/blob/golang/os.go#L122-L138
|
||||
fn get_source_code(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
module_name: &str,
|
||||
filename: &str,
|
||||
) -> DenoResult<CodeFetchOutput> {
|
||||
|
@ -205,7 +205,7 @@ impl DenoDir {
|
|||
}
|
||||
|
||||
pub fn code_fetch(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
module_specifier: &str,
|
||||
containing_file: &str,
|
||||
) -> Result<CodeFetchOutput, DenoError> {
|
||||
|
@ -258,7 +258,7 @@ impl DenoDir {
|
|||
}
|
||||
|
||||
// Prototype: https://github.com/denoland/deno/blob/golang/os.go#L56-L68
|
||||
fn src_file_to_url(self: &DenoDir, filename: &str) -> String {
|
||||
fn src_file_to_url(self: &Self, filename: &str) -> String {
|
||||
let filename_path = Path::new(filename);
|
||||
if filename_path.starts_with(&self.deps) {
|
||||
let (rest, prefix) = if filename_path.starts_with(&self.deps_https) {
|
||||
|
@ -287,7 +287,7 @@ impl DenoDir {
|
|||
// Prototype: https://github.com/denoland/deno/blob/golang/os.go#L70-L98
|
||||
// Returns (module name, local filename)
|
||||
fn resolve_module(
|
||||
self: &DenoDir,
|
||||
self: &Self,
|
||||
module_specifier: &str,
|
||||
containing_file: &str,
|
||||
) -> Result<(String, String), url::ParseError> {
|
||||
|
|
|
@ -124,8 +124,8 @@ impl std::error::Error for DenoError {
|
|||
|
||||
impl From<io::Error> for DenoError {
|
||||
#[inline]
|
||||
fn from(err: io::Error) -> DenoError {
|
||||
DenoError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Self {
|
||||
repr: Repr::IoErr(err),
|
||||
}
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ impl From<io::Error> for DenoError {
|
|||
|
||||
impl From<url::ParseError> for DenoError {
|
||||
#[inline]
|
||||
fn from(err: url::ParseError) -> DenoError {
|
||||
DenoError {
|
||||
fn from(err: url::ParseError) -> Self {
|
||||
Self {
|
||||
repr: Repr::UrlErr(err),
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +142,8 @@ impl From<url::ParseError> for DenoError {
|
|||
|
||||
impl From<hyper::Error> for DenoError {
|
||||
#[inline]
|
||||
fn from(err: hyper::Error) -> DenoError {
|
||||
DenoError {
|
||||
fn from(err: hyper::Error) -> Self {
|
||||
Self {
|
||||
repr: Repr::HyperErr(err),
|
||||
}
|
||||
}
|
||||
|
|
53
src/flags.rs
53
src/flags.rs
|
@ -13,6 +13,7 @@ macro_rules! svec {
|
|||
($($x:expr),*) => (vec![$($x.to_string()),*]);
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct DenoFlags {
|
||||
pub help: bool,
|
||||
|
@ -23,7 +24,7 @@ pub struct DenoFlags {
|
|||
pub allow_write: bool,
|
||||
pub allow_net: bool,
|
||||
pub allow_env: bool,
|
||||
pub types_flag: bool,
|
||||
pub types: bool,
|
||||
}
|
||||
|
||||
pub fn get_usage(opts: &Options) -> String {
|
||||
|
@ -36,6 +37,7 @@ Environment variables:
|
|||
}
|
||||
|
||||
// Parses flags for deno. This does not do v8_set_flags() - call that separately.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
pub fn set_flags(
|
||||
args: Vec<String>,
|
||||
) -> Result<(DenoFlags, Vec<String>, String), String> {
|
||||
|
@ -90,7 +92,7 @@ pub fn set_flags(
|
|||
flags.allow_env = true;
|
||||
}
|
||||
if matches.opt_present("types") {
|
||||
flags.types_flag = true;
|
||||
flags.types = true;
|
||||
}
|
||||
|
||||
let rest: Vec<_> = matches.free.to_vec();
|
||||
|
@ -163,7 +165,7 @@ fn test_set_flags_5() {
|
|||
assert_eq!(
|
||||
flags,
|
||||
DenoFlags {
|
||||
types_flag: true,
|
||||
types: true,
|
||||
..DenoFlags::default()
|
||||
}
|
||||
)
|
||||
|
@ -184,27 +186,16 @@ fn test_set_bad_flags_2() {
|
|||
|
||||
// Returns args passed to V8, followed by args passed to JS
|
||||
fn v8_set_flags_preprocess(args: Vec<String>) -> (Vec<String>, Vec<String>) {
|
||||
let mut rest = vec![];
|
||||
|
||||
// Filter out args that shouldn't be passed to V8
|
||||
let mut args: Vec<String> = args
|
||||
.into_iter()
|
||||
.filter(|arg| {
|
||||
if arg.as_str() == "--help" {
|
||||
rest.push(arg.clone());
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}).collect();
|
||||
let (rest, mut v8_args) =
|
||||
args.into_iter().partition(|ref a| a.as_str() == "--help");
|
||||
|
||||
// Replace args being sent to V8
|
||||
for mut a in &mut args {
|
||||
for mut a in &mut v8_args {
|
||||
if a == "--v8-options" {
|
||||
mem::swap(a, &mut String::from("--help"));
|
||||
}
|
||||
}
|
||||
(args, rest)
|
||||
(v8_args, rest)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -231,32 +222,34 @@ fn test_v8_set_flags_preprocess_2() {
|
|||
|
||||
// Pass the command line arguments to v8.
|
||||
// Returns a vector of command line arguments that v8 did not understand.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
pub fn v8_set_flags(args: Vec<String>) -> Vec<String> {
|
||||
// deno_set_v8_flags(int* argc, char** argv) mutates argc and argv to remove
|
||||
// flags that v8 understands.
|
||||
// First parse core args, then convert to a vector of C strings.
|
||||
let (argv, rest) = v8_set_flags_preprocess(args);
|
||||
let mut argv = argv
|
||||
.iter()
|
||||
.map(|arg| CString::new(arg.as_str()).unwrap().into_bytes_with_nul())
|
||||
.collect::<Vec<_>>();
|
||||
let (args, rest) = v8_set_flags_preprocess(args);
|
||||
|
||||
// Make a new array, that can be modified by V8::SetFlagsFromCommandLine(),
|
||||
// containing mutable raw pointers to the individual command line args.
|
||||
let mut c_argv = argv
|
||||
let mut raw_argv = args
|
||||
.iter()
|
||||
.map(|arg| CString::new(arg.as_str()).unwrap().into_bytes_with_nul())
|
||||
.collect::<Vec<_>>();
|
||||
let mut c_argv = raw_argv
|
||||
.iter_mut()
|
||||
.map(|arg| arg.as_mut_ptr() as *mut i8)
|
||||
.collect::<Vec<_>>();
|
||||
// Store the length of the argv array in a local variable. We'll pass a
|
||||
// pointer to this local variable to deno_set_v8_flags(), which then
|
||||
|
||||
// Store the length of the c_argv array in a local variable. We'll pass
|
||||
// a pointer to this local variable to deno_set_v8_flags(), which then
|
||||
// updates its value.
|
||||
let mut c_argc = c_argv.len() as c_int;
|
||||
let mut c_argv_len = c_argv.len() as c_int;
|
||||
// Let v8 parse the arguments it recognizes and remove them from c_argv.
|
||||
unsafe {
|
||||
libdeno::deno_set_v8_flags(&mut c_argc, c_argv.as_mut_ptr());
|
||||
libdeno::deno_set_v8_flags(&mut c_argv_len, c_argv.as_mut_ptr());
|
||||
};
|
||||
// If c_argc was updated we have to change the length of c_argv to match.
|
||||
c_argv.truncate(c_argc as usize);
|
||||
// If c_argv_len was updated we have to change the length of c_argv to match.
|
||||
c_argv.truncate(c_argv_len as usize);
|
||||
// Copy the modified arguments list into a proper rust vec and return it.
|
||||
c_argv
|
||||
.iter()
|
||||
|
|
|
@ -55,6 +55,7 @@ pub struct Isolate {
|
|||
|
||||
// Isolate cannot be passed between threads but IsolateState can. So any state that
|
||||
// needs to be accessed outside the main V8 thread should be inside IsolateState.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
pub struct IsolateState {
|
||||
pub dir: deno_dir::DenoDir,
|
||||
pub argv: Vec<String>,
|
||||
|
@ -132,7 +133,7 @@ impl Isolate {
|
|||
flags: flags::DenoFlags,
|
||||
argv_rest: Vec<String>,
|
||||
dispatch: Dispatch,
|
||||
) -> Isolate {
|
||||
) -> Self {
|
||||
DENO_INIT.call_once(|| {
|
||||
unsafe { libdeno::deno_init() };
|
||||
});
|
||||
|
@ -152,7 +153,7 @@ impl Isolate {
|
|||
Err(_e) => None,
|
||||
};
|
||||
|
||||
Isolate {
|
||||
Self {
|
||||
libdeno_isolate,
|
||||
dispatch,
|
||||
rx,
|
||||
|
@ -173,7 +174,7 @@ impl Isolate {
|
|||
self as *mut _ as *mut c_void
|
||||
}
|
||||
|
||||
pub fn from_void_ptr<'a>(ptr: *mut c_void) -> &'a mut Isolate {
|
||||
pub fn from_void_ptr<'a>(ptr: *mut c_void) -> &'a mut Self {
|
||||
let ptr = ptr as *mut _;
|
||||
unsafe { &mut *ptr }
|
||||
}
|
||||
|
@ -284,12 +285,12 @@ impl Drop for Isolate {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts Rust Buf to libdeno deno_buf.
|
||||
/// Converts Rust Buf to libdeno `deno_buf`.
|
||||
impl From<Buf> for libdeno::deno_buf {
|
||||
fn from(x: Buf) -> libdeno::deno_buf {
|
||||
fn from(x: Buf) -> Self {
|
||||
let len = x.len();
|
||||
let ptr = Box::into_raw(x);
|
||||
libdeno::deno_buf {
|
||||
Self {
|
||||
alloc_ptr: std::ptr::null_mut(),
|
||||
alloc_len: 0,
|
||||
data_ptr: ptr as *mut u8,
|
||||
|
@ -337,12 +338,12 @@ extern "C" fn pre_dispatch(
|
|||
let buf = tokio_util::block_on(op).unwrap();
|
||||
let buf_size = buf.len();
|
||||
|
||||
if buf_size != 0 {
|
||||
// Set the synchronous response, the value returned from isolate.send().
|
||||
isolate.respond(req_id, buf);
|
||||
} else {
|
||||
if buf_size == 0 {
|
||||
// FIXME
|
||||
isolate.state.metrics_op_completed(buf.len() as u64);
|
||||
} else {
|
||||
// Set the synchronous response, the value returned from isolate.send().
|
||||
isolate.respond(req_id, buf);
|
||||
}
|
||||
} else {
|
||||
// Execute op asynchronously.
|
||||
|
@ -522,9 +523,8 @@ mod tests {
|
|||
_data: &'static mut [u8],
|
||||
) -> (bool, Box<Op>) {
|
||||
// Send back some sync response
|
||||
let vec: Vec<u8> = vec![1, 2, 3, 4];
|
||||
let control = vec.into_boxed_slice();
|
||||
let op = Box::new(futures::future::ok(control));
|
||||
let vec: Box<[u8]> = vec![1, 2, 3, 4].into_boxed_slice();
|
||||
let op = Box::new(futures::future::ok(vec));
|
||||
(true, op)
|
||||
}
|
||||
|
||||
|
@ -534,9 +534,8 @@ mod tests {
|
|||
_data: &'static mut [u8],
|
||||
) -> (bool, Box<Op>) {
|
||||
// Send back some sync response
|
||||
let vec: Vec<u8> = vec![1, 2, 3, 4];
|
||||
let control = vec.into_boxed_slice();
|
||||
let op = Box::new(futures::future::ok(control));
|
||||
let vec: Box<[u8]> = vec![1, 2, 3, 4].into_boxed_slice();
|
||||
let op = Box::new(futures::future::ok(vec));
|
||||
(false, op)
|
||||
}
|
||||
}
|
||||
|
|
22
src/msg.fbs
22
src/msg.fbs
|
@ -283,11 +283,11 @@ table ReplStart {
|
|||
}
|
||||
|
||||
table ReplStartRes {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table ReplReadline {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ table ReplReadlineRes {
|
|||
table Resources {}
|
||||
|
||||
table Resource {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
repr: string;
|
||||
}
|
||||
|
||||
|
@ -340,11 +340,11 @@ table Open {
|
|||
}
|
||||
|
||||
table OpenRes {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table Read {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
// (ptr, len) is passed as second parameter to libdeno.send().
|
||||
}
|
||||
|
||||
|
@ -354,7 +354,7 @@ table ReadRes {
|
|||
}
|
||||
|
||||
table Write {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table WriteRes {
|
||||
|
@ -362,11 +362,11 @@ table WriteRes {
|
|||
}
|
||||
|
||||
table Close {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table Shutdown {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
how: uint;
|
||||
}
|
||||
|
||||
|
@ -376,11 +376,11 @@ table Listen {
|
|||
}
|
||||
|
||||
table ListenRes {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table Accept {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
}
|
||||
|
||||
table Dial {
|
||||
|
@ -390,7 +390,7 @@ table Dial {
|
|||
|
||||
// Response to Accept and Dial.
|
||||
table NewConn {
|
||||
rid: int;
|
||||
rid: uint32;
|
||||
remote_addr: string;
|
||||
local_addr: string;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy, pedantic))]
|
||||
use flatbuffers;
|
||||
// GN_OUT_DIR is set either by build.rs (for the Cargo build), or by
|
||||
// build_extra/rust/run.py (for the GN+Ninja build).
|
||||
|
|
10
src/ops.rs
10
src/ops.rs
|
@ -184,10 +184,10 @@ fn op_start(
|
|||
let cwd_off =
|
||||
builder.create_string(deno_fs::normalize_path(cwd_path.as_ref()).as_ref());
|
||||
|
||||
let v8_version = version::get_v8_version();
|
||||
let v8_version = version::v8();
|
||||
let v8_version_off = builder.create_string(v8_version);
|
||||
|
||||
let deno_version = version::DENO_VERSION;
|
||||
let deno_version = version::DENO;
|
||||
let deno_version_off = builder.create_string(deno_version);
|
||||
|
||||
let inner = msg::StartRes::create(
|
||||
|
@ -197,7 +197,7 @@ fn op_start(
|
|||
argv: Some(argv_off),
|
||||
debug_flag: state.flags.log_debug,
|
||||
recompile_flag: state.flags.recompile,
|
||||
types_flag: state.flags.types_flag,
|
||||
types_flag: state.flags.types,
|
||||
version_flag: state.flags.version,
|
||||
v8_version: Some(v8_version_off),
|
||||
deno_version: Some(deno_version_off),
|
||||
|
@ -323,6 +323,7 @@ fn op_set_timeout(
|
|||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
let inner = base.inner_as_set_timeout().unwrap();
|
||||
// FIXME why is timeout a double if it's cast immediately to i64?
|
||||
let val = inner.timeout() as i64;
|
||||
isolate.timeout_due = if val >= 0 {
|
||||
Some(Instant::now() + Duration::from_millis(val as u64))
|
||||
|
@ -585,7 +586,10 @@ fn op_chmod(
|
|||
// Only work in unix
|
||||
#[cfg(any(unix))]
|
||||
{
|
||||
// We need to use underscore to compile in Windows.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
|
||||
let mut permissions = _metadata.permissions();
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
|
||||
permissions.set_mode(_mode);
|
||||
fs::set_permissions(&path, permissions)?;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use errors::permission_denied;
|
|||
use errors::DenoResult;
|
||||
use std::io;
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct DenoPermissions {
|
||||
pub allow_write: bool,
|
||||
|
@ -14,8 +15,8 @@ pub struct DenoPermissions {
|
|||
}
|
||||
|
||||
impl DenoPermissions {
|
||||
pub fn new(flags: &DenoFlags) -> DenoPermissions {
|
||||
DenoPermissions {
|
||||
pub fn new(flags: &DenoFlags) -> Self {
|
||||
Self {
|
||||
allow_write: flags.allow_write,
|
||||
allow_env: flags.allow_env,
|
||||
allow_net: flags.allow_net,
|
||||
|
|
|
@ -61,8 +61,8 @@ pub struct Repl {
|
|||
}
|
||||
|
||||
impl Repl {
|
||||
pub fn new(history_file: PathBuf) -> Repl {
|
||||
let mut repl = Repl {
|
||||
pub fn new(history_file: PathBuf) -> Self {
|
||||
let mut repl = Self {
|
||||
editor: Editor::<()>::new(),
|
||||
history_file,
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ use std;
|
|||
use std::collections::HashMap;
|
||||
use std::io::{Error, Read, Write};
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Mutex;
|
||||
use tokio;
|
||||
|
@ -32,7 +32,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
|
|||
use tokio::net::TcpStream;
|
||||
use tokio_io;
|
||||
|
||||
pub type ResourceId = i32; // Sometimes referred to RID.
|
||||
pub type ResourceId = u32; // Sometimes referred to RID.
|
||||
|
||||
// These store Deno's file descriptors. These are not necessarily the operating
|
||||
// system ones.
|
||||
|
@ -40,7 +40,7 @@ type ResourceTable = HashMap<ResourceId, Repr>;
|
|||
|
||||
lazy_static! {
|
||||
// Starts at 3 because stdio is [0-2].
|
||||
static ref NEXT_RID: AtomicIsize = AtomicIsize::new(3);
|
||||
static ref NEXT_RID: AtomicUsize = AtomicUsize::new(3);
|
||||
static ref RESOURCE_TABLE: Mutex<ResourceTable> = Mutex::new({
|
||||
let mut m = HashMap::new();
|
||||
// TODO Load these lazily during lookup?
|
||||
|
@ -62,7 +62,7 @@ enum Repr {
|
|||
Repl(Repl),
|
||||
}
|
||||
|
||||
pub fn table_entries() -> Vec<(i32, String)> {
|
||||
pub fn table_entries() -> Vec<(u32, String)> {
|
||||
let table = RESOURCE_TABLE.lock().unwrap();
|
||||
|
||||
table
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
use libdeno;
|
||||
use std::ffi::CStr;
|
||||
|
||||
// Both the verson in Cargo.toml and this string must be kept in sync.
|
||||
pub const DENO_VERSION: &str = "0.1.11";
|
||||
pub const DENO: &str = "0.1.11";
|
||||
|
||||
pub fn get_v8_version() -> &'static str {
|
||||
let v = unsafe { libdeno::deno_v8_version() };
|
||||
let c_str = unsafe { CStr::from_ptr(v) };
|
||||
pub fn v8() -> &'static str {
|
||||
let version = unsafe { libdeno::deno_v8_version() };
|
||||
let c_str = unsafe { CStr::from_ptr(version) };
|
||||
c_str.to_str().unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue