mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-22 22:44:47 +03:00
Merge pull request #1994 from AleoHQ/remove-unsafe
Remove all uses of unsafe
This commit is contained in:
commit
46f21c68a2
@ -16,7 +16,7 @@
|
||||
|
||||
use crate::Program;
|
||||
|
||||
use leo_span::Symbol;
|
||||
use leo_span::{symbol::with_session_globals, Symbol};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
@ -26,13 +26,18 @@ pub fn serialize<S: Serializer>(
|
||||
imported_modules: &IndexMap<Vec<Symbol>, Program>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let joined: IndexMap<String, Program> = imported_modules
|
||||
let joined: IndexMap<String, Program> = with_session_globals(|s| {
|
||||
imported_modules
|
||||
.into_iter()
|
||||
.map(|(package, program)| {
|
||||
let package = package.iter().map(|x| x.as_str().to_string()).collect::<Vec<_>>();
|
||||
let package = package
|
||||
.iter()
|
||||
.map(|x| x.as_str(s, |s| s.to_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
(package.join("."), program.clone())
|
||||
})
|
||||
.collect();
|
||||
.collect()
|
||||
});
|
||||
|
||||
joined.serialize(serializer)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
//! The [`Ast`] type is intended to be parsed and modified by different passes
|
||||
//! of the Leo compiler. The Leo compiler can generate a set of R1CS constraints from any [`Ast`].
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
pub mod access;
|
||||
pub use self::access::*;
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![allow(clippy::module_inception)]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
mod algorithms;
|
||||
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use leo_errors::{emitter::Handler, Result};
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use leo_ast::Ast;
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
|
@ -19,6 +19,7 @@
|
||||
//! This module contains the [`parse_ast()`] method which calls the underlying [`parse()`]
|
||||
//! method to create a new program ast.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![allow(clippy::vec_init_then_push)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod code_generation;
|
||||
|
@ -1,175 +0,0 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Copyright Rust project developers under MIT or APACHE-2.0.
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use core::{cmp, ptr, slice};
|
||||
use std::iter;
|
||||
|
||||
// The arenas start with PAGE-sized chunks, and then each new chunk is twice as
|
||||
// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon
|
||||
// we stop growing. This scales well, from arenas that are barely used up to
|
||||
// arenas that are used for 100s of MiBs. Note also that the chosen sizes match
|
||||
// the usual sizes of pages and huge pages on Linux.
|
||||
const PAGE: usize = 4096;
|
||||
const HUGE_PAGE: usize = 2 * 1024 * 1024;
|
||||
|
||||
pub struct DroplessArena {
|
||||
/// A pointer to the start of the free space.
|
||||
start: Cell<*mut u8>,
|
||||
|
||||
/// A pointer to the end of free space.
|
||||
///
|
||||
/// The allocation proceeds from the end of the chunk towards the start.
|
||||
/// When this pointer crosses the start pointer, a new chunk is allocated.
|
||||
end: Cell<*mut u8>,
|
||||
|
||||
/// A vector of arena chunks.
|
||||
chunks: RefCell<Vec<TypedArenaChunk<u8>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for DroplessArena {}
|
||||
|
||||
impl Default for DroplessArena {
|
||||
#[inline]
|
||||
fn default() -> DroplessArena {
|
||||
DroplessArena {
|
||||
start: Cell::new(ptr::null_mut()),
|
||||
end: Cell::new(ptr::null_mut()),
|
||||
chunks: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DroplessArena {
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn grow(&self, additional: usize) {
|
||||
unsafe {
|
||||
let mut chunks = self.chunks.borrow_mut();
|
||||
|
||||
let new_cap = if let Some(last_chunk) = chunks.last_mut() {
|
||||
// If the previous chunk's len is less than HUGE_PAGE bytes,
|
||||
// then this chunk will be at least double the previous chunk's size.
|
||||
last_chunk.storage.len().min(HUGE_PAGE / 2) * 2
|
||||
} else {
|
||||
PAGE
|
||||
};
|
||||
// Also ensure that this chunk can fit `additional`.
|
||||
let new_cap = cmp::max(additional, new_cap);
|
||||
|
||||
let mut chunk = TypedArenaChunk::<u8>::new(new_cap);
|
||||
self.start.set(chunk.start());
|
||||
self.end.set(chunk.end());
|
||||
chunks.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a byte slice with specified layout from the current memory chunk.
|
||||
/// Returns `None` if there is no free space left to satisfy the request.
|
||||
#[inline]
|
||||
fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> {
|
||||
let start = self.start.get() as usize;
|
||||
let end = self.end.get() as usize;
|
||||
|
||||
let align = layout.align();
|
||||
let bytes = layout.size();
|
||||
|
||||
let new_end = end.checked_sub(bytes)? & !(align - 1);
|
||||
if start <= new_end {
|
||||
let new_end = new_end as *mut u8;
|
||||
self.end.set(new_end);
|
||||
Some(new_end)
|
||||
} else {
|
||||
// There's no more space since we're growing towards the start.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn alloc_raw(&self, layout: Layout) -> *mut u8 {
|
||||
assert!(layout.size() != 0);
|
||||
loop {
|
||||
if let Some(a) = self.alloc_raw_without_grow(layout) {
|
||||
break a;
|
||||
}
|
||||
// No free space left. Allocate a new chunk to satisfy the request.
|
||||
// On failure the grow will panic or abort.
|
||||
self.grow(layout.size());
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a slice of objects that are copied into the `DroplessArena`,
|
||||
/// returning a mutable reference to it.
|
||||
/// Will panic if passed a zero-sized type.
|
||||
///
|
||||
/// Panics:
|
||||
///
|
||||
/// - Zero-sized types
|
||||
/// - Zero-length slices
|
||||
#[inline]
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &mut [T] {
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(!slice.is_empty());
|
||||
|
||||
let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
|
||||
|
||||
unsafe {
|
||||
mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len());
|
||||
slice::from_raw_parts_mut(mem, slice.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TypedArenaChunk<T> {
|
||||
/// The raw storage for the arena chunk.
|
||||
storage: Box<[MaybeUninit<T>]>,
|
||||
}
|
||||
|
||||
impl<T> TypedArenaChunk<T> {
|
||||
#[inline]
|
||||
unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
|
||||
TypedArenaChunk {
|
||||
// HACK(Centril) around `Box::new_uninit_slice` not being stable.
|
||||
storage: iter::repeat_with(MaybeUninit::<T>::uninit).take(capacity).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a pointer to the first allocated object.
|
||||
#[inline]
|
||||
fn start(&mut self) -> *mut T {
|
||||
// HACK(Centril) around `MaybeUninit::slice_as_mut_ptr` not being stable.
|
||||
self.storage.as_mut_ptr() as *mut T
|
||||
}
|
||||
|
||||
// Returns a pointer to the end of the allocated space.
|
||||
#[inline]
|
||||
fn end(&mut self) -> *mut T {
|
||||
unsafe {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
// A pointer as large as possible for zero-sized elements.
|
||||
!0 as *mut T
|
||||
} else {
|
||||
self.start().add(self.storage.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod dropless;
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod symbol;
|
||||
pub use symbol::{sym, Symbol};
|
||||
|
@ -368,12 +368,10 @@ fn normalize_newlines(src: &mut String) {
|
||||
}
|
||||
|
||||
// Account for removed `\r`.
|
||||
// After `set_len`, `buf` is guaranteed to contain utf-8 again.
|
||||
// After `buf.truncate(..)`, `buf` is guaranteed to contain utf-8 again.
|
||||
let new_len = buf.len() - gap_len;
|
||||
unsafe {
|
||||
buf.set_len(new_len);
|
||||
*src = String::from_utf8_unchecked(buf);
|
||||
}
|
||||
buf.truncate(new_len);
|
||||
*src = String::from_utf8(buf).unwrap();
|
||||
|
||||
fn find_crlf(src: &[u8]) -> Option<usize> {
|
||||
let mut search_idx = 0;
|
||||
@ -414,9 +412,8 @@ fn analyze_source_file(src: &str, source_file_start_pos: BytePos) -> (Vec<BytePo
|
||||
let src_bytes = src.as_bytes();
|
||||
|
||||
while i < src.len() {
|
||||
// SAFETY: We verified that i < src.len().
|
||||
let i_usize = i as usize;
|
||||
let byte = unsafe { *src_bytes.get_unchecked(i_usize) };
|
||||
let byte = src_bytes[i_usize];
|
||||
|
||||
// How much to advance to get to the next UTF-8 char in the string.
|
||||
let mut char_len = 1;
|
||||
|
@ -14,11 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::dropless::DroplessArena;
|
||||
use crate::source_map::SourceMap;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::PartialEq;
|
||||
use core::convert::AsRef;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::num::NonZeroU32;
|
||||
use core::ops::Deref;
|
||||
use core::{fmt, str};
|
||||
@ -26,8 +26,6 @@ use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cell::RefCell;
|
||||
use std::intrinsics::transmute;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A helper for `symbols` defined below.
|
||||
/// The macro's job is to bind conveniently usable `const` items to the symbol names provided.
|
||||
@ -240,8 +238,10 @@ impl Symbol {
|
||||
/// Returns the corresponding `Symbol` for the given `index`.
|
||||
pub const fn new(index: u32) -> Self {
|
||||
let index = index.saturating_add(1);
|
||||
// SAFETY: per above addition, we know `index > 0` always applies.
|
||||
Self(unsafe { NonZeroU32::new_unchecked(index) })
|
||||
Self(match NonZeroU32::new(index) {
|
||||
None => unreachable!(),
|
||||
Some(x) => x,
|
||||
})
|
||||
}
|
||||
|
||||
/// Maps a string to its interned representation.
|
||||
@ -249,13 +249,9 @@ impl Symbol {
|
||||
with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
|
||||
}
|
||||
|
||||
/// Convert to effectively a `&'static str`.
|
||||
/// This is a slowish operation because it requires locking the symbol interner.
|
||||
pub fn as_str(self) -> SymbolStr {
|
||||
with_session_globals(|session_globals| {
|
||||
let symbol_str = session_globals.symbol_interner.get(self);
|
||||
SymbolStr::new(unsafe { std::mem::transmute::<&str, &str>(symbol_str) })
|
||||
})
|
||||
/// Convert to effectively a `&'static str` given the `SessionGlobals`.
|
||||
pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
|
||||
s.symbol_interner.get(self, with)
|
||||
}
|
||||
|
||||
/// Converts this symbol to the raw index.
|
||||
@ -268,82 +264,19 @@ impl Symbol {
|
||||
}
|
||||
|
||||
fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
|
||||
ser.serialize_str(&Self(*index).as_str())
|
||||
with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.as_str(), f)
|
||||
with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An alternative to [`Symbol`], useful when the chars within the symbol need to
|
||||
/// be accessed. It deliberately has limited functionality and should only be
|
||||
/// used for temporary values.
|
||||
///
|
||||
/// Because the interner outlives any thread which uses this type, we can
|
||||
/// safely treat `string` which points to interner data, as an immortal string,
|
||||
/// as long as this type never crosses between threads.
|
||||
#[derive(Clone, Eq, PartialOrd, Ord)]
|
||||
pub struct SymbolStr {
|
||||
string: &'static str,
|
||||
/// Ensures the type is neither `Sync` nor `Send`,
|
||||
/// so that we satisfy "never crosses between threads" per above.
|
||||
not_sync_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl SymbolStr {
|
||||
/// Create a `SymbolStr` from a `&'static str`.
|
||||
pub fn new(string: &'static str) -> Self {
|
||||
Self {
|
||||
string,
|
||||
not_sync_send: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This impl allows a `SymbolStr` to be directly equated with a `String` or `&str`.
|
||||
impl<T: Deref<Target = str>> PartialEq<T> for SymbolStr {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.string == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
/// This impl means that if `ss` is a `SymbolStr`:
|
||||
/// - `*ss` is a `str`;
|
||||
/// - `&*ss` is a `&str` (and `match &*ss { ... }` is a common pattern).
|
||||
/// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a
|
||||
/// function expecting a `&str`.
|
||||
impl Deref for SymbolStr {
|
||||
type Target = str;
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
self.string
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for SymbolStr {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.string
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SymbolStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.string, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SymbolStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.string, f)
|
||||
with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,13 +316,53 @@ pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
|
||||
SESSION_GLOBALS.with(f)
|
||||
}
|
||||
|
||||
/// An interned string,
|
||||
/// either prefilled "at compile time" (`Static`),
|
||||
/// or created at runtime (`Owned`).
|
||||
#[derive(Eq)]
|
||||
enum InternedStr {
|
||||
/// String is stored "at compile time", i.e. prefilled.
|
||||
Static(&'static str),
|
||||
/// String is constructed and stored during runtime.
|
||||
Owned(Box<str>),
|
||||
}
|
||||
|
||||
impl Borrow<str> for InternedStr {
|
||||
fn borrow(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for InternedStr {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Static(s) => s,
|
||||
Self::Owned(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InternedStr {
|
||||
fn eq(&self, other: &InternedStr) -> bool {
|
||||
self.deref() == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for InternedStr {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.deref().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// The inner interner.
|
||||
/// This construction is used to get interior mutability in `Interner`.
|
||||
struct InnerInterner {
|
||||
/// Arena used to allocate the strings, giving us `&'static str`s from it.
|
||||
arena: DroplessArena,
|
||||
// /// Arena used to allocate the strings, giving us `&'static str`s from it.
|
||||
// arena: DroplessArena,
|
||||
/// Registration of strings and symbol index allocation is done in this set.
|
||||
set: IndexSet<&'static str, FxBuildHasher>,
|
||||
set: IndexSet<InternedStr, FxBuildHasher>,
|
||||
}
|
||||
|
||||
/// A symbol-to-string interner.
|
||||
@ -406,8 +379,8 @@ impl Interner {
|
||||
/// Returns an interner prefilled with `init`.
|
||||
fn prefill(init: &[&'static str]) -> Self {
|
||||
let inner = InnerInterner {
|
||||
arena: <_>::default(),
|
||||
set: init.iter().copied().collect(),
|
||||
// arena: <_>::default(),
|
||||
set: init.iter().copied().map(InternedStr::Static).collect(),
|
||||
};
|
||||
Self {
|
||||
inner: RefCell::new(inner),
|
||||
@ -416,30 +389,19 @@ impl Interner {
|
||||
|
||||
/// Interns `string`, returning a `Symbol` corresponding to it.
|
||||
fn intern(&self, string: &str) -> Symbol {
|
||||
let InnerInterner { arena, set } = &mut *self.inner.borrow_mut();
|
||||
let InnerInterner { set } = &mut *self.inner.borrow_mut();
|
||||
|
||||
if let Some(sym) = set.get_index_of(string) {
|
||||
// Already internet, return that symbol.
|
||||
// Already interned, return that symbol.
|
||||
return Symbol::new(sym as u32);
|
||||
}
|
||||
|
||||
// SAFETY: `from_utf8_unchecked` is safe since we just allocated a `&str`,
|
||||
// which is known to be UTF-8.
|
||||
let bytes = arena.alloc_slice(string.as_bytes());
|
||||
let string: &str = unsafe { str::from_utf8_unchecked(bytes) };
|
||||
|
||||
unsafe fn transmute_lt<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
|
||||
transmute(x)
|
||||
}
|
||||
|
||||
// SAFETY: Extending to `'static` is fine. Accesses only happen while the arena is alive.
|
||||
let string: &'static _ = unsafe { transmute_lt(string) };
|
||||
|
||||
Symbol::new(set.insert_full(string).0 as u32)
|
||||
Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
|
||||
}
|
||||
|
||||
/// Returns the corresponding string for the given symbol.
|
||||
fn get(&self, symbol: Symbol) -> &str {
|
||||
self.inner.borrow().set.get_index(symbol.as_u32() as usize).unwrap()
|
||||
fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
|
||||
let set = &self.inner.borrow().set;
|
||||
with(set.get_index(symbol.as_u32() as usize).unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,8 @@
|
||||
// ```
|
||||
//
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use abnf::types::{Node, Rule};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, clippy::missing_docs_in_private_items)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod commands;
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod build;
|
||||
|
@ -22,6 +22,7 @@
|
||||
//! To regenerate the tests after a syntax change or failing test, delete the [`tests/expectations/`]
|
||||
//! directory and run the [`parser_tests()`] test in [`parser/src/test.rs`].
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![cfg(not(doctest))] // Don't doctest the markdown.
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user