Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
use hashbrown::HashMap;

use crate::{FileId, LuaAnalysisPhase, semantic::LuaInferCache};
use crate::{CacheOptions, FileId, LuaAnalysisPhase, semantic::LuaInferCache};

#[derive(Debug, Default)]
pub struct InferCacheManager {
infer_map: HashMap<FileId, LuaInferCache>,
cache_options: CacheOptions,
}

impl InferCacheManager {
pub fn new() -> Self {
pub fn new(cache_options: CacheOptions) -> Self {
InferCacheManager {
infer_map: HashMap::new(),
cache_options,
}
}

pub fn get_infer_cache(&mut self, file_id: FileId) -> &mut LuaInferCache {
self.infer_map.entry(file_id).or_insert_with(|| {
LuaInferCache::new(
file_id,
crate::CacheOptions {
analysis_phase: LuaAnalysisPhase::Ordered,
},
)
})
let mut cache_options = self.cache_options;
cache_options.analysis_phase = LuaAnalysisPhase::Ordered;
self.infer_map
.entry(file_id)
.or_insert_with(|| LuaInferCache::new(file_id, cache_options))
}

pub fn set_force(&mut self) {
Expand Down
24 changes: 15 additions & 9 deletions crates/emmylua_code_analysis/src/compilation/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod lua;
mod unresolve;

use crate::{
Emmyrc, FileId, InFiled, InferFailReason, LuaType, LuaTypeDeclId,
CacheOptions, Emmyrc, FileId, InFiled, InferFailReason, LuaType, LuaTypeDeclId,
db_index::{DbIndex, WorkspaceId},
profile::Profile,
};
Expand All @@ -27,12 +27,17 @@ where
lua::func_body::analyze_func_body_missing_return_flags_with(body, infer_expr_type)
}

pub fn analyze(db: &mut DbIndex, need_analyzed_files: Vec<InFiled<LuaChunk>>, config: Arc<Emmyrc>) {
pub fn analyze(
db: &mut DbIndex,
need_analyzed_files: Vec<InFiled<LuaChunk>>,
config: Arc<Emmyrc>,
cache_options: CacheOptions,
) {
if need_analyzed_files.is_empty() {
return;
}

let contexts = module_analyze(db, need_analyzed_files, config);
let contexts = module_analyze(db, need_analyzed_files, config, cache_options);

for (workspace_id, mut context) in contexts {
context.workspace_id = Some(workspace_id);
Expand All @@ -58,6 +63,7 @@ fn module_analyze(
db: &mut DbIndex,
need_analyzed_files: Vec<InFiled<LuaChunk>>,
config: Arc<Emmyrc>,
cache_options: CacheOptions,
) -> Vec<(WorkspaceId, AnalyzeContext)> {
if need_analyzed_files.len() == 1 {
let in_filed_tree = need_analyzed_files[0].clone();
Expand All @@ -75,11 +81,11 @@ fn module_analyze(
.get_module_index_mut()
.add_module_by_path(file_id, path_str);
let workspace_id = workspace_id.unwrap_or(WorkspaceId::MAIN);
let mut context = AnalyzeContext::new(config);
let mut context = AnalyzeContext::new(config, cache_options);
context.add_tree_chunk(in_filed_tree);
return vec![(workspace_id, context)];
} else if db.get_vfs().is_remote_file(&file_id) {
let mut context = AnalyzeContext::new(config);
let mut context = AnalyzeContext::new(config, cache_options);
context.add_tree_chunk(in_filed_tree);
return vec![(WorkspaceId::REMOTE, context)];
};
Expand Down Expand Up @@ -118,14 +124,14 @@ fn module_analyze(

let mut contexts = Vec::new();
if let Some(std_lib) = file_tree_map.remove(&WorkspaceId::STD) {
let mut context = AnalyzeContext::new(config.clone());
let mut context = AnalyzeContext::new(config.clone(), cache_options);
context.tree_list = std_lib;
contexts.push((WorkspaceId::STD, context));
}

let mut main_vec = Vec::new();
for (workspace_id, tree_list) in file_tree_map {
let mut context = AnalyzeContext::new(config.clone());
let mut context = AnalyzeContext::new(config.clone(), cache_options);
context.tree_list = tree_list;
if workspace_id.is_library() || workspace_id.is_remote() {
contexts.push((workspace_id, context));
Expand Down Expand Up @@ -153,13 +159,13 @@ pub struct AnalyzeContext {
}

impl AnalyzeContext {
pub fn new(emmyrc: Arc<Emmyrc>) -> Self {
pub fn new(emmyrc: Arc<Emmyrc>, cache_options: CacheOptions) -> Self {
Self {
tree_list: Vec::new(),
config: emmyrc,
metas: HashSet::new(),
unresolves: Vec::new(),
infer_manager: InferCacheManager::new(),
infer_manager: InferCacheManager::new(cache_options),
workspace_id: None,
pending_type_generic_headers: Vec::new(),
}
Expand Down
19 changes: 15 additions & 4 deletions crates/emmylua_code_analysis/src/compilation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ mod test;
use std::sync::Arc;

use crate::{
Emmyrc, FileId, InFiled, InferFailReason, LuaIndex, LuaInferCache, LuaType, db_index::DbIndex,
semantic::SemanticModel,
CacheOptions, Emmyrc, FileId, InFiled, InferFailReason, LuaIndex, LuaInferCache, LuaType,
db_index::DbIndex, semantic::SemanticModel,
};
use emmylua_parser::{LuaBlock, LuaExpr};

Expand All @@ -23,21 +23,23 @@ where
pub struct LuaCompilation {
db: DbIndex,
emmyrc: Arc<Emmyrc>,
cache_options: CacheOptions,
}

impl LuaCompilation {
pub fn new(emmyrc: Arc<Emmyrc>) -> Self {
let mut compilation = Self {
db: DbIndex::new(),
emmyrc: emmyrc.clone(),
cache_options: CacheOptions::default(),
};

compilation.db.update_config(emmyrc.clone());
compilation
}

pub fn get_semantic_model(&'_ self, file_id: FileId) -> Option<SemanticModel<'_>> {
let cache = LuaInferCache::new(file_id, Default::default());
let cache = LuaInferCache::new(file_id, self.cache_options);
let tree = self.db.get_vfs().get_syntax_tree(&file_id)?;
Some(SemanticModel::new(
file_id,
Expand All @@ -64,7 +66,12 @@ impl LuaCompilation {
});
}

analyzer::analyze(&mut self.db, need_analyzed_files, self.emmyrc.clone());
analyzer::analyze(
&mut self.db,
need_analyzed_files,
self.emmyrc.clone(),
self.cache_options,
);
}

pub fn remove_index(&mut self, file_ids: Vec<FileId>) {
Expand All @@ -83,6 +90,10 @@ impl LuaCompilation {
&mut self.db
}

pub fn get_cache_options_mut(&mut self) -> &mut CacheOptions {
&mut self.cache_options
}

pub fn update_config(&mut self, config: Arc<Emmyrc>) {
self.emmyrc = config.clone();
self.db.update_config(config);
Expand Down
48 changes: 48 additions & 0 deletions crates/emmylua_code_analysis/src/compilation/test/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod test {
const LARGE_LINEAR_ASSIGNMENT_STEPS: usize = 2048;
const MAXWELLHOME_ARRAY_VALUES: usize = 2048;
const ISSUE_1100_HIGHLIGHT_GROUPS: usize = 2048;
const ISSUE_1114_REPEATED_ASSIGNMENT_STEPS: usize = 512;

#[test]
fn test_closure_return() {
Expand Down Expand Up @@ -469,6 +470,53 @@ mod test {
assert_eq!(ws.humanize_type(after_assign), "integer");
}

#[test]
#[timeout(5000)]
fn test_issue_1114_repeated_self_dependent_assignments_build_semantic_model() {
let cases = [
(
"concat",
r#"local value = """#,
"value = value .. config.pic[idx][index]",
),
(
"add",
"local value = 0",
"value = value + config.pic[idx][index]",
),
(
"comparison",
"local value = true",
"value = value == config.pic[idx][index]",
),
];

for (name, init, assignment) in cases {
let mut ws = VirtualWorkspace::new();
let repeated_assignments =
format!("{assignment}\n").repeat(ISSUE_1114_REPEATED_ASSIGNMENT_STEPS);
let block = format!(
r#"
function f(idx, index)
{init}
{repeated_assignments}
return value
end
"#
);

let file_id = ws.def(&block);

assert!(
ws.analysis
.compilation
.get_semantic_model(file_id)
.is_some(),
"expected semantic model for repeated self-dependent {name} assignment"
);
}
}

#[test]
#[timeout(5000)]
fn test_issue_1094_self_call_fallback_stress() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct CacheOptions {
pub analysis_phase: LuaAnalysisPhase,
// Development/test escape hatch: keep flow inference unbounded so budget
// blowups can be reproduced instead of hidden behind `unknown`.
pub disable_flow_inference_step_budget: bool,
}

impl Default for CacheOptions {
fn default() -> Self {
Self {
analysis_phase: LuaAnalysisPhase::Ordered,
disable_flow_inference_step_budget: false,
}
}
}

#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub enum LuaAnalysisPhase {
// Ordered phase
Ordered,
Expand Down
Loading
Loading