Pārlūkot izejas kodu

Add missing Identifier locations to AST (#1580)

Adds missing Identifier locations to AST. 
This solves some of the issues currently faced when using Language
Server features like rename.

Signed-off-by: Govardhan G D <chioni1620@gmail.com>
Govardhan G D 2 gadi atpakaļ
vecāks
revīzija
8c4111b5b8

+ 8 - 8
src/abi/anchor.rs

@@ -166,7 +166,7 @@ fn idl_instructions(
         } else if func.mangled_name_contracts.contains(&contract_no) {
             func.mangled_name.clone()
         } else {
-            func.name.clone()
+            func.id.name.clone()
         };
 
         let accounts = func
@@ -251,7 +251,7 @@ impl TypeManager<'_> {
             name: name.clone(),
             docs: Some(vec![format!(
                 "Data structure to hold the multiple returns of function {}",
-                func.name
+                func.id
             )]),
             ty: IdlTypeDefinitionTy::Struct { fields },
             generics: None,
@@ -315,11 +315,11 @@ impl TypeManager<'_> {
             });
         }
 
-        let name = self.unique_custom_type_name(&def.name, &def.contract);
+        let name = self.unique_custom_type_name(&def.id.name, &def.contract);
 
         self.added_names.insert(
             name.clone(),
-            (self.types.len(), def.contract.clone(), def.name.clone()),
+            (self.types.len(), def.contract.clone(), def.id.name.clone()),
         );
 
         self.types.push(IdlTypeDefinition {
@@ -368,10 +368,10 @@ impl TypeManager<'_> {
 
         let docs = idl_docs(&def.tags);
 
-        let name = self.unique_custom_type_name(&def.name, &def.contract);
+        let name = self.unique_custom_type_name(&def.id.name, &def.contract);
         self.added_names.insert(
             name.clone(),
-            (self.types.len(), def.contract.clone(), def.name.clone()),
+            (self.types.len(), def.contract.clone(), def.id.name.clone()),
         );
 
         let variants = def
@@ -419,7 +419,7 @@ impl TypeManager<'_> {
             Type::Struct(struct_type) => {
                 let def = struct_type.definition(self.namespace);
                 self.add_struct_definition(def, ast_type);
-                IdlType::Defined(def.name.clone())
+                IdlType::Defined(def.id.name.clone())
             }
             Type::Array(ty, dims) => {
                 let mut idl_type = self.convert(ty);
@@ -442,7 +442,7 @@ impl TypeManager<'_> {
             Type::Bytes(dim) => IdlType::Array(Box::new(IdlType::U8), *dim as usize),
             Type::Enum(enum_no) => {
                 self.add_enum_definition(*enum_no, ast_type);
-                IdlType::Defined(self.namespace.enums[*enum_no].name.clone())
+                IdlType::Defined(self.namespace.enums[*enum_no].id.name.clone())
             }
             Type::ExternalFunction { .. } => {
                 self.convert(&Type::Struct(StructType::ExternalFunction))

+ 1 - 1
src/abi/ethereum.rs

@@ -85,7 +85,7 @@ pub fn gen_abi(contract_no: usize, ns: &Namespace) -> Vec<ABI> {
             None
         })
         .map(|func| ABI {
-            name: func.name.to_owned(),
+            name: func.id.name.to_owned(),
             mutability: format!("{}", func.mutability),
             ty: func.ty.to_string(),
             inputs: if func.ty == pt::FunctionTy::Function || func.ty == pt::FunctionTy::Constructor

+ 22 - 11
src/abi/polkadot.rs

@@ -197,7 +197,7 @@ fn resolve_ast(ty: &ast::Type, ns: &ast::Namespace, registry: &mut PortableRegis
                 })
                 .collect::<Vec<Field<PortableForm>>>();
             let c = TypeDefComposite::new(fields);
-            let path = path!(&def.name);
+            let path = path!(&def.id);
             let ty = Type::new(path, vec![], TypeDef::Composite(c), Default::default());
             registry.register_type(ty)
         }
@@ -215,7 +215,7 @@ fn resolve_ast(ty: &ast::Type, ns: &ast::Namespace, registry: &mut PortableRegis
                 })
                 .collect::<Vec<_>>();
             let variant = TypeDef::Variant(TypeDefVariant::new(variants));
-            let path = path!(&decl.name);
+            let path = path!(&decl.id);
             let ty = Type::new(path, vec![], variant, Default::default());
             registry.register_type(ty)
         }
@@ -329,13 +329,20 @@ pub fn gen_project(contract_no: usize, ns: &ast::Namespace) -> InkProject {
             })
             .collect::<Vec<MessageParamSpec<PortableForm>>>();
 
-        ConstructorSpec::from_label(if f.name.is_empty() { "new" } else { &f.name }.into())
-            .selector(f.selector(ns, &contract_no).try_into().unwrap())
-            .payable(payable)
-            .args(args)
-            .docs(vec![render(&f.tags).as_str()])
-            .returns(ReturnTypeSpec::new(None))
-            .done()
+        ConstructorSpec::from_label(
+            if f.id.name.is_empty() {
+                "new"
+            } else {
+                &f.id.name
+            }
+            .into(),
+        )
+        .selector(f.selector(ns, &contract_no).try_into().unwrap())
+        .payable(payable)
+        .args(args)
+        .docs(vec![render(&f.tags).as_str()])
+        .returns(ReturnTypeSpec::new(None))
+        .done()
     };
 
     let constructors = ns.contracts[contract_no]
