#![warn(
bare_trait_objects,
elided_lifetimes_in_paths,
missing_copy_implementations,
missing_debug_implementations,
future_incompatible,
rust_2018_idioms,
trivial_numeric_casts,
variant_size_differences,
unreachable_pub,
unused,
missing_docs
)]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![warn(
clippy::else_if_without_else,
clippy::float_arithmetic,
clippy::get_unwrap,
clippy::inline_asm_x86_att_syntax,
clippy::integer_division,
clippy::rc_buffer,
clippy::rest_pat_in_fully_bound_structs,
clippy::string_add,
clippy::unwrap_used,
clippy::wrong_pub_self_convention
)]
#![allow(
clippy::cognitive_complexity,
clippy::comparison_chain,
clippy::default_trait_access,
clippy::filter_map,
clippy::inline_always,
clippy::map_err_ignore,
clippy::missing_const_for_fn,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::multiple_crate_versions,
clippy::option_if_let_else,
clippy::shadow_unrelated,
clippy::too_many_lines,
clippy::use_self
)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate bitflags;
#[cfg(feature = "memory")]
use mm0_deepsize_derive::DeepSizeOf;
use std::collections::{
hash_map::{Entry, OccupiedEntry},
HashMap,
};
use std::error::Error;
use std::ffi::CStr;
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
use std::mem::{self, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{borrow::Borrow, convert::TryInto};
mod ids;
mod atoms;
mod lined_string;
pub use {ids::*, lined_string::*};
pub type BoxError = Box<dyn Error + Send + Sync>;
pub trait SliceExt<T> {
fn cloned_box(&self) -> Box<[T]>
where T: Clone;
}
impl<T> SliceExt<T> for [T] {
fn cloned_box(&self) -> Box<[T]>
where T: Clone {
self.to_vec().into()
}
}
pub trait HashMapExt<K, V> {
fn try_insert_ext(&mut self, k: K, v: V) -> Option<(V, OccupiedEntry<'_, K, V>)>;
}
impl<K: Hash + Eq, V, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S> {
fn try_insert_ext(&mut self, k: K, v: V) -> Option<(V, OccupiedEntry<'_, K, V>)> {
match self.entry(k) {
Entry::Vacant(e) => {
e.insert(v);
None
}
Entry::Occupied(e) => Some((v, e)),
}
}
}
#[macro_export]
macro_rules! let_unchecked {
($q:ident as $p:pat = $e:expr) => {
let $q = let_unchecked!($p = $e, $q);
};
(($($q:tt),*) as $p:pat = $e:expr) => {
let ($($q),*) = let_unchecked!($p = $e, ($($q),*));
};
($p:pat = $e:expr, $bl:expr) => {
if let $p = $e { $bl } else { unsafe { std::hint::unreachable_unchecked() } }
};
}
#[macro_export]
macro_rules! unwrap_unchecked {
($e:expr) => {
let_unchecked!(Some(x) = $e, x)
};
}
#[macro_export]
macro_rules! const_panic {
() => {{
#[allow(unconditional_panic)] [][0]
}}
}
#[inline]
pub fn u32_as_usize(n: u32) -> usize {
n.try_into().expect("here's a nickel, get a better computer")
}
pub trait MutexExt<T> {
fn ulock(&self) -> std::sync::MutexGuard<'_, T>;
}
impl<T> MutexExt<T> for std::sync::Mutex<T> {
fn ulock(&self) -> std::sync::MutexGuard<'_, T> {
self.lock().expect("propagating poisoned mutex")
}
}
pub trait CondvarExt {
fn uwait<'a, T>(&self, g: std::sync::MutexGuard<'a, T>) -> std::sync::MutexGuard<'a, T>;
}
impl CondvarExt for std::sync::Condvar {
fn uwait<'a, T>(&self, g: std::sync::MutexGuard<'a, T>) -> std::sync::MutexGuard<'a, T> {
self.wait(g).expect("propagating poisoned mutex")
}
}
#[cfg_attr(feature = "memory", derive(DeepSizeOf))]
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct ArcString(pub Arc<[u8]>);
impl Borrow<[u8]> for ArcString {
fn borrow(&self) -> &[u8] { &*self.0 }
}
impl Deref for ArcString {
type Target = [u8];
fn deref(&self) -> &[u8] { &*self.0 }
}
impl ArcString {
#[must_use]
pub fn new(s: Box<[u8]>) -> Self { Self(s.into()) }
}
impl fmt::Display for ArcString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(self))
}
}
impl fmt::Debug for ArcString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", String::from_utf8_lossy(self))
}
}
impl From<&[u8]> for ArcString {
fn from(s: &[u8]) -> Self { Self::new(s.into()) }
}
impl From<Box<[u8]>> for ArcString {
fn from(s: Box<[u8]>) -> Self { Self::new(s) }
}
impl From<Vec<u8>> for ArcString {
fn from(s: Vec<u8>) -> Self { s.into_boxed_slice().into() }
}
impl From<String> for ArcString {
fn from(s: String) -> Self { s.into_bytes().into() }
}
impl ArcString {
pub fn as_str(&self) -> &str { unsafe { std::str::from_utf8_unchecked(self) } }
}
#[derive(Debug, Clone, Copy)]
pub struct StackList<'a, T>(pub Option<&'a (StackList<'a, T>, T)>);
impl<T> StackList<'_, T> {
pub fn contains(&self, t: &T) -> bool
where T: PartialEq {
let mut s = self;
loop {
match s.0 {
None => return false,
Some((_, t2)) if *t2 == *t => return true,
Some((s2, _)) => s = s2,
}
}
}
}
#[cfg_attr(feature = "memory", derive(DeepSizeOf))]
#[derive(Debug)]
pub struct ArcList<T>(Option<Arc<(ArcList<T>, T)>>);
impl<T> Default for ArcList<T> {
fn default() -> Self { Self(None) }
}
impl<T> Clone for ArcList<T> {
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl<T> ArcList<T> {
#[must_use]
pub fn is_empty(&self) -> bool { self.0.is_none() }
#[must_use]
pub fn push(self, t: T) -> Self { Self(Some(Arc::new((self, t)))) }
#[must_use]
pub fn contains(&self, t: &T) -> bool
where T: PartialEq {
let mut s = self;
loop {
match s.0.as_deref() {
None => return false,
Some((_, t2)) if *t2 == *t => return true,
Some((s2, _)) => s = s2,
}
}
}
#[must_use]
pub fn join(&self, t: T, tail: Self) -> Self
where T: PartialEq + Clone {
let (l, t2) = &**self.0.as_ref().expect("self should contain t");
if *t2 == t {
return tail.push(t)
}
l.join(t, tail).push(t2.clone())
}
}
#[derive(Debug, Clone)]
pub struct ArcListIter<'a, T>(&'a ArcList<T>);
impl<'a, T> Iterator for ArcListIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let (l, t) = &**self.0 .0.as_ref()?;
self.0 = l;
Some(t)
}
}
impl<'a, T> IntoIterator for &'a ArcList<T> {
type Item = &'a T;
type IntoIter = ArcListIter<'a, T>;
fn into_iter(self) -> ArcListIter<'a, T> { ArcListIter(self) }
}
#[derive(Debug)]
pub struct SliceUninit<T>(Box<[MaybeUninit<T>]>);
impl<T> SliceUninit<T> {
#[must_use]
pub fn new(size: usize) -> Self {
let mut res = Vec::with_capacity(size);
unsafe { res.set_len(size) };
Self(res.into_boxed_slice())
}
pub fn set(&mut self, i: usize, val: T) { self.0[i] = MaybeUninit::new(val) }
#[must_use]
pub unsafe fn assume_init(self) -> Box<[T]> { mem::transmute(self.0) }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Span {
pub start: usize,
pub end: usize,
}
#[cfg(feature = "memory")]
mm0_deepsize::deep_size_0!(Span);
impl From<std::ops::Range<usize>> for Span {
#[inline]
fn from(r: std::ops::Range<usize>) -> Self { Span { start: r.start, end: r.end } }
}
impl From<std::ops::RangeInclusive<usize>> for Span {
#[inline]
fn from(r: std::ops::RangeInclusive<usize>) -> Self {
Span { start: *r.start(), end: *r.end() + 1 }
}
}
impl From<usize> for Span {
#[inline]
fn from(n: usize) -> Self { Span { start: n, end: n } }
}
impl From<Span> for std::ops::Range<usize> {
#[inline]
fn from(s: Span) -> Self { s.start..s.end }
}
impl Deref for Span {
type Target = std::ops::Range<usize>;
fn deref(&self) -> &std::ops::Range<usize> { unsafe { &*<*const _>::cast(self) } }
}
impl DerefMut for Span {
fn deref_mut(&mut self) -> &mut std::ops::Range<usize> { unsafe { &mut *<*mut _>::cast(self) } }
}
impl IntoIterator for Span {
type Item = usize;
type IntoIter = std::ops::Range<usize>;
fn into_iter(self) -> std::ops::Range<usize> { (*self).clone() }
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
#[cfg(feature = "server")]
pub use lsp_types::{Position, Range};
#[cfg(not(feature = "server"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default)]
pub struct Position {
pub line: u32,
pub character: u32,
}
#[cfg(not(feature = "server"))]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Range {
pub start: Position,
pub end: Position,
}
lazy_static! {
pub static ref CURRENT_DIR: PathBuf =
std::fs::canonicalize(".").expect("failed to find current directory");
}
#[cfg(not(target_arch = "wasm32"))]
fn make_relative(buf: &Path) -> String {
pathdiff::diff_paths(buf, &*CURRENT_DIR)
.as_deref()
.unwrap_or(buf)
.to_str()
.expect("bad unicode in file path")
.to_owned()
}
#[cfg_attr(feature = "memory", derive(DeepSizeOf))]
struct FileRefInner {
path: PathBuf,
rel: String,
#[cfg(feature = "server")]
url: lsp_types::Url,
}
#[cfg_attr(feature = "memory", derive(DeepSizeOf))]
#[derive(Clone)]
pub struct FileRef(Arc<FileRefInner>);
impl From<PathBuf> for FileRef {
#[cfg(target_arch = "wasm32")]
fn from(_: PathBuf) -> FileRef { todo!() }
#[cfg(not(target_arch = "wasm32"))]
fn from(path: PathBuf) -> FileRef {
FileRef(Arc::new(FileRefInner {
rel: make_relative(&path),
#[cfg(feature = "server")]
url: lsp_types::Url::from_file_path(&path).expect("bad file path"),
path,
}))
}
}
#[cfg(feature = "server")]
impl From<lsp_types::Url> for FileRef {
#[cfg(target_arch = "wasm32")]
fn from(_: lsp_types::Url) -> FileRef { todo!() }
#[cfg(not(target_arch = "wasm32"))]
fn from(url: lsp_types::Url) -> FileRef {
let path = url.to_file_path().expect("bad URL");
let rel = make_relative(&path);
FileRef(Arc::new(FileRefInner { path, rel, url }))
}
}
impl FileRef {
#[must_use]
pub fn path(&self) -> &PathBuf { &self.0.path }
#[must_use]
pub fn rel(&self) -> &str { &self.0.rel }
#[cfg(feature = "server")]
#[must_use]
pub fn url(&self) -> &lsp_types::Url { &self.0.url }
#[must_use]
pub fn ptr(&self) -> *const PathBuf { self.path() }
#[must_use]
pub fn ptr_eq(&self, other: &FileRef) -> bool { Arc::ptr_eq(&self.0, &other.0) }
#[must_use]
pub fn has_extension(&self, ext: &str) -> bool {
self.path().extension().map_or(false, |s| s == ext)
}
}
impl PartialEq for FileRef {
fn eq(&self, other: &Self) -> bool { self.0.rel == other.0.rel }
}
impl Eq for FileRef {}
impl Hash for FileRef {
fn hash<H: Hasher>(&self, state: &mut H) { self.0.rel.hash(state) }
}
impl fmt::Display for FileRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = self.0.path.file_name().unwrap_or_else(|| self.0.path.as_os_str());
s.to_str().expect("bad unicode in path").fmt(f)
}
}
impl fmt::Debug for FileRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) }
}
#[cfg_attr(feature = "memory", derive(DeepSizeOf))]
#[derive(Clone, PartialEq, Eq)]
pub struct FileSpan {
pub file: FileRef,
pub span: Span,
}
impl fmt::Debug for FileSpan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{:?}", self.file, self.span)
}
}
impl<'a> From<&'a FileSpan> for Span {
fn from(fsp: &'a FileSpan) -> Self { fsp.span }
}
#[must_use]
pub fn cstr_from_bytes_prefix(bytes: &[u8]) -> Option<(&CStr, &[u8])> {
let mid = memchr::memchr(0, bytes)? + 1;
unsafe {
Some((
CStr::from_bytes_with_nul_unchecked(bytes.get_unchecked(..mid)),
bytes.get_unchecked(..mid),
))
}
}
#[allow(unused)]
#[cfg(feature = "memory")]
fn get_memory_rusage() -> usize {
use std::convert::TryInto;
let usage = unsafe {
let mut usage = MaybeUninit::uninit();
assert_eq!(libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()), 0);
usage.assume_init()
};
let x: usize = usage.ru_maxrss.try_into().expect("negative memory?");
x * 1024
}
#[cfg(all(feature = "memory", target_os = "linux"))]
#[must_use]
pub fn get_memory_usage() -> usize {
procinfo::pid::statm_self().map_or_else(|_| get_memory_rusage(), |stat| stat.data * 4096)
}
#[cfg(all(feature = "memory", not(target_os = "linux")))]
#[must_use]
pub fn get_memory_usage() -> usize { get_memory_rusage() }
#[cfg(not(feature = "memory"))]
#[must_use]
pub fn get_memory_usage() -> usize { 0 }