diff --git a/kclvm/ast/src/ast.rs b/kclvm/ast/src/ast.rs index d5ff6d34b..492e8b513 100644 --- a/kclvm/ast/src/ast.rs +++ b/kclvm/ast/src/ast.rs @@ -680,6 +680,43 @@ pub enum Expr { Missing(MissingExpr), } +impl Expr { + pub fn get_expr_name(&self) -> String { + match self { + Expr::Identifier(_) => "IdentifierExpression", + Expr::Unary(_) => "UnaryExpression", + Expr::Binary(_) => "BinaryExpression", + Expr::If(_) => "IfExpression", + Expr::Selector(_) => "SelectorExpression", + Expr::Call(_) => "CallExpression", + Expr::Paren(_) => "ParenExpression", + Expr::Quant(_) => "QuantExpression", + Expr::List(_) => "ListExpression", + Expr::ListIfItem(_) => "ListIfItemExpression", + Expr::ListComp(_) => "ListCompExpression", + Expr::Starred(_) => "StarredExpression", + Expr::DictComp(_) => "DictCompExpression", + Expr::ConfigIfEntry(_) => "ConfigIfEntryExpression", + Expr::CompClause(_) => "CompClauseExpression", + Expr::Schema(_) => "SchemaExpression", + Expr::Config(_) => "ConfigExpression", + Expr::Check(_) => "CheckExpression", + Expr::Lambda(_) => "LambdaExpression", + Expr::Subscript(_) => "SubscriptExpression", + Expr::Keyword(_) => "KeywordExpression", + Expr::Arguments(_) => "ArgumentsExpression", + Expr::Compare(_) => "CompareExpression", + Expr::NumberLit(_) => "NumberLitExpression", + Expr::StringLit(_) => "StringLitExpression", + Expr::NameConstantLit(_) => "NameConstantLitExpression", + Expr::JoinedString(_) => "JoinedStringExpression", + Expr::FormattedValue(_) => "FormattedValueExpression", + Expr::Missing(_) => "MissingExpression", + } + .to_string() + } +} + /// Identifier, e.g. /// ```kcl /// a diff --git a/kclvm/sema/src/advanced_resolver/mod.rs b/kclvm/sema/src/advanced_resolver/mod.rs index 0c9b34e23..7c7b6d08e 100644 --- a/kclvm/sema/src/advanced_resolver/mod.rs +++ b/kclvm/sema/src/advanced_resolver/mod.rs @@ -1163,53 +1163,43 @@ mod tests { vec![(1, 0, 1, 2, "_b".to_string(), SymbolKind::Value)], ), ]; + let mut skip_def_info = false; for (filepath, symbols) in except_symbols.iter() { let abs_filepath = adjust_canonicalization(base_path.join(filepath)); - let file_sema_info = gs.sema_db.file_sema_map.get(&abs_filepath).unwrap(); - - let mut def_count = 0; - // symbols will be sorted according to their position in the file // now we check all symbols - for (index, symbol_ref) in file_sema_info.symbols.iter().enumerate() { - let symbol = gs.get_symbols().get_symbol(*symbol_ref).unwrap(); - let (start, end) = symbol.get_range(); + for (index, symbol_info) in symbols.iter().enumerate() { + if skip_def_info { + skip_def_info = false; + continue; + } + let (start_line, start_col, end_line, end_col, name, kind) = symbol_info; if abs_filepath.is_empty() { continue; } // test look up symbols let inner_pos = Position { filename: abs_filepath.clone(), - line: (start.line + end.line) / 2, - column: Some((start.column.unwrap_or(0) + end.column.unwrap_or(0)) / 2), - }; - let looked_symbol = gs.look_up_exact_symbol(&inner_pos); - assert_eq!(looked_symbol, Some(*symbol_ref)); - let out_pos = Position { - filename: abs_filepath.clone(), - line: (start.line + end.line) / 2 + 1, - column: Some(end.column.unwrap_or(0) + 1), + line: (start_line + end_line) / 2, + column: Some((start_col + end_col) / 2), }; - let looked_symbol = gs.look_up_exact_symbol(&out_pos); - assert_ne!(looked_symbol, Some(*symbol_ref)); - + let looked_symbol_ref = gs.look_up_exact_symbol(&inner_pos).unwrap(); + let looked_symbol = gs.get_symbols().get_symbol(looked_symbol_ref).unwrap(); + let (start, end) = looked_symbol.get_range(); // test symbol basic infomation - let (start_line, start_col, end_line, end_col, name, kind) = - symbols.get(index + def_count).unwrap(); assert_eq!(start.filename, abs_filepath); assert_eq!(start.line, *start_line); assert_eq!(start.column.unwrap_or(0), *start_col); assert_eq!(end.line, *end_line); assert_eq!(end.column.unwrap_or(0), *end_col); - assert_eq!(*name, symbol.get_name()); - assert_eq!(symbol_ref.get_kind(), *kind); + assert_eq!(*name, looked_symbol.get_name()); + assert_eq!(looked_symbol_ref.get_kind(), *kind); // test find def - if SymbolKind::Unresolved == symbol_ref.get_kind() { - def_count = def_count + 1; + if SymbolKind::Unresolved == looked_symbol_ref.get_kind() { let (start_line, start_col, end_line, end_col, path, kind) = - symbols.get(index + def_count).unwrap(); - let def_ref = symbol.get_definition().unwrap(); + symbols.get(index + 1).unwrap(); + let def_ref = looked_symbol.get_definition().unwrap(); let def = gs.get_symbols().get_symbol(def_ref).unwrap(); let (start, end) = def.get_range(); let def_filepath = adjust_canonicalization(base_path.join(path)); @@ -1221,6 +1211,7 @@ mod tests { assert_eq!(start.filename, def_filepath); } assert_eq!(def_ref.get_kind(), *kind); + skip_def_info = true; } } } diff --git a/kclvm/sema/src/advanced_resolver/node.rs b/kclvm/sema/src/advanced_resolver/node.rs index 3464cafca..4776a96fb 100644 --- a/kclvm/sema/src/advanced_resolver/node.rs +++ b/kclvm/sema/src/advanced_resolver/node.rs @@ -9,7 +9,9 @@ use kclvm_error::{diagnostic::Range, Position}; use crate::{ core::{ scope::LocalSymbolScopeKind, - symbol::{KCLSymbolSemanticInfo, SymbolRef, UnresolvedSymbol, ValueSymbol}, + symbol::{ + ExpressionSymbol, KCLSymbolSemanticInfo, SymbolRef, UnresolvedSymbol, ValueSymbol, + }, }, ty::{Type, SCHEMA_MEMBER_FUNCTIONS}, }; @@ -752,9 +754,25 @@ impl<'ctx> AdvancedResolver<'ctx> { self.ctx.end_pos = end; } self.ctx.cur_node = expr.id.clone(); - let result = self.walk_expr(&expr.node); - - result + match self.walk_expr(&expr.node) { + None => match self.ctx.node_ty_map.get(&self.ctx.get_node_key(&expr.id)) { + Some(ty) => { + let (_, end) = expr.get_span_pos(); + let mut expr_symbol = ExpressionSymbol::new( + format!("@{}", expr.node.get_expr_name()), + end.clone(), + end, + None, + ); + expr_symbol.sema_info.ty = Some(ty.clone()); + self.gs + .get_symbols_mut() + .alloc_expression_symbol(expr_symbol, self.ctx.get_node_key(&expr.id)) + } + None => None, + }, + some => some, + } } #[inline] diff --git a/kclvm/sema/src/core/global_state.rs b/kclvm/sema/src/core/global_state.rs index cf8e89371..c28156e73 100644 --- a/kclvm/sema/src/core/global_state.rs +++ b/kclvm/sema/src/core/global_state.rs @@ -400,6 +400,26 @@ impl GlobalState { }, ); } + + for (index, symbol) in self.symbols.exprs.iter() { + let symbol_ref = SymbolRef { + kind: SymbolKind::Expression, + id: index, + }; + let filename = symbol.start.filename.clone(); + if !file_sema_map.contains_key(&filename) { + file_sema_map.insert(filename.clone(), FileSemanticInfo::new(filename.clone())); + } + let file_sema_info = file_sema_map.get_mut(&filename).unwrap(); + file_sema_info.symbols.push(symbol_ref); + file_sema_info.symbol_locs.insert( + symbol_ref, + CachedLocation { + line: symbol.start.line, + column: symbol.start.column.unwrap_or(0), + }, + ); + } // remove dummy file file_sema_map.remove(""); diff --git a/kclvm/sema/src/core/symbol.rs b/kclvm/sema/src/core/symbol.rs index c83f2e212..1bb33a163 100644 --- a/kclvm/sema/src/core/symbol.rs +++ b/kclvm/sema/src/core/symbol.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use generational_arena::Arena; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use kclvm_error::{diagnostic::Range, Position}; @@ -63,12 +63,14 @@ pub struct KCLSymbolData { pub(crate) type_aliases: Arena, pub(crate) unresolved: Arena, pub(crate) rules: Arena, + pub(crate) exprs: Arena, pub(crate) symbols_info: SymbolDB, } #[derive(Default, Debug, Clone)] pub struct SymbolDB { + pub(crate) symbol_pos_set: IndexSet, pub(crate) global_builtin_symbols: IndexMap, pub(crate) fully_qualified_name_map: IndexMap, pub(crate) schema_builtin_symbols: IndexMap>, @@ -155,6 +157,10 @@ impl KCLSymbolData { .rules .get(id.get_id()) .map(|symbol| symbol as &KCLSymbol), + SymbolKind::Expression => self + .exprs + .get(id.get_id()) + .map(|symbol| symbol as &KCLSymbol), } } @@ -202,6 +208,12 @@ impl KCLSymbolData { symbol }); } + SymbolKind::Expression => { + self.exprs.get_mut(id.get_id()).map(|symbol| { + symbol.sema_info.ty = Some(ty); + symbol + }); + } } } @@ -466,6 +478,7 @@ impl KCLSymbolData { } pub fn alloc_schema_symbol(&mut self, schema: SchemaSymbol, node_key: NodeKey) -> SymbolRef { + self.symbols_info.symbol_pos_set.insert(schema.end.clone()); let symbol_id = self.schemas.insert(schema); let symbol_ref = SymbolRef { id: symbol_id, @@ -486,6 +499,9 @@ impl KCLSymbolData { unresolved: UnresolvedSymbol, node_key: NodeKey, ) -> SymbolRef { + self.symbols_info + .symbol_pos_set + .insert(unresolved.end.clone()); let symbol_id = self.unresolved.insert(unresolved); let symbol_ref = SymbolRef { id: symbol_id, @@ -506,6 +522,7 @@ impl KCLSymbolData { alias: TypeAliasSymbol, node_key: NodeKey, ) -> SymbolRef { + self.symbols_info.symbol_pos_set.insert(alias.end.clone()); let symbol_id = self.type_aliases.insert(alias); let symbol_ref = SymbolRef { id: symbol_id, @@ -522,6 +539,7 @@ impl KCLSymbolData { } pub fn alloc_rule_symbol(&mut self, rule: RuleSymbol, node_key: NodeKey) -> SymbolRef { + self.symbols_info.symbol_pos_set.insert(rule.end.clone()); let symbol_id = self.rules.insert(rule); let symbol_ref = SymbolRef { id: symbol_id, @@ -542,6 +560,9 @@ impl KCLSymbolData { attribute: AttributeSymbol, node_key: NodeKey, ) -> SymbolRef { + self.symbols_info + .symbol_pos_set + .insert(attribute.end.clone()); let symbol_id = self.attributes.insert(attribute); let symbol_ref = SymbolRef { id: symbol_id, @@ -558,6 +579,7 @@ impl KCLSymbolData { } pub fn alloc_value_symbol(&mut self, value: ValueSymbol, node_key: NodeKey) -> SymbolRef { + self.symbols_info.symbol_pos_set.insert(value.end.clone()); let symbol_id = self.values.insert(value); let symbol_ref = SymbolRef { id: symbol_id, @@ -572,6 +594,30 @@ impl KCLSymbolData { self.values.get_mut(symbol_id).unwrap().id = Some(symbol_ref); symbol_ref } + + pub fn alloc_expression_symbol( + &mut self, + expr: ExpressionSymbol, + node_key: NodeKey, + ) -> Option { + if self.symbols_info.symbol_pos_set.contains(&expr.end) { + return None; + } + self.symbols_info.symbol_pos_set.insert(expr.end.clone()); + let symbol_id = self.exprs.insert(expr); + let symbol_ref = SymbolRef { + id: symbol_id, + kind: SymbolKind::Expression, + }; + self.symbols_info + .node_symbol_map + .insert(node_key.clone(), symbol_ref); + self.symbols_info + .symbol_ref_map + .insert(symbol_ref, node_key); + self.exprs.get_mut(symbol_id).unwrap().id = Some(symbol_ref); + Some(symbol_ref) + } } #[allow(unused)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -583,6 +629,7 @@ pub enum SymbolKind { TypeAlias, Unresolved, Rule, + Expression, } #[allow(unused)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1558,3 +1605,123 @@ impl UnresolvedSymbol { pkg_path + "." + names.last().unwrap() } } + +#[derive(Debug, Clone)] +pub struct ExpressionSymbol { + pub(crate) id: Option, + pub(crate) start: Position, + pub(crate) end: Position, + pub(crate) owner: Option, + pub(crate) name: String, + + pub(crate) sema_info: KCLSymbolSemanticInfo, +} + +impl Symbol for ExpressionSymbol { + type SymbolData = KCLSymbolData; + type SemanticInfo = KCLSymbolSemanticInfo; + + fn is_global(&self) -> bool { + false + } + fn get_range(&self) -> Range { + (self.start.clone(), self.end.clone()) + } + + fn get_owner(&self) -> Option { + self.owner.clone() + } + + fn get_definition(&self) -> Option { + self.id + } + + fn get_name(&self) -> String { + self.name.clone() + } + + fn get_id(&self) -> Option { + self.id.clone() + } + + fn get_attribute( + &self, + name: &str, + data: &Self::SymbolData, + module_info: Option<&ModuleInfo>, + ) -> Option { + data.get_type_attribute(self.sema_info.ty.as_ref()?, name, module_info) + } + + fn get_all_attributes( + &self, + data: &Self::SymbolData, + module_info: Option<&ModuleInfo>, + ) -> Vec { + let mut result = vec![]; + if let Some(ty) = self.sema_info.ty.as_ref() { + if let Some(symbol_ref) = data.get_type_symbol(ty, module_info) { + if let Some(symbol) = data.get_symbol(symbol_ref) { + result.append(&mut symbol.get_all_attributes(data, module_info)) + } + } + } + + result + } + + fn has_attribute( + &self, + name: &str, + data: &Self::SymbolData, + module_info: Option<&ModuleInfo>, + ) -> bool { + self.get_attribute(name, data, module_info).is_some() + } + fn simple_dump(&self) -> String { + let mut output = "{\n".to_string(); + output.push_str("\"kind\": \"ExpressionSymbol\",\n"); + output.push_str(&format!( + "\"range\": \"{}:{}", + self.start.filename, self.start.line + )); + if let Some(start_col) = self.start.column { + output.push_str(&format!(":{}", start_col)); + } + + output.push_str(&format!(" to {}", self.end.line)); + if let Some(end_col) = self.end.column { + output.push_str(&format!(":{}", end_col)); + } + output.push_str("\"\n}"); + output + } + + fn full_dump(&self, data: &Self::SymbolData) -> Option { + let mut output = format!("{{\n\"simple_info\": {},\n", self.simple_dump()); + output.push_str("\"additional_info\": {\n"); + if let Some(owner) = self.owner.as_ref() { + let owner_symbol = data.get_symbol(*owner)?; + output.push_str(&format!("\"owner\": {}\n", owner_symbol.simple_dump())); + } + output.push_str("\n}\n}"); + Some(output) + } + + fn get_sema_info(&self) -> &Self::SemanticInfo { + &self.sema_info + } +} + +impl ExpressionSymbol { + pub fn new(name: String, start: Position, end: Position, owner: Option) -> Self { + Self { + id: None, + name, + start, + end, + sema_info: KCLSymbolSemanticInfo::default(), + owner, + } + } +} diff --git a/kclvm/sema/src/resolver/node.rs b/kclvm/sema/src/resolver/node.rs index 3042907c0..6b27bf96d 100644 --- a/kclvm/sema/src/resolver/node.rs +++ b/kclvm/sema/src/resolver/node.rs @@ -869,6 +869,10 @@ impl<'ctx> MutSelfTypedResultWalker<'ctx> for Resolver<'ctx> { ); let init_stack_depth = self.switch_config_expr_context(Some(obj)); self.expr(&schema_expr.config); + self.node_ty_map.insert( + self.get_node_key(schema_expr.config.id.clone()), + def_ty.clone(), + ); self.clear_config_expr_context(init_stack_depth as usize, false); if schema_ty.is_instance { if !schema_expr.args.is_empty() || !schema_expr.kwargs.is_empty() { diff --git a/kclvm/tools/src/LSP/src/document_symbol.rs b/kclvm/tools/src/LSP/src/document_symbol.rs index 51a59330e..872a92567 100644 --- a/kclvm/tools/src/LSP/src/document_symbol.rs +++ b/kclvm/tools/src/LSP/src/document_symbol.rs @@ -102,7 +102,7 @@ fn symbol_to_document_symbol(symbol: &KCLSymbol) -> Option { end: lsp_pos(&symbol_range.1), }; let kind = def.get_kind(); - let kind = symbol_kind_to_document_symbol_kind(kind); + let kind = symbol_kind_to_document_symbol_kind(kind)?; let detail = sema_info.ty.clone().map(|ty| ty.ty_str()); #[allow(deprecated)] @@ -121,15 +121,16 @@ fn symbol_to_document_symbol(symbol: &KCLSymbol) -> Option { } } -fn symbol_kind_to_document_symbol_kind(kind: KCLSymbolKind) -> SymbolKind { +fn symbol_kind_to_document_symbol_kind(kind: KCLSymbolKind) -> Option { match kind { - KCLSymbolKind::Schema => SymbolKind::STRUCT, - KCLSymbolKind::Attribute => SymbolKind::PROPERTY, - KCLSymbolKind::Value => SymbolKind::VARIABLE, - KCLSymbolKind::Package => SymbolKind::PACKAGE, - KCLSymbolKind::TypeAlias => SymbolKind::TYPE_PARAMETER, - KCLSymbolKind::Unresolved => SymbolKind::NULL, - KCLSymbolKind::Rule => SymbolKind::FUNCTION, + KCLSymbolKind::Schema => Some(SymbolKind::STRUCT), + KCLSymbolKind::Attribute => Some(SymbolKind::PROPERTY), + KCLSymbolKind::Value => Some(SymbolKind::VARIABLE), + KCLSymbolKind::Package => Some(SymbolKind::PACKAGE), + KCLSymbolKind::TypeAlias => Some(SymbolKind::TYPE_PARAMETER), + KCLSymbolKind::Unresolved => Some(SymbolKind::NULL), + KCLSymbolKind::Rule => Some(SymbolKind::FUNCTION), + KCLSymbolKind::Expression => None, } } diff --git a/kclvm/tools/src/LSP/src/semantic_token.rs b/kclvm/tools/src/LSP/src/semantic_token.rs index 6e5fa4552..59be78472 100644 --- a/kclvm/tools/src/LSP/src/semantic_token.rs +++ b/kclvm/tools/src/LSP/src/semantic_token.rs @@ -25,6 +25,9 @@ pub(crate) fn semantic_tokens_full(file: &str, gs: &GlobalState) -> Option Option type_index(SemanticTokenType::MACRO), + SymbolKind::Expression => unreachable!(), }; kcl_tokens.push(KCLSemanticToken { start: start.clone(),