Skip to content

Commit b6b3f5f

Browse files
authored
Fix common lsp panics (hyperledger-solang#1575)
These lsp crashes are concurrency issues, so writing tests from them is hard. If we could hand-craft lsp json-rpc messages then this would be easier, but we only test the extension by controlling vscode. Signed-off-by: Sean Young <[email protected]>
1 parent aaceeb9 commit b6b3f5f

33 files changed

+244
-177
lines changed

RELEASE_CHECKLIST.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
- Update the version in `Cargo.toml`, `solang-parser/Cargo.toml`, the binary
44
links in `docs/installing.rst`, and `CHANGELOG.md`. Remember to match the
55
solang-parser version in the top level `Cargo.toml`.
6+
- Ensure the `CHANGELOG.md` and `vscode/CHANGELOG.md` are up to date.
7+
- If the vscode extension is going to be updated, fix the version in
8+
`docs/extension.rst`.
69
- Copy the contents of the CHANGELOG for this release into commit message,
710
using `git commit -s --cleanup=whitespace` so the that the lines beginning
811
with `#` are not removed.

docs/extension.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ Once you have node and npm installed, you can build the extension like so:
7575
npm install -g vsce
7676
vsce package
7777
78-
You should now have an extension file called solang-0.3.0.vsix which can be
79-
installed using ``code --install-extension solang-0.3.0.vsix``.
78+
You should now have an extension file called ``solang-0.3.3.vsix`` which can be
79+
installed using ``code --install-extension solang-0.3.3.vsix``.
8080

8181
Alternatively, the extension is run from vscode itself.
8282

src/abi/anchor.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn generate_anchor_idl(contract_no: usize, ns: &Namespace, contract_version:
4949

5050
Idl {
5151
version: Version::parse(contract_version).unwrap().to_string(),
52-
name: ns.contracts[contract_no].name.clone(),
52+
name: ns.contracts[contract_no].id.name.clone(),
5353
docs,
5454
constants: vec![],
5555
instructions,
@@ -261,7 +261,8 @@ impl TypeManager<'_> {
261261
// If the existing type was declared outside a contract or if it is from the current contract,
262262
// we should change the name of the type we are adding now.
263263
if other_contract.is_none()
264-
|| other_contract.as_ref().unwrap() == &self.namespace.contracts[self.contract_no].name
264+
|| other_contract.as_ref().unwrap()
265+
== &self.namespace.contracts[self.contract_no].id.name
265266
{
266267
let new_name = if let Some(this_name) = contract {
267268
format!("{this_name}_{type_name}")

src/abi/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn generate_abi(
2121
if verbose {
2222
eprintln!(
2323
"info: Generating ink! metadata for contract {}",
24-
ns.contracts[contract_no].name
24+
ns.contracts[contract_no].id
2525
);
2626
}
2727

@@ -33,7 +33,7 @@ pub fn generate_abi(
3333
if verbose {
3434
eprintln!(
3535
"info: Generating Anchor metadata for contract {}",
36-
ns.contracts[contract_no].name
36+
ns.contracts[contract_no].id
3737
);
3838
}
3939

@@ -45,7 +45,7 @@ pub fn generate_abi(
4545
if verbose {
4646
eprintln!(
4747
"info: Generating Ethereum ABI for contract {}",
48-
ns.contracts[contract_no].name
48+
ns.contracts[contract_no].id
4949
);
5050
}
5151

src/abi/polkadot.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ pub fn gen_project(contract_no: usize, ns: &ast::Namespace) -> InkProject {
309309
}
310310
})
311311
.collect();
312-
let contract_name = ns.contracts[contract_no].name.clone();
312+
let contract_name = ns.contracts[contract_no].id.name.clone();
313313
let storage = Layout::Struct(StructLayout::new(contract_name, fields));
314314

315315
let constructor_spec = |f: &Function| -> ConstructorSpec<PortableForm> {
@@ -384,7 +384,7 @@ pub fn gen_project(contract_no: usize, ns: &ast::Namespace) -> InkProject {
384384

385385
let t = TypeDefTuple::new_portable(fields);
386386

387-
let path = path!(&ns.contracts[contract_no].name, &f.name, "return_type");
387+
let path = path!(&ns.contracts[contract_no].id.name, &f.name, "return_type");
388388

389389
let ty = registry.register_type(Type::new(
390390
path,
@@ -562,7 +562,7 @@ pub fn metadata(
562562
);
563563

564564
let mut builder = Contract::builder();
565-
builder.name(&ns.contracts[contract_no].name);
565+
builder.name(&ns.contracts[contract_no].id.name);
566566
let mut description = tags(contract_no, "title", ns);
567567
description.extend(tags(contract_no, "notice", ns));
568568
if !description.is_empty() {

src/bin/doc/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
186186
name: &event_decl.name,
187187
contract: event_decl
188188
.contract
189-
.map(|contract_no| file.contracts[contract_no].name.as_str()),
189+
.map(|contract_no| file.contracts[contract_no].id.name.as_str()),
190190
title: get_tag("title", &event_decl.tags),
191191
notice: get_tag("notice", &event_decl.tags),
192192
author: get_tag("author", &event_decl.tags),
@@ -356,7 +356,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
356356
for var in base
357357
.variables
358358
.iter()
359-
.map(|var| map_var(file, Some(&base.name), var))
359+
.map(|var| map_var(file, Some(&base.id.name), var))
360360
{
361361
base_variables.push(var);
362362
}
@@ -365,7 +365,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
365365
let f = &file.functions[*function_no];
366366

367367
if f.has_body {
368-
Some(map_func(file, Some(&base.name), f))
368+
Some(map_func(file, Some(&base.id.name), f))
369369
} else {
370370
None
371371
}
@@ -376,7 +376,7 @@ pub fn generate_docs(outdir: &OsString, files: &[ast::Namespace], verbose: bool)
376376

377377
top.contracts.push(Contract {
378378
loc: contract.loc,
379-
name: &contract.name,
379+
name: &contract.id.name,
380380
ty: format!("{}", contract.ty),
381381
title: get_tag("title", &contract.tags),
382382
notice: get_tag("notice", &contract.tags),

src/bin/languageserver/mod.rs

+45-36
Original file line numberDiff line numberDiff line change
@@ -334,16 +334,17 @@ impl SolangServer {
334334
let files = self.files.lock().await;
335335
if let Some(cache) = files.caches.get(&path) {
336336
let f = &cache.file;
337-
let offset = f.get_offset(
337+
if let Some(offset) = f.get_offset(
338338
params.text_document_position_params.position.line as _,
339339
params.text_document_position_params.position.character as _,
340-
);
341-
if let Some(reference) = cache
342-
.references
343-
.find(offset, offset + 1)
344-
.min_by(|a, b| (a.stop - a.start).cmp(&(b.stop - b.start)))
345-
{
346-
return Ok(Some(reference.val.clone()));
340+
) {
341+
if let Some(reference) = cache
342+
.references
343+
.find(offset, offset + 1)
344+
.min_by(|a, b| (a.stop - a.start).cmp(&(b.stop - b.start)))
345+
{
346+
return Ok(Some(reference.val.clone()));
347+
}
347348
}
348349
}
349350
Ok(None)
@@ -905,7 +906,7 @@ impl<'a> Builder<'a> {
905906
}
906907
ast::Expression::ConstantVariable { loc, ty, contract_no, var_no } => {
907908
let (contract, name) = if let Some(contract_no) = contract_no {
908-
let contract = format!("{}.", self.ns.contracts[*contract_no].name);
909+
let contract = format!("{}.", self.ns.contracts[*contract_no].id);
909910
let name = &self.ns.contracts[*contract_no].variables[*var_no].name;
910911
(contract, name)
911912
} else {
@@ -944,7 +945,7 @@ impl<'a> Builder<'a> {
944945
ast::Expression::StorageVariable { loc, ty, contract_no, var_no } => {
945946
let contract = &self.ns.contracts[*contract_no];
946947
let name = &contract.variables[*var_no].name;
947-
let val = format!("{} {}.{}", ty.to_string(self.ns), contract.name, name);
948+
let val = format!("{} {}.{}", ty.to_string(self.ns), contract.id, name);
948949
self.hovers.push((
949950
loc.file_no(),
950951
HoverEntry {
@@ -1082,7 +1083,7 @@ impl<'a> Builder<'a> {
10821083
msg
10831084
}).join(", ");
10841085

1085-
let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].name)).unwrap_or_default();
1086+
let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].id)).unwrap_or_default();
10861087

10871088
let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.name, params, rets);
10881089

@@ -1140,7 +1141,7 @@ impl<'a> Builder<'a> {
11401141
msg
11411142
}).join(", ");
11421143

1143-
let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].name)).unwrap_or_default();
1144+
let contract = fnc.contract_no.map(|contract_no| format!("{}.", self.ns.contracts[contract_no].id)).unwrap_or_default();
11441145

11451146
let val = format!("{} {}{}({}) returns ({})\n", fnc.ty, contract, fnc.name, params, rets);
11461147

@@ -1562,7 +1563,7 @@ impl<'a> Builder<'a> {
15621563
stop: base.loc.exclusive_end(),
15631564
val: make_code_block(format!(
15641565
"contract {}",
1565-
self.ns.contracts[base.contract_no].name
1566+
self.ns.contracts[base.contract_no].id
15661567
)),
15671568
},
15681569
));
@@ -1589,8 +1590,8 @@ impl<'a> Builder<'a> {
15891590
self.hovers.push((
15901591
file_no,
15911592
HoverEntry {
1592-
start: contract.loc.start(),
1593-
stop: contract.loc.start() + contract.name.len(),
1593+
start: contract.id.loc.start(),
1594+
stop: contract.id.loc.exclusive_end(),
15941595
val: render(&contract.tags[..]),
15951596
},
15961597
));
@@ -1601,7 +1602,7 @@ impl<'a> Builder<'a> {
16011602
};
16021603

16031604
self.definitions
1604-
.insert(cdi.clone(), loc_to_range(&contract.loc, file));
1605+
.insert(cdi.clone(), loc_to_range(&contract.id.loc, file));
16051606

16061607
let impls = contract
16071608
.functions
@@ -1719,7 +1720,9 @@ impl<'a> Builder<'a> {
17191720
.collect::<HashMap<PathBuf, usize>>();
17201721

17211722
for val in self.types.values_mut() {
1722-
val.def_path = defs_to_files[&val.def_type].clone();
1723+
if let Some(path) = defs_to_files.get(&val.def_type) {
1724+
val.def_path = path.clone();
1725+
}
17231726
}
17241727

17251728
for (di, range) in &self.definitions {
@@ -1729,10 +1732,13 @@ impl<'a> Builder<'a> {
17291732
file_no,
17301733
ReferenceEntry {
17311734
start: file
1732-
.get_offset(range.start.line as usize, range.start.character as usize),
1735+
.get_offset(range.start.line as usize, range.start.character as usize)
1736+
.unwrap(),
17331737
// 1 is added to account for the fact that `Lapper` expects half open ranges of the type: [`start`, `stop`)
17341738
// i.e, `start` included but `stop` excluded.
1735-
stop: file.get_offset(range.end.line as usize, range.end.character as usize)
1739+
stop: file
1740+
.get_offset(range.end.line as usize, range.end.character as usize)
1741+
.unwrap()
17361742
+ 1,
17371743
val: di.clone(),
17381744
},
@@ -1761,7 +1767,9 @@ impl<'a> Builder<'a> {
17611767
.filter(|h| h.0 == i)
17621768
.map(|(_, i)| {
17631769
let mut i = i.clone();
1764-
i.val.def_path = defs_to_files[&i.val.def_type].clone();
1770+
if let Some(path) = defs_to_files.get(&i.val.def_type) {
1771+
i.val.def_path = path.clone();
1772+
}
17651773
i
17661774
})
17671775
.collect(),
@@ -1990,24 +1998,25 @@ impl LanguageServer for SolangServer {
19901998
if let Ok(path) = uri.to_file_path() {
19911999
let files = &self.files.lock().await;
19922000
if let Some(cache) = files.caches.get(&path) {
1993-
let offset = cache
2001+
if let Some(offset) = cache
19942002
.file
1995-
.get_offset(pos.line as usize, pos.character as usize);
1996-
1997-
// The shortest hover for the position will be most informative
1998-
if let Some(hover) = cache
1999-
.hovers
2000-
.find(offset, offset + 1)
2001-
.min_by(|a, b| (a.stop - a.start).cmp(&(b.stop - b.start)))
2003+
.get_offset(pos.line as usize, pos.character as usize)
20022004
{
2003-
let range = get_range_exclusive(hover.start, hover.stop, &cache.file);
2004-
2005-
return Ok(Some(Hover {
2006-
contents: HoverContents::Scalar(MarkedString::from_markdown(
2007-
hover.val.to_string(),
2008-
)),
2009-
range: Some(range),
2010-
}));
2005+
// The shortest hover for the position will be most informative
2006+
if let Some(hover) = cache
2007+
.hovers
2008+
.find(offset, offset + 1)
2009+
.min_by(|a, b| (a.stop - a.start).cmp(&(b.stop - b.start)))
2010+
{
2011+
let range = get_range_exclusive(hover.start, hover.stop, &cache.file);
2012+
2013+
return Ok(Some(Hover {
2014+
contents: HoverContents::Scalar(MarkedString::from_markdown(
2015+
hover.val.to_string(),
2016+
)),
2017+
range: Some(range),
2018+
}));
2019+
}
20112020
}
20122021
}
20132022
}

src/bin/solang.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ fn compile(compile_args: &Compile) {
230230
!namespaces
231231
.iter()
232232
.flat_map(|ns| ns.contracts.iter())
233-
.any(|contract| **name == contract.name)
233+
.any(|contract| **name == contract.id.name)
234234
})
235235
.collect();
236236

@@ -370,15 +370,15 @@ fn contract_results(
370370

371371
let loc = ns.loc_to_string(PathDisplay::FullPath, &resolved_contract.loc);
372372

373-
if let Some(other_loc) = seen_contracts.get(&resolved_contract.name) {
373+
if let Some(other_loc) = seen_contracts.get(&resolved_contract.id.name) {
374374
eprintln!(
375375
"error: contract {} defined at {other_loc} and {}",
376-
resolved_contract.name, loc
376+
resolved_contract.id, loc
377377
);
378378
exit(1);
379379
}
380380

381-
seen_contracts.insert(resolved_contract.name.to_string(), loc);
381+
seen_contracts.insert(resolved_contract.id.to_string(), loc);
382382

383383
if let Some("cfg") = compiler_output.emit.as_deref() {
384384
println!("{}", resolved_contract.print_cfg(ns));
@@ -389,13 +389,13 @@ fn contract_results(
389389
if ns.target == solang::Target::Solana {
390390
eprintln!(
391391
"info: contract {} uses at least {} bytes account data",
392-
resolved_contract.name, resolved_contract.fixed_layout_size,
392+
resolved_contract.id, resolved_contract.fixed_layout_size,
393393
);
394394
}
395395

396396
eprintln!(
397397
"info: Generating LLVM IR for contract {} with target {}",
398-
resolved_contract.name, ns.target
398+
resolved_contract.id, ns.target
399399
);
400400
}
401401

@@ -413,7 +413,7 @@ fn contract_results(
413413
if let Some(level) = opt.wasm_opt.filter(|_| ns.target.is_polkadot() && verbose) {
414414
eprintln!(
415415
"info: wasm-opt level '{}' for contract {}",
416-
level, resolved_contract.name
416+
level, resolved_contract.id
417417
);
418418
}
419419

src/codegen/cfg.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ impl ControlFlowGraph {
12831283
} else {
12841284
String::new()
12851285
},
1286-
ns.contracts[*contract_no].name,
1286+
ns.contracts[*contract_no].id,
12871287
self.expr_to_string(contract, ns, encoded_args),
12881288
if let ExternalCallAccounts::Present(accounts) = accounts {
12891289
self.expr_to_string(contract, ns, accounts)
@@ -1615,9 +1615,9 @@ fn function_cfg(
16151615
let contract_name = match func.contract_no {
16161616
Some(base_contract_no) => format!(
16171617
"{}::{}",
1618-
ns.contracts[contract_no].name, ns.contracts[base_contract_no].name
1618+
ns.contracts[contract_no].id, ns.contracts[base_contract_no].id
16191619
),
1620-
None => ns.contracts[contract_no].name.to_string(),
1620+
None => ns.contracts[contract_no].id.to_string(),
16211621
};
16221622

16231623
let name = match func.ty {
@@ -1889,8 +1889,8 @@ fn generate_modifier_dispatch(
18891889
let modifier = &ns.functions[modifier_no];
18901890
let name = format!(
18911891
"{}::{}::{}::modifier{}::{}",
1892-
&ns.contracts[contract_no].name,
1893-
&ns.contracts[func.contract_no.unwrap()].name,
1892+
&ns.contracts[contract_no].id,
1893+
&ns.contracts[func.contract_no.unwrap()].id,
18941894
func.llvm_symbol(ns),
18951895
chain_no,
18961896
modifier.llvm_symbol(ns)
@@ -2032,7 +2032,7 @@ fn generate_modifier_dispatch(
20322032
impl Contract {
20332033
/// Print the entire contract; storage initializers, constructors and functions and their CFGs
20342034
pub fn print_cfg(&self, ns: &Namespace) -> String {
2035-
let mut out = format!("#\n# Contract: {}\n#\n\n", self.name);
2035+
let mut out = format!("#\n# Contract: {}\n#\n\n", self.id);
20362036

20372037
for cfg in &self.cfg {
20382038
if !cfg.is_placeholder() {

0 commit comments

Comments
 (0)