@@ -384,7 +391,11 @@ pub fn gen_project(contract_no: usize, ns: &ast::Namespace) -> InkProject {
 
                 let t = TypeDefTuple::new_portable(fields);
 
-                let path = path!(&ns.contracts[contract_no].id.name, &f.name, "return_type");
+                let path = path!(
+                    &ns.contracts[contract_no].id.name,
+                    &f.id.name,
+                    "return_type"
+                );
 
                 let ty = registry.register_type(Type::new(
                     path,
@@ -413,7 +424,7 @@ pub fn gen_project(contract_no: usize, ns: &ast::Namespace) -> InkProject {
         let label = if f.mangled_name_contracts.contains(&contract_no) {
             &f.mangled_name
         } else {
-            &f.name
+            &f.id.name
         };
         MessageSpec::from_label(label.into())
             .selector(f.selector(ns, &contract_no).try_into().unwrap())

+ 3 - 3
src/bin/doc/mod.rs

@@ -216,7 +216,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
                 }
 
                 top.structs.push(StructDecl {
-                    name: &struct_decl.name,
+                    name: &struct_decl.id.name,
                     contract: struct_decl.contract.as_deref(),
                     title: get_tag("title", &struct_decl.tags),
                     notice: get_tag("notice", &struct_decl.tags),
@@ -241,7 +241,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
             }
 
             top.enums.push(EnumDecl {
-                name: &enum_decl.name,
+                name: &enum_decl.id.name,
                 contract: enum_decl.contract.as_deref(),
                 title: get_tag("title", &enum_decl.tags),
                 notice: get_tag("notice", &enum_decl.tags),
@@ -305,7 +305,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
                 }
 
                 Function {
-                    name: &func.name,
+                    name: &func.id.name,
                     ty: format!("{}", func.ty),
                     mutability: format!("{}", func.mutability),
                     base_contract,

+ 65 - 44
src/bin/languageserver/mod.rs

@@ -684,8 +684,9 @@ impl<'a> Builder<'a> {
                     }
                 ));
             }
-            ast::Expression::StructLiteral { loc, ty, values } => {
+            ast::Expression::StructLiteral { id: id_path, ty, values, .. } => {
                 if let Type::Struct(StructType::UserDefined(id)) = ty {
+                    let loc = id_path.identifiers.last().unwrap().loc;
                     self.references.push((
                         loc.file_no(),
                         ReferenceEntry {
@@ -698,8 +699,23 @@ impl<'a> Builder<'a> {
                         },
                     ));
                 }
-                for expr in values {
+
+                for (i, (field_name, expr)) in values.iter().enumerate() {
                     self.expression(expr, symtab);
+
+                    if let Some(pt::Identifier { loc: field_name_loc, ..}) = field_name {
+                        self.references.push((
+                            field_name_loc.file_no(),
+                            ReferenceEntry {
+                                start: field_name_loc.start(),
+                                stop: field_name_loc.exclusive_end(),
+                                val: DefinitionIndex {
+                                    def_path: Default::default(),
+                                    def_type: DefinitionType::Field(ty.clone(), i),
+                                },
+                            },
+                        ));
+                    }
                 }
             }
             ast::Expression::ArrayLiteral { values, .. }
@@ -1058,7 +1074,7 @@ impl<'a> Builder<'a> {
                 }
             }
 
-            ast::Expression::InternalFunction {loc, function_no, ..} => {
+            ast::Expression::InternalFunction {id, function_no, ..} => {
                 let fnc = &self.ns.functions[*function_no];
                 let mut msg_tg = render(&fnc.tags[..]);
                 if !msg_tg.is_empty() {
@@ -1077,21 +1093,23 @@ impl<'a> Builder<'a> {
 
                 let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].id)).unwrap_or_default();
 
-                let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.name, params, rets);
+                let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.id, params, rets);
+
+                let func_loc = id.identifiers.last().unwrap().loc;
 
                 self.hovers.push((
-                    loc.file_no(),
+                    func_loc.file_no(),
                     HoverEntry {
-                        start: loc.start(),
-                        stop: loc.exclusive_end(),
+                        start: func_loc.start(),
+                        stop: func_loc.exclusive_end(),
                         val: format!("{}{}", msg_tg, make_code_block(val)),
                     },
                 ));
                 self.references.push((
-                    loc.file_no(),
+                    func_loc.file_no(),
                     ReferenceEntry {
-                        start: loc.start(),
-                        stop: loc.exclusive_end(),
+                        start: func_loc.start(),
+                        stop: func_loc.exclusive_end(),
                         val: DefinitionIndex {
                             def_path: Default::default(),
                             def_type: DefinitionType::Function(*function_no),
@@ -1135,7 +1153,7 @@ impl<'a> Builder<'a> {
 
                 let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].id)).unwrap_or_default();
 
-                let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.name, params, rets);
+                let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.id, params, rets);
 
                 self.hovers.push((
                     loc.file_no(),
@@ -1314,14 +1332,14 @@ impl<'a> Builder<'a> {
                 ));
             }
         }
-
-        let file_no = field.loc.file_no();
+        let loc = field.id.as_ref().map(|id| &id.loc).unwrap_or(&field.loc);
+        let file_no = loc.file_no();
         let file = &self.ns.files[file_no];
         self.hovers.push((
             file_no,
             HoverEntry {
-                start: field.loc.start(),
-                stop: field.loc.exclusive_end(),
+                start: loc.start(),
+                stop: loc.exclusive_end(),
                 val: make_code_block(format!(
                     "{} {}",
                     field.ty.to_string(self.ns),
@@ -1337,8 +1355,7 @@ impl<'a> Builder<'a> {
                 field_id,
             ),
         };
-        self.definitions
-            .insert(di.clone(), loc_to_range(&field.loc, file));
+        self.definitions.insert(di.clone(), loc_to_range(loc, file));
         if let Some(dt) = get_type_definition(&field.ty) {
             self.types.insert(di, dt.into());
         }
@@ -1358,7 +1375,7 @@ impl<'a> Builder<'a> {
                         stop: loc.exclusive_end(),
                         val: make_code_block(format!(
                             "enum {}.{} {}",
-                            enum_decl.name, nam, discriminant
+                            enum_decl.id, nam, discriminant
                         )),
                     },
                 ));
@@ -1373,13 +1390,13 @@ impl<'a> Builder<'a> {
                 self.types.insert(di, dt.into());
             }
 
-            let file_no = enum_decl.loc.file_no();
+            let file_no = enum_decl.id.loc.file_no();
             let file = &self.ns.files[file_no];
             self.hovers.push((
                 file_no,
                 HoverEntry {
-                    start: enum_decl.loc.start(),
-                    stop: enum_decl.loc.start() + enum_decl.name.len(),
+                    start: enum_decl.id.loc.start(),
+                    stop: enum_decl.id.loc.exclusive_end(),
                     val: render(&enum_decl.tags[..]),
                 },
             ));
@@ -1388,23 +1405,23 @@ impl<'a> Builder<'a> {
                     def_path: file.path.clone(),
                     def_type: DefinitionType::Enum(ei),
                 },
-                loc_to_range(&enum_decl.loc, file),
+                loc_to_range(&enum_decl.id.loc, file),
             );
         }
 
         for (si, struct_decl) in self.ns.structs.iter().enumerate() {
-            if let pt::Loc::File(_, start, _) = &struct_decl.loc {
+            if matches!(struct_decl.loc, pt::Loc::File(_, _, _)) {
                 for (fi, field) in struct_decl.fields.iter().enumerate() {
                     self.field(si, fi, field);
                 }
 
-                let file_no = struct_decl.loc.file_no();
+                let file_no = struct_decl.id.loc.file_no();
                 let file = &self.ns.files[file_no];
                 self.hovers.push((
                     file_no,
                     HoverEntry {
-                        start: *start,
-                        stop: start + struct_decl.name.len(),
+                        start: struct_decl.id.loc.start(),
+                        stop: struct_decl.id.loc.exclusive_end(),
                         val: render(&struct_decl.tags[..]),
                     },
                 ));
@@ -1413,7 +1430,7 @@ impl<'a> Builder<'a> {
                         def_path: file.path.clone(),
                         def_type: DefinitionType::Struct(si),
                     },
-                    loc_to_range(&struct_decl.loc, file),
+                    loc_to_range(&struct_decl.id.loc, file),
                 );
             }
         }
@@ -1448,14 +1465,16 @@ impl<'a> Builder<'a> {
             }
 
             for (i, param) in func.params.iter().enumerate() {
+                let loc = param.id.as_ref().map(|id| &id.loc).unwrap_or(&param.loc);
                 self.hovers.push((
-                    param.loc.file_no(),
+                    loc.file_no(),
                     HoverEntry {
-                        start: param.loc.start(),
-                        stop: param.loc.exclusive_end(),
+                        start: loc.start(),
+                        stop: loc.exclusive_end(),
                         val: self.expanded_ty(&param.ty),
                     },
                 ));
+
                 if let Some(Some(var_no)) = func.symtable.arguments.get(i) {
                     if let Some(id) = &param.id {
                         let file_no = id.loc.file_no();
@@ -1471,13 +1490,14 @@ impl<'a> Builder<'a> {
                         }
                     }
                 }
-                if let Some(loc) = param.ty_loc {
+
+                if let Some(ty_loc) = param.ty_loc {
                     if let Some(dt) = get_type_definition(&param.ty) {
                         self.references.push((
-                            loc.file_no(),
+                            ty_loc.file_no(),
                             ReferenceEntry {
-                                start: loc.start(),
-                                stop: loc.exclusive_end(),
+                                start: ty_loc.start(),
+                                stop: ty_loc.exclusive_end(),
                                 val: dt.into(),
                             },
                         ));
@@ -1486,11 +1506,12 @@ impl<'a> Builder<'a> {
             }
 
             for (i, ret) in func.returns.iter().enumerate() {
+                let loc = ret.id.as_ref().map(|id| &id.loc).unwrap_or(&ret.loc);
                 self.hovers.push((
-                    ret.loc.file_no(),
+                    loc.file_no(),
                     HoverEntry {
-                        start: ret.loc.start(),
-                        stop: ret.loc.exclusive_end(),
+                        start: loc.start(),
+                        stop: loc.exclusive_end(),
                         val: self.expanded_ty(&ret.ty),
                     },
                 ));
@@ -1511,13 +1532,13 @@ impl<'a> Builder<'a> {
                     }
                 }
 
-                if let Some(loc) = ret.ty_loc {
+                if let Some(ty_loc) = ret.ty_loc {
                     if let Some(dt) = get_type_definition(&ret.ty) {
                         self.references.push((
-                            loc.file_no(),
+                            ty_loc.file_no(),
                             ReferenceEntry {
-                                start: loc.start(),
-                                stop: loc.exclusive_end(),
+                                start: ty_loc.start(),
+                                stop: ty_loc.exclusive_end(),
                                 val: dt.into(),
                             },
                         ));
@@ -1529,14 +1550,14 @@ impl<'a> Builder<'a> {
                 self.statement(stmt, &func.symtable);
             }
 
-            let file_no = func.loc.file_no();
+            let file_no = func.id.loc.file_no();
             let file = &self.ns.files[file_no];
             self.definitions.insert(
                 DefinitionIndex {
                     def_path: file.path.clone(),
                     def_type: DefinitionType::Function(i),
                 },
-                loc_to_range(&func.loc, file),
+                loc_to_range(&func.id.loc, file),
             );
         }
 
@@ -1663,7 +1684,7 @@ impl<'a> Builder<'a> {
                 self.field(ei, fi, field);
             }
 
-            let file_no = event.loc.file_no();
+            let file_no = event.id.loc.file_no();
             let file = &self.ns.files[file_no];
             self.hovers.push((
                 file_no,

+ 1 - 1
src/codegen/cfg.rs

@@ -1130,7 +1130,7 @@ impl ControlFlowGraph {
                     .map(|local| format!("%{}", self.vars[local].id.name))
                     .collect::<Vec<String>>()
                     .join(", "),
-                ns.functions[*ast_func_no].name,
+                ns.functions[*ast_func_no].id,
                 args.iter()
                     .map(|expr| self.expr_to_string(contract, ns, expr))
                     .collect::<Vec<String>>()

+ 4 - 2
src/codegen/expression.rs

@@ -336,12 +336,14 @@ pub fn expression(
             overflowing: *unchecked,
             expr: Box::new(expression(expr, cfg, contract_no, func, ns, vartab, opt)),
         },
-        ast::Expression::StructLiteral { loc, ty, values } => Expression::StructLiteral {
+        ast::Expression::StructLiteral {
+            loc, ty, values, ..
+        } => Expression::StructLiteral {
             loc: *loc,
             ty: ty.clone(),
             values: values
                 .iter()
-                .map(|e| expression(e, cfg, contract_no, func, ns, vartab, opt))
+                .map(|(_, e)| expression(e, cfg, contract_no, func, ns, vartab, opt))
                 .collect(),
         },
         ast::Expression::ArrayLiteral {

+ 1 - 1
src/codegen/mod.rs

@@ -368,7 +368,7 @@ trait LLVMName {
 impl LLVMName for Function {
     /// Return a unique string for this function which is a valid llvm symbol
     fn llvm_symbol(&self, ns: &Namespace) -> String {
-        let mut sig = self.name.to_owned();
+        let mut sig = self.id.name.to_owned();
 
         if !self.params.is_empty() {
             sig.push_str("__");

+ 1 - 1
src/codegen/solana_accounts/account_collection.rs

@@ -229,7 +229,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
                     _ => (),
                 }
             } else if let InternalCallTy::Builtin { ast_func_no } = call {
-                let name = &data.functions[*ast_func_no].name;
+                let name = &data.functions[*ast_func_no].id.name;
                 if name == "create_program_address" {
                     data.add_system_account();
                 }

+ 4 - 1
src/codegen/statements/mod.rs

@@ -1143,7 +1143,10 @@ impl Namespace {
     pub fn default_constructor(&self, contract_no: usize) -> Function {
         let mut func = Function::new(
             Codegen,
-            "".to_owned(),
+            pt::Identifier {
+                name: "".to_owned(),
+                loc: Codegen,
+            },
             Some(contract_no),
             vec![],
             pt::FunctionTy::Constructor,

+ 1 - 1
src/emit/polkadot/target.rs

@@ -1545,7 +1545,7 @@ impl<'a> TargetRuntime<'a> for PolkadotTarget {
     ) -> Option<BasicValueEnum<'a>> {
         emit_context!(binary);
 
-        match builtin_func.name.as_str() {
+        match builtin_func.id.name.as_str() {
             "chain_extension" => {
                 let input_ptr = binary.vector_bytes(args[1].into_pointer_value().into());
                 let input_len = binary.vector_len(args[1].into_pointer_value().into());

+ 2 - 2
src/emit/solana/target.rs

@@ -1254,7 +1254,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         first_arg_type: BasicTypeEnum,
         ns: &ast::Namespace,
     ) -> Option<BasicValueEnum<'a>> {
-        if builtin_func.name == "create_program_address" {
+        if builtin_func.id.name == "create_program_address" {
             let func = binary
                 .module
                 .get_function("sol_create_program_address")
@@ -1288,7 +1288,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
                 .left()
                 .unwrap();
             Some(ret)
-        } else if builtin_func.name == "try_find_program_address" {
+        } else if builtin_func.id.name == "try_find_program_address" {
             let func = binary
                 .module
                 .get_function("sol_try_find_program_address")

+ 22 - 14
src/sema/ast.rs

@@ -156,7 +156,7 @@ pub enum StructType {
 #[derive(PartialEq, Eq, Clone, Debug)]
 pub struct StructDecl {
     pub tags: Vec<Tag>,
-    pub name: String,
+    pub id: pt::Identifier,
     pub loc: pt::Loc,
     pub contract: Option<String>,
     pub fields: Vec<Parameter>,
@@ -211,8 +211,8 @@ impl fmt::Display for StructDecl {
     /// inside or outside a contract.
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match &self.contract {
-            Some(c) => write!(f, "{}.{}", c, self.name),
-            None => write!(f, "{}", self.name),
+            Some(c) => write!(f, "{}.{}", c, self.id),
+            None => write!(f, "{}", self.id),
         }
     }
 }
@@ -220,7 +220,7 @@ impl fmt::Display for StructDecl {
 #[derive(Debug)]
 pub struct EnumDecl {
     pub tags: Vec<Tag>,
-    pub name: String,
+    pub id: pt::Identifier,
     pub contract: Option<String>,
     pub loc: pt::Loc,
     pub ty: Type,
@@ -232,8 +232,8 @@ impl fmt::Display for EnumDecl {
     /// inside or outside a contract.
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match &self.contract {
-            Some(c) => write!(f, "{}.{}", c, self.name),
-            None => write!(f, "{}", self.name),
+            Some(c) => write!(f, "{}.{}", c, self.id),
+            None => write!(f, "{}", self.id),
         }
     }
 }
@@ -320,7 +320,7 @@ pub struct Function {
     pub tags: Vec<Tag>,
     /// The location of the prototype (not body)
     pub loc: pt::Loc,
-    pub name: String,
+    pub id: pt::Identifier,
     pub contract_no: Option<usize>,
     pub ty: pt::FunctionTy,
     pub signature: String,
@@ -402,7 +402,7 @@ impl FunctionAttributes for Function {
 impl Function {
     pub fn new(
         loc: pt::Loc,
-        name: String,
+        id: pt::Identifier,
         contract_no: Option<usize>,
         tags: Vec<Tag>,
         ty: pt::FunctionTy,
@@ -415,7 +415,7 @@ impl Function {
         let signature = match ty {
             pt::FunctionTy::Fallback => String::from("@fallback"),
             pt::FunctionTy::Receive => String::from("@receive"),
-            _ => ns.signature(&name, &params),
+            _ => ns.signature(&id.name, &params),
         };
 
         let mutability = match mutability {
@@ -437,7 +437,7 @@ impl Function {
         Function {
             tags,
             loc,
-            name,
+            id,
             contract_no,
             ty,
             signature,
@@ -473,7 +473,7 @@ impl Function {
                     let discriminator_image = if self.mangled_name_contracts.contains(contract_no) {
                         &self.mangled_name
                     } else {
-                        &self.name
+                        &self.id.name
                     };
                     function_discriminator(discriminator_image.as_str())
                 }
@@ -838,8 +838,10 @@ pub enum Expression {
     },
     StructLiteral {
         loc: pt::Loc,
+        id: pt::IdentifierPath,
         ty: Type,
-        values: Vec<Expression>,
+        /// pt::Identifier represents the field name
+        values: Vec<(Option<pt::Identifier>, Expression)>,
     },
     ArrayLiteral {
         loc: pt::Loc,
@@ -1130,6 +1132,7 @@ pub enum Expression {
     },
     InternalFunction {
         loc: pt::Loc,
+        id: pt::IdentifierPath,
         ty: Type,
         function_no: usize,
         signature: Option<String>,
@@ -1293,8 +1296,13 @@ impl Recurse for Expression {
     fn recurse<T>(&self, cx: &mut T, f: fn(expr: &Expression, ctx: &mut T) -> bool) {
         if f(self, cx) {
             match self {
-                Expression::StructLiteral { values, .. }
-                | Expression::ArrayLiteral { values, .. }
+                Expression::StructLiteral { values, .. } => {
+                    for (_, e) in values {
+                        e.recurse(cx, f);
+                    }
+                }
+
+                Expression::ArrayLiteral { values, .. }
                 | Expression::ConstArrayLiteral { values, .. } => {
                     for e in values {
                         e.recurse(cx, f);

+ 23 - 8
src/sema/builtin.rs

@@ -1555,7 +1555,10 @@ impl Namespace {
 
         let mut func = Function::new(
             pt::Loc::Builtin,
-            "create_program_address".to_string(),
+            pt::Identifier {
+                name: "create_program_address".to_string(),
+                loc: pt::Loc::Builtin,
+            },
             None,
             Vec::new(),
             pt::FunctionTy::Function,
@@ -1606,7 +1609,7 @@ impl Namespace {
 
         let func_no = self.functions.len();
         let id = Identifier {
-            name: func.name.to_owned(),
+            name: func.id.name.to_owned(),
             loc: pt::Loc::Builtin,
         };
 
@@ -1621,7 +1624,10 @@ impl Namespace {
 
         let mut func = Function::new(
             pt::Loc::Builtin,
-            "try_find_program_address".to_string(),
+            pt::Identifier {
+                name: "try_find_program_address".to_string(),
+                loc: pt::Loc::Builtin,
+            },
             None,
             Vec::new(),
             pt::FunctionTy::Function,
@@ -1685,7 +1691,7 @@ impl Namespace {
 
         let func_no = self.functions.len();
         let id = Identifier {
-            name: func.name.to_owned(),
+            name: func.id.name.to_owned(),
             loc: pt::Loc::Builtin,
         };
 
@@ -1736,7 +1742,10 @@ impl Namespace {
         for mut func in [
             Function::new(
                 loc,
-                "chain_extension".to_string(),
+                pt::Identifier {
+                    name: "chain_extension".to_string(),
+                    loc,
+                },
                 None,
                 Vec::new(),
                 pt::FunctionTy::Function,
@@ -1795,7 +1804,10 @@ impl Namespace {
             // is_contract API
             Function::new(
                 loc,
-                "is_contract".to_string(),
+                pt::Identifier {
+                    name: "is_contract".to_string(),
+                    loc,
+                },
                 None,
                 Vec::new(),
                 pt::FunctionTy::Function,
@@ -1828,7 +1840,10 @@ impl Namespace {
             // set_code_hash API
             Function::new(
                 loc,
-                "set_code_hash".to_string(),
+                pt::Identifier {
+                    name: "set_code_hash".to_string(),
+                    loc,
+                },
                 None,
                 Vec::new(),
                 pt::FunctionTy::Function,
@@ -1862,7 +1877,7 @@ impl Namespace {
         ] {
             func.has_body = true;
             let func_no = self.functions.len();
-            let id = identifier(&func.name);
+            let id = identifier(&func.id.name);
             self.functions.push(func);
             assert!(self.add_symbol(file_no, None, &id, Symbol::Function(vec![(loc, func_no)])));
         }

+ 12 - 3
src/sema/builtin_structs.rs

@@ -10,7 +10,10 @@ static BUILTIN_STRUCTS: Lazy<[StructDecl; 3]> = Lazy::new(|| {
             tags: Vec::new(),
             loc: pt::Loc::Builtin,
             contract: None,
-            name: "AccountInfo".to_string(),
+            id: pt::Identifier {
+                name: "AccountInfo".to_string(),
+                loc: pt::Loc::Builtin,
+            },
             fields: vec![
                 Parameter {
                     loc: pt::Loc::Builtin,
@@ -132,7 +135,10 @@ static BUILTIN_STRUCTS: Lazy<[StructDecl; 3]> = Lazy::new(|| {
             tags: Vec::new(),
             loc: pt::Loc::Builtin,
             contract: None,
-            name: "AccountMeta".to_string(),
+            id: pt::Identifier {
+                name: "AccountMeta".to_string(),
+                loc: pt::Loc::Builtin,
+            },
             fields: vec![
                 Parameter {
                     loc: pt::Loc::Builtin,
@@ -182,7 +188,10 @@ static BUILTIN_STRUCTS: Lazy<[StructDecl; 3]> = Lazy::new(|| {
         },
         StructDecl {
             tags: Vec::new(),
-            name: "ExternalFunction".to_string(),
+            id: pt::Identifier {
+                name: "ExternalFunction".to_string(),
+                loc: pt::Loc::Builtin,
+            },
             loc: pt::Loc::Builtin,
             contract: None,
             fields: vec![

+ 40 - 44
src/sema/contracts.rs

@@ -350,7 +350,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                                 loc: func.loc,
                                 message: format!(
                                     "function '{}' is not specified 'virtual'",
-                                    func.name
+                                    func.id
                                 ),
                             })
                         }
@@ -362,7 +362,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                         cur.loc,
                         format!(
                             "function '{}' overrides functions which are not 'virtual'",
-                            cur.name
+                            cur.id
                         ),
                         non_virtual,
                     ));
@@ -380,7 +380,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                             *loc,
                             format!(
                                 "function '{}' should specify override list 'override({})'",
-                                cur.name, source_override
+                                cur.id, source_override
                             ),
                         ));
                     } else {
@@ -400,7 +400,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                                 *loc,
                                 format!(
                                     "function '{}' missing overrides '{}', specify 'override({})'",
-                                    cur.name,
+                                    cur.id,
                                     missing.join(","),
                                     source_override
                                 ),
@@ -418,7 +418,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                                 *loc,
                                 format!(
                                     "function '{}' includes extraneous overrides '{}', specify 'override({})'",
-                                    cur.name,
+                                    cur.id,
                                     extra.join(","),
                                     source_override
                                 ),
@@ -442,7 +442,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                     if !ns.contracts[base_contract_no].is_interface() {
                         diagnostics.push(ast::Diagnostic::error(
                             cur.loc,
-                            format!("function '{}' should specify 'override'", cur.name),
+                            format!("function '{}' should specify 'override'", cur.id),
                         ));
                     }
 
@@ -456,7 +456,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                         cur.loc,
                         format!(
                             "function '{}' should specify override list 'override({})'",
-                            cur.name, source_override
+                            cur.id, source_override
                         ),
                     ));
                 }
@@ -475,7 +475,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                 if previous_defs.is_empty() && cur.is_override.is_some() {
                     diagnostics.push(ast::Diagnostic::error(
                         cur.loc,
-                        format!("'{}' does not override anything", cur.name),
+                        format!("'{}' does not override anything", cur.id),
                     ));
                     continue;
                 }
@@ -496,12 +496,9 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                     if Some(base_contract_no) == func_prev.contract_no {
                         diagnostics.push(ast::Diagnostic::error_with_note(
                             cur.loc,
-                            format!(
-                                "function '{}' overrides function in same contract",
-                                cur.name
-                            ),
+                            format!("function '{}' overrides function in same contract", cur.id),
                             func_prev.loc,
-                            format!("previous definition of '{}'", func_prev.name),
+                            format!("previous definition of '{}'", func_prev.id),
                         ));
 
                         continue;
@@ -510,9 +507,9 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                     if func_prev.ty != cur.ty {
                         diagnostics.push(ast::Diagnostic::error_with_note(
                             cur.loc,
-                            format!("{} '{}' overrides {}", cur.ty, cur.name, func_prev.ty,),
+                            format!("{} '{}' overrides {}", cur.ty, cur.id, func_prev.ty,),
                             func_prev.loc,
-                            format!("previous definition of '{}'", func_prev.name),
+                            format!("previous definition of '{}'", func_prev.id),
                         ));
 
                         continue;
@@ -528,10 +525,10 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                             cur.loc,
                             format!(
                                 "{} '{}' overrides {} with different argument types",
-                                cur.ty, cur.name, func_prev.ty,
+                                cur.ty, cur.id, func_prev.ty,
                             ),
                             func_prev.loc,
-                            format!("previous definition of '{}'", func_prev.name),
+                            format!("previous definition of '{}'", func_prev.id),
                         ));
 
                         continue;
@@ -547,10 +544,10 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                             cur.loc,
                             format!(
                                 "{} '{}' overrides {} with different return types",
-                                cur.ty, cur.name, func_prev.ty,
+                                cur.ty, cur.id, func_prev.ty,
                             ),
                             func_prev.loc,
-                            format!("previous definition of '{}'", func_prev.name),
+                            format!("previous definition of '{}'", func_prev.id),
                         ));
 
                         continue;
@@ -567,10 +564,10 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                                 cur.loc,
                                 format!(
                                     "function '{}' overrides function which is not virtual",
-                                    cur.name
+                                    cur.id
                                 ),
                                 func_prev.loc,
-                                format!("previous definition of function '{}'", func_prev.name),
+                                format!("previous definition of function '{}'", func_prev.id),
                             ));
 
                             continue;
@@ -581,10 +578,10 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                                 *loc,
                                 format!(
                                     "function '{}' override list does not contain '{}'",
-                                    cur.name, ns.contracts[prev_contract_no].id
+                                    cur.id, ns.contracts[prev_contract_no].id
                                 ),
                                 func_prev.loc,
-                                format!("previous definition of function '{}'", func_prev.name),
+                                format!("previous definition of function '{}'", func_prev.id),
                             ));
                             continue;
                         }
@@ -643,10 +640,10 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                     loc,
                     format!(
                         "contract '{}' missing override for function '{}'",
-                        ns.contracts[contract_no].id, func.name
+                        ns.contracts[contract_no].id, func.id
                     ),
                     func.loc,
-                    format!("declaration of function '{}'", func.name),
+                    format!("declaration of function '{}'", func.id),
                 )),
             }
 
@@ -660,17 +657,14 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                 let func = &ns.functions[*function_no];
                 ast::Note {
                     loc: func.loc,
-                    message: format!("previous definition of function '{}'", func.name),
+                    message: format!("previous definition of function '{}'", func.id),
                 }
             })
             .collect();
 
         diagnostics.push(ast::Diagnostic::error_with_notes(
             func.loc,
-            format!(
-                "function '{}' with this signature already defined",
-                func.name
-            ),
+            format!("function '{}' with this signature already defined", func.id),
             notes,
         ));
     }
@@ -700,12 +694,12 @@ fn check_mangled_function_names(contract_no: usize, ns: &mut ast::Namespace) {
     for f in &public_functions {
         if let Some(offender) = public_functions
             .iter()
-            .find(|other| ns.functions[*f].mangled_name == ns.functions[**other].name)
+            .find(|other| ns.functions[*f].mangled_name == ns.functions[**other].id.name)
         {
             let f = &ns.functions[*f];
             let message = format!(
                 "mangling the symbol of overloaded function '{}' with signature '{}' results in a new symbol '{}' but this symbol already exists",
-                &f.name, &f.signature, f.mangled_name
+                &f.id, &f.signature, f.mangled_name
             );
             ns.diagnostics.push(ast::Diagnostic::error_with_note(
                 f.loc,
@@ -760,9 +754,9 @@ fn unique_constructor_names(contract_no: usize, ns: &mut ast::Namespace) {
         if let Some(offender) = functions.insert(&func.mangled_name, *f) {
             ns.diagnostics.push(ast::Diagnostic::error_with_note(
                 func.loc,
-                format!("Non unique function or constructor name '{}'", &func.name),
+                format!("Non unique function or constructor name '{}'", &func.id),
                 ns.functions[offender].loc,
-                format!("previous declaration of '{}'", &ns.functions[offender].name),
+                format!("previous declaration of '{}'", &ns.functions[offender].id),
             ))
         }
     }
@@ -782,7 +776,7 @@ fn base_function_compatible(
             func.loc,
             format!(
                 "mutability '{}' of function '{}' is not compatible with mutability '{}'",
-                func.mutability, func.name, base.mutability
+                func.mutability, func.id, base.mutability
             ),
             base.loc,
             String::from("location of base function"),
@@ -794,7 +788,7 @@ fn base_function_compatible(
             func.loc,
             format!(
                 "visibility '{}' of function '{}' is not compatible with visibility '{}'",
-                func.visibility, func.name, base.visibility
+                func.visibility, func.id, base.visibility
             ),
             base.loc,
             String::from("location of base function"),
@@ -808,7 +802,7 @@ fn base_function_compatible(
                 cur_selector.0,
                 format!(
                     "selector of function '{}' different from base selector",
-                    func.name,
+                    func.id,
                 ),
                 func_selector.0,
                 String::from("location of base function"),
@@ -819,7 +813,7 @@ fn base_function_compatible(
                 func.loc,
                 format!(
                     "selector of function '{}' must match base selector",
-                    func.name,
+                    func.id,
                 ),
                 func_selector.0,
                 String::from("location of base function"),
@@ -830,7 +824,7 @@ fn base_function_compatible(
                 cur_selector.0,
                 format!(
                     "base function needs same selector as selector of function '{}'",
-                    func.name,
+                    func.id,
                 ),
                 base.loc,
                 String::from("location of base function"),
@@ -1015,7 +1009,7 @@ fn resolve_declarations<'a>(
                     loc: ns.functions[function_no].loc,
                     message: format!(
                         "location of function '{}' with no body",
-                        ns.functions[function_no].name
+                        ns.functions[function_no].id
                     ),
                 })
                 .collect::<Vec<ast::Note>>();
@@ -1276,7 +1270,9 @@ fn mangle_function_names(contract_no: usize, ns: &mut Namespace) {
             continue;
         }
 
-        if let Some(old_no) = repeated_names.insert(ns.functions[*func_no].name.clone(), *func_no) {
+        if let Some(old_no) =
+            repeated_names.insert(ns.functions[*func_no].id.name.clone(), *func_no)
+        {
             ns.functions[old_no]
                 .mangled_name_contracts
                 .insert(contract_no);
@@ -1309,7 +1305,7 @@ fn verify_unique_selector(contract_no: usize, ns: &mut Namespace) {
                 *loc,
                 format!(
                     "function '{}' selector must be {} bytes rather than {} bytes",
-                    func.name,
+                    func.id,
                     selector_len,
                     selector.len()
                 ),
@@ -1328,10 +1324,10 @@ fn verify_unique_selector(contract_no: usize, ns: &mut Namespace) {
                     func.loc,
                     format!(
                         "{} '{}' selector is the same as {} '{}'",
-                        func.ty, func.name, other.ty, other.name
+                        func.ty, func.id, other.ty, other.id
                     ),
                     other.loc,
-                    format!("definition of {} '{}'", other.ty, other.name),
+                    format!("definition of {} '{}'", other.ty, other.id),
                 ));
             }
         } else {

+ 16 - 13
src/sema/dotgraphviz.rs

@@ -129,7 +129,7 @@ impl Dot {
 
     fn add_function(&mut self, func: &Function, ns: &Namespace, parent: usize) {
         let mut labels = vec![
-            format!("{} {}", func.ty, func.name),
+            format!("{} {}", func.ty, func.id),
             ns.loc_to_string(PathDisplay::FullPath, &func.loc),
         ];
 
@@ -163,7 +163,7 @@ impl Dot {
         }
 
         let func_node = self.add_node(
-            Node::new(&func.name, labels),
+            Node::new(&func.id.name, labels),
             Some(parent),
             Some(format!("{}", func.ty)),
         );
@@ -337,7 +337,9 @@ impl Dot {
                     Some(parent_rel),
                 );
             }
-            Expression::StructLiteral { loc, ty, values } => {
+            Expression::StructLiteral {
+                loc, ty, values, ..
+            } => {
                 let labels = vec![
                     format!("struct literal: {}", ty.to_string(ns)),
                     ns.loc_to_string(PathDisplay::FullPath, loc),
@@ -349,7 +351,7 @@ impl Dot {
                     Some(parent_rel),
                 );
 
-                for (no, arg) in values.iter().enumerate() {
+                for (no, (_, arg)) in values.iter().enumerate() {
                     self.add_expression(arg, func, ns, node, format!("arg #{no}"));
                 }
             }
@@ -1180,6 +1182,7 @@ impl Dot {
                 ty,
                 function_no,
                 signature,
+                ..
             } => {
                 let mut labels = vec![
                     ty.to_string(ns),
@@ -1189,9 +1192,9 @@ impl Dot {
                 let func = &ns.functions[*function_no];
 
                 if let Some(contract_no) = func.contract_no {
-                    labels.insert(1, format!("{}.{}", ns.contracts[contract_no].id, func.name))
+                    labels.insert(1, format!("{}.{}", ns.contracts[contract_no].id, func.id))
                 } else {
-                    labels.insert(1, format!("free function {}", func.name))
+                    labels.insert(1, format!("free function {}", func.id))
                 }
 
                 if let Some(signature) = signature {
@@ -1218,7 +1221,7 @@ impl Dot {
                 let f = &ns.functions[*function_no];
 
                 if let Some(contract_no) = f.contract_no {
-                    labels.insert(1, format!("{}.{}", ns.contracts[contract_no].id, f.name))
+                    labels.insert(1, format!("{}.{}", ns.contracts[contract_no].id, f.id))
                 }
 
                 let node = self.add_node(
@@ -1388,7 +1391,7 @@ impl Dot {
                     ),
                     format!(
                         "function {} {}",
-                        user_func.name,
+                        user_func.id,
                         ns.loc_to_string(PathDisplay::FullPath, &user_func.loc)
                     ),
                 ];
@@ -2428,9 +2431,9 @@ impl Namespace {
                 if let Some(contract) = &decl.contract {
                     labels.insert(0, format!("contract: {contract}"));
                 }
-                labels.insert(0, format!("name: {}", decl.name));
+                labels.insert(0, format!("name: {}", decl.id));
 
-                let e = Node::new(&decl.name, labels);
+                let e = Node::new(&decl.id.name, labels);
 
                 let node = dot.add_node(e, Some(enums), None);
 
@@ -2445,7 +2448,7 @@ impl Namespace {
             for decl in &self.structs {
                 if let pt::Loc::File(..) = &decl.loc {
                     let mut labels = vec![
-                        format!("name:{}", decl.name),
+                        format!("name:{}", decl.id),
                         self.loc_to_string(PathDisplay::FullPath, &decl.loc),
                     ];
 
@@ -2461,7 +2464,7 @@ impl Namespace {
                         ));
                     }
 
-                    let e = Node::new(&decl.name, labels);
+                    let e = Node::new(&decl.id.name, labels);
 
                     let node = dot.add_node(e, Some(structs), None);
 
@@ -2648,7 +2651,7 @@ impl Namespace {
 
                             let mut label = format!(
                                 "function {} {}",
-                                func.name,
+                                func.id,
                                 self.loc_to_string(PathDisplay::FullPath, &func.loc)
                             );
 

+ 62 - 23
src/sema/expression/function_call.rs

@@ -203,7 +203,7 @@ pub fn available_functions(
             ns.contracts[contract_no]
                 .all_functions
                 .keys()
-                .filter(|func_no| ns.functions[**func_no].name == name)
+                .filter(|func_no| ns.functions[**func_no].id.name == name)
                 .filter_map(|func_no| {
                     let is_abstract = ns.functions[*func_no].is_virtual
                         && !ns.contracts[contract_no].is_concrete();
@@ -234,7 +234,7 @@ pub fn available_super_functions(name: &str, contract_no: usize, ns: &Namespace)
                 .filter_map(|func_no| {
                     let func = &ns.functions[*func_no];
 
-                    if func.name == name && func.has_body {
+                    if func.id.name == name && func.has_body {
                         Some(*func_no)
                     } else {
                         None
@@ -249,7 +249,7 @@ pub fn available_super_functions(name: &str, contract_no: usize, ns: &Namespace)
 /// Resolve a function call with positional arguments
 pub fn function_call_pos_args(
     loc: &pt::Loc,
-    id: &pt::Identifier,
+    id: &pt::IdentifierPath,
     func_ty: pt::FunctionTy,
     args: &[pt::Expression],
     function_nos: Vec<usize>,
@@ -317,6 +317,7 @@ pub fn function_call_pos_args(
 
         match resolve_internal_call(
             loc,
+            id,
             *function_no,
             context,
             resolve_to,
@@ -330,6 +331,7 @@ pub fn function_call_pos_args(
         }
     }
 
+    let id = id.identifiers.last().unwrap();
     match name_matches {
         0 => {
             if func_ty == pt::FunctionTy::Modifier {
@@ -359,7 +361,7 @@ pub fn function_call_pos_args(
 /// Resolve a function call with named arguments
 pub(super) fn function_call_named_args(
     loc: &pt::Loc,
-    id: &pt::Identifier,
+    id: &pt::IdentifierPath,
     args: &[pt::NamedArgument],
     function_nos: Vec<usize>,
     virtual_call: bool,
@@ -412,7 +414,7 @@ pub(super) fn function_call_named_args(
                     "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
                 ),
                 func.loc,
-                format!("definition of {}", func.name),
+                format!("definition of {}", func.id),
             ));
             matches = false;
         } else if params_len != args.len() {
@@ -444,7 +446,7 @@ pub(super) fn function_call_named_args(
                         format!(
                             "missing argument '{}' to function '{}'",
                             param.name_as_str(),
-                            id.name,
+                            id.identifiers.last().unwrap().name,
                         ),
                     ));
                     continue;
@@ -466,6 +468,7 @@ pub(super) fn function_call_named_args(
 
         match resolve_internal_call(
             loc,
+            id,
             *function_no,
             context,
             resolve_to,
@@ -481,6 +484,7 @@ pub(super) fn function_call_named_args(
 
     match function_nos.len() {
         0 => {
+            let id = id.identifiers.last().unwrap();
             diagnostics.push(Diagnostic::error(
                 id.loc,
                 format!("unknown function or type '{}'", id.name),
@@ -551,6 +555,11 @@ fn try_namespace(
             )?));
         }
 
+        let id_path = pt::IdentifierPath {
+            loc: *loc,
+            identifiers: vec![namespace.clone(), func.clone()],
+        };
+
         // is it a call to super
         if namespace.name == "super" {
             if let Some(cur_contract_no) = context.contract_no {
@@ -564,7 +573,7 @@ fn try_namespace(
 
                 return Ok(Some(function_call_pos_args(
                     loc,
-                    func,
+                    &id_path,
                     pt::FunctionTy::Function,
                     args,
                     available_super_functions(&func.name, cur_contract_no, ns),
@@ -597,7 +606,7 @@ fn try_namespace(
 
                 return Ok(Some(function_call_pos_args(
                     loc,
-                    func,
+                    &id_path,
                     pt::FunctionTy::Function,
                     args,
                     available_functions(
@@ -644,7 +653,7 @@ fn try_namespace(
 
                     return Ok(Some(function_call_pos_args(
                         loc,
-                        func,
+                        &id_path,
                         pt::FunctionTy::Function,
                         args,
                         available_functions(
@@ -1375,6 +1384,7 @@ pub(super) fn method_call_pos_args(
     }
 
     if let Some(mut path) = ns.expr_to_identifier_path(var) {
+        // `path.loc` needs to be modified `func.loc`
         path.identifiers.push(func.clone());
 
         if let Ok(list) = ns.resolve_function_with_namespace(
@@ -1412,7 +1422,7 @@ pub(super) fn method_call_pos_args(
 
             return function_call_pos_args(
                 loc,
-                func,
+                &path,
                 pt::FunctionTy::Function,
                 args,
                 list.iter().map(|(_, no)| *no).collect(),
@@ -1528,6 +1538,11 @@ pub(super) fn method_call_named_args(
     resolve_to: ResolveTo,
 ) -> Result<Expression, ()> {
     if let pt::Expression::Variable(namespace) = var {
+        let id_path = pt::IdentifierPath {
+            loc: *loc,
+            identifiers: vec![namespace.clone(), func_name.clone()],
+        };
+
         // is it a call to super
         if namespace.name == "super" {
             if let Some(cur_contract_no) = context.contract_no {
@@ -1541,7 +1556,7 @@ pub(super) fn method_call_named_args(
 
                 return function_call_named_args(
                     loc,
-                    func_name,
+                    &id_path,
                     args,
                     available_super_functions(&func_name.name, cur_contract_no, ns),
                     false,
@@ -1573,7 +1588,7 @@ pub(super) fn method_call_named_args(
 
                 return function_call_named_args(
                     loc,
-                    func_name,
+                    &id_path,
                     args,
                     available_functions(
                         &func_name.name,
@@ -1619,7 +1634,7 @@ pub(super) fn method_call_named_args(
 
                     return function_call_named_args(
                         loc,
-                        func_name,
+                        &id_path,
                         args,
                         available_functions(
                             &func_name.name,
@@ -1664,6 +1679,7 @@ pub(super) fn method_call_named_args(
     }
 
     if let Some(mut path) = ns.expr_to_identifier_path(var) {
+        // `path.loc` needs to be modified to include `func_name.loc`
         path.identifiers.push(func_name.clone());
 
         if let Ok(list) = ns.resolve_function_with_namespace(
@@ -1699,7 +1715,7 @@ pub(super) fn method_call_named_args(
 
             return function_call_named_args(
                 loc,
-                func_name,
+                &path,
                 args,
                 list.iter().map(|(_, no)| *no).collect(),
                 false,
@@ -2125,7 +2141,17 @@ pub fn named_call_expr(
         &mut nullsink,
     ) {
         Ok(Type::Struct(str_ty)) => {
-            return named_struct_literal(loc, &str_ty, args, context, ns, symtable, diagnostics);
+            let id = ns.expr_to_identifier_path(ty).unwrap();
+            return named_struct_literal(
+                loc,
+                id,
+                &str_ty,
+                args,
+                context,
+                ns,
+                symtable,
+                diagnostics,
+            );
         }
         Ok(_) => {
             diagnostics.push(Diagnostic::error(
@@ -2192,7 +2218,8 @@ pub fn call_expr(
         &mut nullsink,
     ) {
         Ok(Type::Struct(str_ty)) => {
-            return struct_literal(loc, &str_ty, args, context, ns, symtable, diagnostics);
+            let id = ns.expr_to_identifier_path(ty).unwrap();
+            return struct_literal(loc, id, &str_ty, args, context, ns, symtable, diagnostics);
         }
         Ok(to) => {
             // Cast
@@ -2349,9 +2376,14 @@ pub fn function_call_expr(
                     return Err(());
                 }
 
+                let id_path = pt::IdentifierPath {
+                    loc: id.loc,
+                    identifiers: vec![id.clone()],
+                };
+
                 function_call_pos_args(
                     loc,
-                    id,
+                    &id_path,
                     pt::FunctionTy::Function,
                     args,
                     available_functions(&id.name, true, context.file_no, context.contract_no, ns),
@@ -2415,9 +2447,14 @@ pub fn named_function_call_expr(
                 return Err(());
             }
 
+            let id_path = pt::IdentifierPath {
+                loc: id.loc,
+                identifiers: vec![id.clone()],
+            };
+
             function_call_named_args(
                 loc,
-                id,
+                &id_path,
                 args,
                 available_functions(&id.name, true, context.file_no, context.contract_no, ns),
                 true,
@@ -2496,6 +2533,7 @@ fn evaluate_argument(
 /// possible to resolve the function.
 fn resolve_internal_call(
     loc: &Loc,
+    id: &pt::IdentifierPath,
     function_no: usize,
     context: &ExprContext,
     resolve_to: ResolveTo,
@@ -2511,7 +2549,7 @@ fn resolve_internal_call(
             *loc,
             format!("cannot call private {}", func.ty),
             func.loc,
-            format!("declaration of {} '{}'", func.ty, func.name),
+            format!("declaration of {} '{}'", func.ty, func.id),
         ));
 
         return None;
@@ -2522,7 +2560,7 @@ fn resolve_internal_call(
                     *loc,
                     "accessor function cannot be called via an internal function call".to_string(),
                     func.loc,
-                    format!("declaration of '{}'", func.name),
+                    format!("declaration of '{}'", func.id),
                 ));
             } else {
                 errors.push(Diagnostic::error_with_note(
@@ -2530,7 +2568,7 @@ fn resolve_internal_call(
                     "functions declared external cannot be called via an internal function call"
                         .to_string(),
                     func.loc,
-                    format!("declaration of {} '{}'", func.ty, func.name),
+                    format!("declaration of {} '{}'", func.ty, func.id),
                 ));
             }
             return None;
@@ -2545,6 +2583,7 @@ fn resolve_internal_call(
         returns,
         function: Box::new(Expression::InternalFunction {
             loc: *loc,
+            id: id.clone(),
             ty,
             function_no,
             signature: if virtual_call && (func.is_virtual || func.is_override.is_some()) {
@@ -2632,7 +2671,7 @@ fn contract_call_named_args(
                     "function cannot be called with named arguments as {unnamed_params} of its parameters do not have names"
                 ),
                 func.loc,
-                format!("definition of {}", func.name),
+                format!("definition of {}", func.id),
             ));
             matches = false;
         } else if params_len != args.len() {
@@ -2932,7 +2971,7 @@ fn preprocess_contract_call<T>(
     let mut name_matches: Vec<usize> = Vec::new();
 
     for function_no in ns.contracts[external_contract_no].all_functions.keys() {
-        if func.name != ns.functions[*function_no].name
+        if func.name != ns.functions[*function_no].id.name
             || ns.functions[*function_no].ty != pt::FunctionTy::Function
         {
             continue;

+ 24 - 14
src/sema/expression/literals.rs

@@ -458,6 +458,7 @@ pub(super) fn rational_number_literal(
 /// Resolve a function call with positional arguments
 pub(super) fn struct_literal(
     loc: &pt::Loc,
+    id: pt::IdentifierPath,
     struct_ty: &StructType,
     args: &[pt::Expression],
     context: &ExprContext,
@@ -474,7 +475,7 @@ pub(super) fn struct_literal(
             *loc,
             format!(
                 "builtin struct '{}' cannot be created using struct literal",
-                struct_def.name,
+                struct_def.id,
             ),
         ));
         Err(())
@@ -483,7 +484,7 @@ pub(super) fn struct_literal(
             *loc,
             format!(
                 "struct '{}' has {} fields, not {}",
-                struct_def.name,
+                struct_def.id,
                 struct_def.fields.len(),
                 args.len()
             ),
@@ -502,11 +503,15 @@ pub(super) fn struct_literal(
                 ResolveTo::Type(&struct_def.fields[i].ty),
             )?;
             used_variable(ns, &expr, symtable);
-            fields.push(expr.cast(loc, &struct_def.fields[i].ty, true, ns, diagnostics)?);
+            fields.push((
+                None,
+                expr.cast(loc, &struct_def.fields[i].ty, true, ns, diagnostics)?,
+            ));
         }
 
         Ok(Expression::StructLiteral {
             loc: *loc,
+            id,
             ty,
             values: fields,
         })
@@ -563,6 +568,7 @@ pub(crate) fn unit_literal(
 /// Resolve a struct literal with named fields
 pub(super) fn named_struct_literal(
     loc: &pt::Loc,
+    id: pt::IdentifierPath,
     str_ty: &StructType,
     args: &[pt::NamedArgument],
     context: &ExprContext,
@@ -578,7 +584,7 @@ pub(super) fn named_struct_literal(
             *loc,
             format!(
                 "builtin struct '{}' cannot be created using struct literal",
-                struct_def.name,
+                struct_def.id,
             ),
         ));
         Err(())
@@ -587,7 +593,7 @@ pub(super) fn named_struct_literal(
             *loc,
             format!(
                 "struct '{}' has {} fields, not {}",
-                struct_def.name,
+                struct_def.id,
                 struct_def.fields.len(),
                 args.len()
             ),
@@ -597,10 +603,13 @@ pub(super) fn named_struct_literal(
         let mut fields = Vec::new();
         fields.resize(
             args.len(),
-            Expression::BoolLiteral {
-                loc: Loc::Implicit,
-                value: false,
-            },
+            (
+                None,
+                Expression::BoolLiteral {
+                    loc: Loc::Implicit,
+                    value: false,
+                },
+            ),
         );
         for a in args {
             match struct_def.fields.iter().enumerate().find(|(_, f)| {
@@ -616,15 +625,15 @@ pub(super) fn named_struct_literal(
                         ResolveTo::Type(&f.ty),
                     )?;
                     used_variable(ns, &expr, symtable);
-                    fields[i] = expr.cast(loc, &f.ty, true, ns, diagnostics)?;
+                    fields[i] = (
+                        Some(a.name.clone()),
+                        expr.cast(loc, &f.ty, true, ns, diagnostics)?,
+                    );
                 }
                 None => {
                     diagnostics.push(Diagnostic::error(
                         a.name.loc,
-                        format!(
-                            "struct '{}' has no field '{}'",
-                            struct_def.name, a.name.name,
-                        ),
+                        format!("struct '{}' has no field '{}'", struct_def.id, a.name.name,),
                     ));
                     return Err(());
                 }
@@ -632,6 +641,7 @@ pub(super) fn named_struct_literal(
         }
         Ok(Expression::StructLiteral {
             loc: *loc,
+            id,
             ty,
             values: fields,
         })

+ 10 - 3
src/sema/expression/member_access.rs

@@ -107,14 +107,19 @@ pub(super) fn member_access(
                 for function_no in ns.contracts[call_contract_no].all_functions.keys() {
                     let func = &ns.functions[*function_no];
 
-                    if func.name != id.name || func.ty != pt::FunctionTy::Function {
+                    if func.id.name != id.name || func.ty != pt::FunctionTy::Function {
                         continue;
                     }
 
                     name_matches += 1;
 
+                    let mut id_path = ns.expr_to_identifier_path(e).unwrap();
+                    id_path.identifiers.push(id.clone());
+                    id_path.loc = *loc;
+
                     expr = Ok(Expression::InternalFunction {
                         loc: e.loc(),
+                        id: id_path,
                         ty: function_type(func, false, resolve_to),
                         function_no: *function_no,
                         signature: None,
@@ -315,7 +320,7 @@ pub(super) fn member_access(
                         id.loc,
                         format!(
                             "struct '{}' does not have a field called '{}'",
-                            str_ty.definition(ns).name,
+                            str_ty.definition(ns).id,
                             id.name
                         ),
                     ));
@@ -422,7 +427,9 @@ pub(super) fn member_access(
             for function_no in ns.contracts[ref_contract_no].all_functions.keys() {
                 let func = &ns.functions[*function_no];
 
-                if func.name != id.name || func.ty != pt::FunctionTy::Function || !func.is_public()
+                if func.id.name != id.name
+                    || func.ty != pt::FunctionTy::Function
+                    || !func.is_public()
                 {
                     continue;
                 }

+ 7 - 0
src/sema/expression/variable.rs

@@ -116,8 +116,15 @@ pub(super) fn variable(
                 };
 
                 name_matches += 1;
+
+                let id_path = pt::IdentifierPath {
+                    loc: id.loc,
+                    identifiers: vec![id.clone()],
+                };
+
                 expr = Some(Expression::InternalFunction {
                     loc: id.loc,
+                    id: id_path,
                     ty,
                     function_no,
                     signature: if func.is_virtual || func.is_override.is_some() {

+ 17 - 15
src/sema/functions.rs

@@ -12,7 +12,7 @@ use crate::sema::ast::ParameterAnnotation;
 use crate::sema::function_annotation::unexpected_parameter_annotation;
 use crate::sema::namespace::ResolveTypeContext;
 use crate::Target;
-use solang_parser::pt::FunctionTy;
+use solang_parser::pt::{FunctionTy, Identifier};
 use solang_parser::{
     doccomment::DocComment,
     pt,
@@ -428,18 +428,17 @@ pub fn contract_function(
         return None;
     }
 
-    let name = func
-        .name
-        .as_ref()
-        .map(|s| s.name.as_str())
-        .unwrap_or_else(|| {
-            if ns.target.is_polkadot() && func.ty == pt::FunctionTy::Constructor {
-                "new"
-            } else {
-                ""
-            }
-        })
-        .to_owned();
+    let name = func.name.clone().unwrap_or_else(|| {
+        let name = if ns.target.is_polkadot() && func.ty == pt::FunctionTy::Constructor {
+            "new"
+        } else {
+            ""
+        };
+        Identifier {
+            name: name.to_string(),
+            loc: func.name_loc,
+        }
+    });
 
     let bases = ns.contract_bases(contract_no);
 
@@ -773,7 +772,7 @@ pub fn function(
     }
 
     let name = match &func.name {
-        Some(s) => s.name.to_owned(),
+        Some(s) => s.to_owned(),
         None => {
             ns.diagnostics.push(Diagnostic::error(
                 func.loc,
@@ -1123,7 +1122,10 @@ fn signatures() {
 
     let fdecl = Function::new(
         pt::Loc::Implicit,
-        "foo".to_owned(),
+        pt::Identifier {
+            name: "foo".to_owned(),
+            loc: pt::Loc::Implicit,
+        },
         None,
         vec![],
         pt::FunctionTy::Function,

+ 1 - 1
src/sema/statements.rs

@@ -198,7 +198,7 @@ pub fn resolve_function_body(
                     let modifier_name = &modifier.name.identifiers[0];
                     if let Ok(e) = function_call_pos_args(
                         &modifier.loc,
-                        modifier_name,
+                        &modifier.name,
                         pt::FunctionTy::Modifier,
                         modifier.args.as_ref().unwrap_or(&Vec::new()),
                         available_functions(

+ 15 - 15
src/sema/tests/data_account.rs

@@ -74,43 +74,43 @@ contract Test {
         generated: true,
     };
 
-    let read1 = ns.functions.iter().find(|f| f.name == "read1").unwrap();
+    let read1 = ns.functions.iter().find(|f| f.id.name == "read1").unwrap();
     assert_eq!(
         *read1.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read2 = ns.functions.iter().find(|f| f.name == "read2").unwrap();
+    let read2 = ns.functions.iter().find(|f| f.id.name == "read2").unwrap();
     assert_eq!(
         *read2.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read3 = ns.functions.iter().find(|f| f.name == "read3").unwrap();
+    let read3 = ns.functions.iter().find(|f| f.id.name == "read3").unwrap();
     assert_eq!(
         *read3.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read4 = ns.functions.iter().find(|f| f.name == "read4").unwrap();
+    let read4 = ns.functions.iter().find(|f| f.id.name == "read4").unwrap();
     assert_eq!(
         *read4.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read5 = ns.functions.iter().find(|f| f.name == "read5").unwrap();
+    let read5 = ns.functions.iter().find(|f| f.id.name == "read5").unwrap();
     assert_eq!(
         *read5.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read6 = ns.functions.iter().find(|f| f.name == "read6").unwrap();
+    let read6 = ns.functions.iter().find(|f| f.id.name == "read6").unwrap();
     assert_eq!(
         *read6.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let read7 = ns.functions.iter().find(|f| f.name == "read7").unwrap();
+    let read7 = ns.functions.iter().find(|f| f.id.name == "read7").unwrap();
     assert_eq!(
         *read7.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
@@ -174,49 +174,49 @@ fn write_account() {
         generated: true,
     };
 
-    let write1 = ns.functions.iter().find(|f| f.name == "write1").unwrap();
+    let write1 = ns.functions.iter().find(|f| f.id.name == "write1").unwrap();
     assert_eq!(
         *write1.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write2 = ns.functions.iter().find(|f| f.name == "write2").unwrap();
+    let write2 = ns.functions.iter().find(|f| f.id.name == "write2").unwrap();
     assert_eq!(
         *write2.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write3 = ns.functions.iter().find(|f| f.name == "write3").unwrap();
+    let write3 = ns.functions.iter().find(|f| f.id.name == "write3").unwrap();
     assert_eq!(
         *write3.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write4 = ns.functions.iter().find(|f| f.name == "write4").unwrap();
+    let write4 = ns.functions.iter().find(|f| f.id.name == "write4").unwrap();
     assert_eq!(
         *write4.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write5 = ns.functions.iter().find(|f| f.name == "write5").unwrap();
+    let write5 = ns.functions.iter().find(|f| f.id.name == "write5").unwrap();
     assert_eq!(
         *write5.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write6 = ns.functions.iter().find(|f| f.name == "write6").unwrap();
+    let write6 = ns.functions.iter().find(|f| f.id.name == "write6").unwrap();
     assert_eq!(
         *write6.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write6 = ns.functions.iter().find(|f| f.name == "write6").unwrap();
+    let write6 = ns.functions.iter().find(|f| f.id.name == "write6").unwrap();
     assert_eq!(
         *write6.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account
     );
 
-    let write7 = ns.functions.iter().find(|f| f.name == "write7").unwrap();
+    let write7 = ns.functions.iter().find(|f| f.id.name == "write7").unwrap();
     assert_eq!(
         *write7.solana_accounts.borrow().get("dataAccount").unwrap(),
         data_account

+ 4 - 4
src/sema/types.rs

@@ -91,7 +91,7 @@ pub fn resolve_typenames<'a>(
                 ) {
                     ns.structs.push(StructDecl {
                         tags: Vec::new(),
-                        name: def.name.as_ref().unwrap().name.to_owned(),
+                        id: def.name.clone().unwrap(),
                         loc: def.name.as_ref().unwrap().loc,
                         contract: None,
                         fields: Vec::new(),
@@ -381,7 +381,7 @@ fn find_struct_recursion(ns: &mut Namespace) {
         if !notes.is_empty() {
             ns.diagnostics.push(Diagnostic::error_with_notes(
                 ns.structs[n].loc,
-                format!("struct '{}' has infinite size", ns.structs[n].name),
+                format!("struct '{}' has infinite size", ns.structs[n].id),
                 notes,
             ));
         }
@@ -495,7 +495,7 @@ fn resolve_contract<'a>(
                 ) {
                     ns.structs.push(StructDecl {
                         tags: Vec::new(),
-                        name: pt.name.as_ref().unwrap().name.to_owned(),
+                        id: pt.name.clone().unwrap(),
                         loc: pt.name.as_ref().unwrap().loc,
                         contract: Some(def.name.as_ref().unwrap().name.to_owned()),
                         fields: Vec::new(),
@@ -1073,7 +1073,7 @@ fn enum_decl(
 
     let decl = EnumDecl {
         tags,
-        name: enum_.name.as_ref().unwrap().name.to_string(),
+        id: enum_.name.clone().unwrap(),
         loc: enum_.loc,
         contract: match contract_no {
             Some(c) => Some(ns.contracts[c].id.name.to_owned()),

+ 11 - 5
src/sema/using.rs

@@ -244,7 +244,7 @@ pub(crate) fn using_decl(
                                     existing.loc,
                                     format!(
                                         "previous definition of '{oper}' was '{}'",
-                                        ns.functions[existing.function_no].name
+                                        ns.functions[existing.function_no].id
                                     ),
                                 ));
                             } else {
@@ -254,7 +254,7 @@ pub(crate) fn using_decl(
                                     existing.loc,
                                     format!(
                                         "previous definition of '{oper}' was '{}'",
-                                        ns.functions[existing.function_no].name
+                                        ns.functions[existing.function_no].id
                                     ),
                                 ));
                             }
@@ -391,7 +391,7 @@ fn possible_functions(
         .filter(|func_no| {
             let func = &ns.functions[**func_no];
 
-            func.name == function_name && func.ty == pt::FunctionTy::Function
+            func.id.name == function_name && func.ty == pt::FunctionTy::Function
         })
         .cloned()
         .collect()
@@ -449,7 +449,7 @@ pub(super) fn try_resolve_using_call(
 
     for function_no in functions {
         let libfunc = &ns.functions[function_no];
-        if libfunc.name != func.name || libfunc.ty != pt::FunctionTy::Function {
+        if libfunc.id.name != func.name || libfunc.ty != pt::FunctionTy::Function {
             continue;
         }
 
@@ -520,7 +520,7 @@ pub(super) fn try_resolve_using_call(
                 *loc,
                 "cannot call private library function".to_string(),
                 libfunc.loc,
-                format!("declaration of function '{}'", libfunc.name),
+                format!("declaration of function '{}'", libfunc.id),
             ));
 
             continue;
@@ -529,11 +529,17 @@ pub(super) fn try_resolve_using_call(
         let returns = function_returns(libfunc, resolve_to);
         let ty = function_type(libfunc, false, resolve_to);
 
+        let id_path = pt::IdentifierPath {
+            loc: func.loc,
+            identifiers: vec![func.clone()],
+        };
+
         return Ok(Some(Expression::InternalFunctionCall {
             loc: *loc,
             returns,
             function: Box::new(Expression::InternalFunction {
                 loc: *loc,
+                id: id_path,
                 ty,
                 function_no,
                 signature: None,

+ 1 - 1
src/sema/variables.rs

@@ -467,7 +467,7 @@ pub fn variable_decl<'a>(
 
             let mut func = Function::new(
                 def.name.as_ref().unwrap().loc,
-                def.name.as_ref().unwrap().name.to_owned(),
+                def.name.as_ref().unwrap().clone(),
                 Some(contract_no),
                 Vec::new(),
                 pt::FunctionTy::Function,

+ 10 - 2
tests/solana_tests/tags.rs

@@ -200,7 +200,11 @@ fn functions() {
 
     assert_eq!(ns.diagnostics.len(), 5);
 
-    let func = ns.functions.iter().find(|func| func.name == "foo").unwrap();
+    let func = ns
+        .functions
+        .iter()
+        .find(|func| func.id.name == "foo")
+        .unwrap();
 
     assert_eq!(func.tags[0].tag, "param");
     assert_eq!(func.tags[0].value, "sadad");
@@ -237,7 +241,11 @@ fn functions() {
         "'@param' used in stead of '@return' for 'k'"
     );
 
-    let func = ns.functions.iter().find(|func| func.name == "foo").unwrap();
+    let func = ns
+        .functions
+        .iter()
+        .find(|func| func.id.name == "foo")
+        .unwrap();
 
     assert_eq!(func.tags[0].tag, "return");
     assert_eq!(func.tags[0].value, "is a boolean");

+ 37 - 25
vscode/src/test/suite/extension.test.ts

@@ -175,7 +175,7 @@ async function testdefs(docUri: vscode.Uri) {
   )) as vscode.Location[];
   const loc4 = actualdef4[0];
   assert.strictEqual(loc4.range.start.line, 23);
-  assert.strictEqual(loc4.range.start.character, 8);
+  assert.strictEqual(loc4.range.start.character, 14);
   assert.strictEqual(loc4.range.end.line, 23);
   assert.strictEqual(loc4.range.end.character, 15);
   assert.strictEqual(loc4.uri.path, docUri.path);
@@ -188,7 +188,7 @@ async function testdefs(docUri: vscode.Uri) {
   )) as vscode.Location[];
   const loc5 = actualdef5[0];
   assert.strictEqual(loc5.range.start.line, 24);
-  assert.strictEqual(loc5.range.start.character, 8);
+  assert.strictEqual(loc5.range.start.character, 13);
   assert.strictEqual(loc5.range.end.line, 24);
   assert.strictEqual(loc5.range.end.character, 14);
   assert.strictEqual(loc5.uri.path, docUri.path);
@@ -218,9 +218,9 @@ async function testtypedefs(docUri: vscode.Uri) {
   )) as vscode.Location[];
   const loc1 = actualtypedef1[0];
   assert.strictEqual(loc1.range.start.line, 7);
-  assert.strictEqual(loc1.range.start.character, 4);
-  assert.strictEqual(loc1.range.end.line, 21);
-  assert.strictEqual(loc1.range.end.character, 5);
+  assert.strictEqual(loc1.range.start.character, 9);
+  assert.strictEqual(loc1.range.end.line, 7);
+  assert.strictEqual(loc1.range.end.character, 14);
   assert.strictEqual(loc1.uri.path, docUri.path);
 }
 
@@ -236,15 +236,15 @@ async function testdecls(docUri: vscode.Uri) {
   assert.strictEqual(actualdecl0.length, 2);
   const loc00 = actualdecl0[0];
   assert.strictEqual(loc00.range.start.line, 12);
-  assert.strictEqual(loc00.range.start.character, 4);
+  assert.strictEqual(loc00.range.start.character, 13);
   assert.strictEqual(loc00.range.end.line, 12);
-  assert.strictEqual(loc00.range.end.character, 61);
+  assert.strictEqual(loc00.range.end.character, 16);
   assert.strictEqual(loc00.uri.path, docUri.path);
   const loc01 = actualdecl0[1];
   assert.strictEqual(loc01.range.start.line, 22);
-  assert.strictEqual(loc01.range.start.character, 4);
+  assert.strictEqual(loc01.range.start.character, 13);
   assert.strictEqual(loc01.range.end.line, 22);
-  assert.strictEqual(loc01.range.end.character, 61);
+  assert.strictEqual(loc01.range.end.character, 16);
   assert.strictEqual(loc01.uri.path, docUri.path);
 
   const pos1 = new vscode.Position(12, 14);
@@ -256,9 +256,9 @@ async function testdecls(docUri: vscode.Uri) {
   assert.strictEqual(actualdecl1.length, 1);
   const loc10 = actualdecl1[0];
   assert.strictEqual(loc10.range.start.line, 32);
-  assert.strictEqual(loc10.range.start.character, 4);
+  assert.strictEqual(loc10.range.start.character, 13);
   assert.strictEqual(loc10.range.end.line, 32);
-  assert.strictEqual(loc10.range.end.character, 52);
+  assert.strictEqual(loc10.range.end.character, 16);
   assert.strictEqual(loc10.uri.path, docUri.path);
 
   const pos2 = new vscode.Position(22, 14);
@@ -270,9 +270,9 @@ async function testdecls(docUri: vscode.Uri) {
   assert.strictEqual(actualdecl2.length, 1);
   const loc20 = actualdecl2[0];
   assert.strictEqual(loc20.range.start.line, 32);
-  assert.strictEqual(loc20.range.start.character, 4);
+  assert.strictEqual(loc20.range.start.character, 13);
   assert.strictEqual(loc20.range.end.line, 32);
-  assert.strictEqual(loc20.range.end.character, 52);
+  assert.strictEqual(loc20.range.end.character, 16);
   assert.strictEqual(loc20.uri.path, docUri.path);
 }
 
@@ -289,15 +289,15 @@ async function testimpls(docUri: vscode.Uri) {
   assert.strictEqual(actualimpl0.length, 2);
   const loc00 = actualimpl0[0];
   assert.strictEqual(loc00.range.start.line, 1);
-  assert.strictEqual(loc00.range.start.character, 4);
+  assert.strictEqual(loc00.range.start.character, 13);
   assert.strictEqual(loc00.range.end.line, 1);
-  assert.strictEqual(loc00.range.end.character, 42);
+  assert.strictEqual(loc00.range.end.character, 16);
   assert.strictEqual(loc00.uri.path, docUri.path);
   const loc01 = actualimpl0[1];
   assert.strictEqual(loc01.range.start.line, 6);
-  assert.strictEqual(loc01.range.start.character, 4);
+  assert.strictEqual(loc01.range.start.character, 13);
   assert.strictEqual(loc01.range.end.line, 6);
-  assert.strictEqual(loc01.range.end.character, 65);
+  assert.strictEqual(loc01.range.end.character, 16);
   assert.strictEqual(loc01.uri.path, docUri.path);
 
 
@@ -310,15 +310,15 @@ async function testimpls(docUri: vscode.Uri) {
   assert.strictEqual(actualimpl1.length, 2);
   const loc10 = actualimpl1[0];
   assert.strictEqual(loc10.range.start.line, 12);
-  assert.strictEqual(loc10.range.start.character, 4);
+  assert.strictEqual(loc10.range.start.character, 13);
   assert.strictEqual(loc10.range.end.line, 12);
-  assert.strictEqual(loc10.range.end.character, 61);
+  assert.strictEqual(loc10.range.end.character, 16);
   assert.strictEqual(loc10.uri.path, docUri.path);
   const loc11 = actualimpl1[1];
   assert.strictEqual(loc11.range.start.line, 16);
-  assert.strictEqual(loc11.range.start.character, 4);
+  assert.strictEqual(loc11.range.start.character, 13);
   assert.strictEqual(loc11.range.end.line, 16);
-  assert.strictEqual(loc11.range.end.character, 53);
+  assert.strictEqual(loc11.range.end.character, 16);
   assert.strictEqual(loc11.uri.path, docUri.path);
 
 
@@ -331,15 +331,15 @@ async function testimpls(docUri: vscode.Uri) {
   assert.strictEqual(actualimpl2.length, 2);
   const loc20 = actualimpl2[0];
   assert.strictEqual(loc20.range.start.line, 22);
-  assert.strictEqual(loc20.range.start.character, 4);
+  assert.strictEqual(loc20.range.start.character, 13);
   assert.strictEqual(loc20.range.end.line, 22);
-  assert.strictEqual(loc20.range.end.character, 61);
+  assert.strictEqual(loc20.range.end.character, 16);
   assert.strictEqual(loc20.uri.path, docUri.path);
   const loc21 = actualimpl2[1];
   assert.strictEqual(loc21.range.start.line, 26);
-  assert.strictEqual(loc21.range.start.character, 4);
+  assert.strictEqual(loc21.range.start.character, 13);
   assert.strictEqual(loc21.range.end.line, 26);
-  assert.strictEqual(loc21.range.end.character, 54);
+  assert.strictEqual(loc21.range.end.character, 17);
   assert.strictEqual(loc21.uri.path, docUri.path);
 }
 
@@ -569,6 +569,18 @@ async function testhover(docUri: vscode.Uri) {
   )) as vscode.Hover[];
 
   assert.strictEqual(actualhover4.length, 0);
+
+  const pos5 = new vscode.Position(134, 73);
+
+  const actualhover5 = (await vscode.commands.executeCommand(
+    'vscode.executeHoverProvider',
+    docUri,
+    pos5
+  )) as vscode.Hover[];
+
+  const contentarr5 = actualhover5[0].contents as vscode.MarkdownString[];
+
+  assert.strictEqual(contentarr5[0].value, '```solidity\nfunction foo.sum(uint64 a, uint64 b) returns (uint64)\n\n```');
 }
 
 async function testdiagnos(docUri: vscode.Uri, expecteddiag: vscode.Diagnostic[]) {

+ 18 - 1
vscode/src/testFixture/hover1.sol

@@ -124,4 +124,21 @@ contract SimpleAuction {
         // 3. Interaction
         beneficiary.transfer(highestBid);
     }
-}
+}
+
+contract foo {
+    function sum(uint64 a, uint64 b) public pure returns (uint64) {
+        return a + b;
+    }
+
+    function bar() public view {
+        function (uint64, uint64) external returns (uint64) fPtr = this.sum;
+        assembly {
+            // 'a' contains 'sum' selector
+            let a := fPtr.selector
+
+            // 'b' contains 'sum' address
+            let b := fPtr.address
+        }
+    }
+}