Add Limit struct.

Fixes #9286.
This commit is contained in:
Robert Bartlensky 2021-07-10 21:49:17 +01:00
parent fe00358888
commit 0b3d0cde8b
16 changed files with 84 additions and 24 deletions

8
Cargo.lock generated
View File

@ -487,6 +487,7 @@ dependencies = [
"indexmap",
"itertools",
"la-arena",
"limit",
"log",
"mbe",
"once_cell",
@ -508,6 +509,7 @@ dependencies = [
"either",
"expect-test",
"la-arena",
"limit",
"log",
"mbe",
"parser",
@ -534,6 +536,7 @@ dependencies = [
"hir_expand",
"itertools",
"la-arena",
"limit",
"log",
"once_cell",
"profile",
@ -640,6 +643,7 @@ dependencies = [
"fst",
"hir",
"itertools",
"limit",
"log",
"once_cell",
"profile",
@ -792,6 +796,10 @@ dependencies = [
"cc",
]
[[package]]
name = "limit"
version = "0.0.0"
[[package]]
name = "lock_api"
version = "0.4.4"

View File

@ -31,6 +31,7 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
mbe = { path = "../mbe", version = "0.0.0" }
cfg = { path = "../cfg", version = "0.0.0" }
tt = { path = "../tt", version = "0.0.0" }
limit = { path = "../limit", version = "0.0.0" }
[dev-dependencies]
test_utils = { path = "../test_utils" }

View File

@ -15,6 +15,7 @@
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId,
};
use la_arena::{Arena, ArenaMap};
use limit::Limit;
use profile::Count;
use rustc_hash::FxHashMap;
use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
@ -53,10 +54,10 @@ pub struct Expander {
}
#[cfg(test)]
const EXPANSION_RECURSION_LIMIT: usize = 32;
const EXPANSION_RECURSION_LIMIT: Limit = Limit::new(32);
#[cfg(not(test))]
const EXPANSION_RECURSION_LIMIT: usize = 128;
const EXPANSION_RECURSION_LIMIT: Limit = Limit::new(128);
impl CfgExpander {
pub(crate) fn new(
@ -99,7 +100,7 @@ pub fn enter_expand<T: ast::AstNode>(
db: &dyn DefDatabase,
macro_call: ast::MacroCall,
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
if EXPANSION_RECURSION_LIMIT.check(self.recursion_limit + 1).is_err() {
cov_mark::hit!(your_stack_belongs_to_me);
return Ok(ExpandResult::str_err(
"reached recursion limit during macro expansion".into(),

View File

@ -19,6 +19,7 @@
use hir_expand::{InFile, MacroCallLoc};
use itertools::Itertools;
use la_arena::Idx;
use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::ast;
@ -49,9 +50,9 @@
UnresolvedMacro,
};
const GLOB_RECURSION_LIMIT: usize = 100;
const EXPANSION_DEPTH_LIMIT: usize = 128;
const FIXED_POINT_LIMIT: usize = 8192;
const GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
const EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
const FIXED_POINT_LIMIT: Limit = Limit::new(8192);
pub(super) fn collect_defs(
db: &dyn DefDatabase,
@ -357,7 +358,7 @@ fn collect(&mut self) {
}
i += 1;
if i == FIXED_POINT_LIMIT {
if FIXED_POINT_LIMIT.check(i).is_err() {
log::error!("name resolution is stuck");
break 'outer;
}
@ -924,7 +925,7 @@ fn update_recursive(
import_type: ImportType,
depth: usize,
) {
if depth > GLOB_RECURSION_LIMIT {
if GLOB_RECURSION_LIMIT.check(depth).is_err() {
// prevent stack overflows (but this shouldn't be possible)
panic!("infinite recursion in glob imports!");
}
@ -1157,7 +1158,7 @@ fn collect_macro_expansion(
macro_call_id: MacroCallId,
depth: usize,
) {
if depth > EXPANSION_DEPTH_LIMIT {
if EXPANSION_DEPTH_LIMIT.check(depth).is_err() {
cov_mark::hit!(macro_expansion_overflow);
log::warn!("macro expansion is too deep");
return;

View File

@ -1,11 +1,12 @@
//! This module resolves `mod foo;` declaration to file.
use base_db::{AnchoredPath, FileId};
use hir_expand::name::Name;
use limit::Limit;
use syntax::SmolStr;
use crate::{db::DefDatabase, HirFileId};
const MOD_DEPTH_LIMIT: u32 = 32;
const MOD_DEPTH_LIMIT: Limit = Limit::new(32);
#[derive(Clone, Debug)]
pub(super) struct ModDir {
@ -25,7 +26,7 @@ pub(super) fn root() -> ModDir {
}
fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
let depth = self.depth + 1;
if depth > MOD_DEPTH_LIMIT {
if MOD_DEPTH_LIMIT.check(depth as usize).is_err() {
log::error!("MOD_DEPTH_LIMIT exceeded");
cov_mark::hit!(circular_mods);
return None;

View File

@ -21,6 +21,7 @@ parser = { path = "../parser", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" }
tt = { path = "../tt", version = "0.0.0" }
mbe = { path = "../mbe", version = "0.0.0" }
limit = { path = "../limit", version = "0.0.0" }
[dev-dependencies]
test_utils = { path = "../test_utils" }

View File

@ -3,6 +3,7 @@
use std::sync::Arc;
use base_db::{salsa, SourceDatabase};
use limit::Limit;
use mbe::{ExpandError, ExpandResult};
use parser::FragmentKind;
use syntax::{
@ -21,7 +22,7 @@
///
/// If an invocation produces more tokens than this limit, it will not be stored in the database and
/// an error will be emitted.
const TOKEN_LIMIT: usize = 524288;
const TOKEN_LIMIT: Limit = Limit::new(524288);
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {
@ -356,10 +357,12 @@ fn macro_expand_with_arg(
let ExpandResult { value: tt, err } = macro_rules.expand(db, id, &macro_arg.0);
// Set a hard limit for the expanded tt
let count = tt.count();
if count > TOKEN_LIMIT {
// XXX: Make ExpandResult a real error and use .map_err instead?
if TOKEN_LIMIT.check(count).is_err() {
return ExpandResult::str_err(format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count, TOKEN_LIMIT,
count,
TOKEN_LIMIT.inner(),
));
}

View File

@ -29,6 +29,7 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
base_db = { path = "../base_db", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" }
limit = { path = "../limit", version = "0.0.0" }
[dev-dependencies]
test_utils = { path = "../test_utils" }

View File

@ -9,6 +9,7 @@
use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
use hir_def::lang_item::LangItemTarget;
use hir_expand::name::name;
use limit::Limit;
use log::{info, warn};
use crate::{
@ -17,6 +18,8 @@
Ty, TyBuilder, TyKind,
};
const AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
pub(crate) enum AutoderefKind {
Builtin,
Overloaded,
@ -63,7 +66,7 @@ fn next(&mut self) -> Option<Self::Item> {
return Some((self.ty.clone(), 0));
}
if self.steps.len() >= AUTODEREF_RECURSION_LIMIT {
if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
return None;
}
@ -87,8 +90,6 @@ fn next(&mut self) -> Option<Self::Item> {
}
}
const AUTODEREF_RECURSION_LIMIT: usize = 10;
// FIXME: replace uses of this with Autoderef above
pub fn autoderef<'a>(
db: &'a dyn HirDatabase,
@ -99,7 +100,7 @@ pub fn autoderef<'a>(
successors(Some(ty), move |ty| {
deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() })
})
.take(AUTODEREF_RECURSION_LIMIT)
.take(AUTODEREF_RECURSION_LIMIT.inner())
}
pub(crate) fn deref(

View File

@ -65,7 +65,7 @@ pub(crate) fn replace_derive_with_manual_impl(
current_crate,
NameToImport::Exact(trait_name.to_string()),
items_locator::AssocItemSearch::Exclude,
Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
ModuleDef::Trait(trait_) => Some(trait_),

View File

@ -187,7 +187,7 @@ pub fn resolve_completion_edits(
current_crate,
NameToImport::Exact(imported_name),
items_locator::AssocItemSearch::Include,
Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|candidate| {
current_module

View File

@ -26,6 +26,7 @@ profile = { path = "../profile", version = "0.0.0" }
# ide should depend only on the top-level `hir` package. if you need
# something from some `hir_xxx` subpackage, reexport the API via `hir`.
hir = { path = "../hir", version = "0.0.0" }
limit = { path = "../limit", version = "0.0.0" }
[dev-dependencies]
test_utils = { path = "../test_utils" }

View File

@ -282,7 +282,7 @@ fn path_applicable_imports(
//
// see also an ignored test under FIXME comment in the qualify_path.rs module
AssocItemSearch::Exclude,
Some(DEFAULT_QUERY_SEARCH_LIMIT),
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|item| {
let mod_path = mod_path(item)?;
@ -299,7 +299,7 @@ fn path_applicable_imports(
current_crate,
path_candidate.name.clone(),
AssocItemSearch::Include,
Some(DEFAULT_QUERY_SEARCH_LIMIT),
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|item| {
import_for_item(
@ -445,7 +445,7 @@ fn trait_applicable_items(
current_crate,
trait_candidate.assoc_item_name.clone(),
AssocItemSearch::AssocItemsOnly,
Some(DEFAULT_QUERY_SEARCH_LIMIT),
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|input| item_as_assoc(db, input))
.filter_map(|assoc| {

View File

@ -7,6 +7,7 @@
import_map::{self, ImportKind},
AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
};
use limit::Limit;
use syntax::{ast, AstNode, SyntaxKind::NAME};
use crate::{
@ -17,7 +18,7 @@
};
/// A value to use, when uncertain which limit to pick.
pub const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
pub const DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
/// Three possible ways to search for the name in associated and/or other items.
#[derive(Debug, Clone, Copy)]

9
crates/limit/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "limit"
version = "0.0.0"
description = "TBD"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[dependencies]

31
crates/limit/src/lib.rs Normal file
View File

@ -0,0 +1,31 @@
//! limit defines a struct to enforce limits.
/// Represents a struct used to enforce a numerical limit.
pub struct Limit {
upper_bound: usize,
}
impl Limit {
/// Creates a new limit.
#[inline]
pub const fn new(upper_bound: usize) -> Self {
Self { upper_bound }
}
/// Gets the underlying numeric limit.
#[inline]
pub const fn inner(&self) -> usize {
self.upper_bound
}
/// Checks whether the given value is below the limit.
/// Returns `Ok` when `other` is below `self`, and `Err` otherwise.
#[inline]
pub const fn check(&self, other: usize) -> Result<(), ()> {
if other > self.upper_bound {
Err(())
} else {
Ok(())
}
}
}