Skip to content

Commit

Permalink
Implement Go To Declaration (#1540)
Browse files Browse the repository at this point in the history
- gives a list of contract methods that the given contract method
overrides.
-  only returns the methods belonging to the immediate parent contracts.
-  handles multiple inheritance.

Signed-off-by: Govardhan G D <[email protected]>
  • Loading branch information
chioni16 authored Sep 29, 2023
1 parent 5675a34 commit b972858
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 151 deletions.
331 changes: 227 additions & 104 deletions src/bin/languageserver/mod.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/codegen/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,7 @@ fn is_there_virtual_function(
if func.ty == pt::FunctionTy::Receive {
// if there is a virtual receive function, and it's not this one, ignore it
if let Some(receive) = ns.contracts[contract_no].virtual_functions.get("@receive") {
let receive = receive.last().unwrap();
if Some(*receive) != function_no {
return true;
}
Expand All @@ -1452,6 +1453,7 @@ fn is_there_virtual_function(
if func.ty == pt::FunctionTy::Fallback {
// if there is a virtual fallback function, and it's not this one, ignore it
if let Some(fallback) = ns.contracts[contract_no].virtual_functions.get("@fallback") {
let fallback = fallback.last().unwrap();
if Some(*fallback) != function_no {
return true;
}
Expand Down Expand Up @@ -1537,6 +1539,9 @@ fn resolve_modifier_call<'a>(
// is it a virtual function call
let function_no = if let Some(signature) = signature {
contract.virtual_functions[signature]
.last()
.copied()
.unwrap()
} else {
*function_no
};
Expand Down Expand Up @@ -2130,6 +2135,7 @@ impl Namespace {
&& self.contracts[contract_no]
.virtual_functions
.get(&func.signature)
.and_then(|v| v.last())
!= function_no.as_ref()
{
return false;
Expand Down
7 changes: 6 additions & 1 deletion src/codegen/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,9 @@ pub fn expression(
..
} => {
let function_no = if let Some(signature) = signature {
&ns.contracts[contract_no].virtual_functions[signature]
ns.contracts[contract_no].virtual_functions[signature]
.last()
.unwrap()
} else {
function_no
};
Expand Down Expand Up @@ -2696,6 +2698,9 @@ pub fn emit_function_call(

let function_no = if let Some(signature) = signature {
ns.contracts[caller_contract_no].virtual_functions[signature]
.last()
.copied()
.unwrap()
} else {
*function_no
};
Expand Down
5 changes: 4 additions & 1 deletion src/sema/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,10 @@ pub struct Contract {
pub fixed_layout_size: BigInt,
pub functions: Vec<usize>,
pub all_functions: BTreeMap<usize, usize>,
pub virtual_functions: HashMap<String, usize>,
/// maps the name of virtual functions to a vector of overriden functions.
/// Each time a virtual function is overriden, there will be an entry pushed to the vector. The last
/// element represents the current overriding function - there will be at least one entry in this vector.
pub virtual_functions: HashMap<String, Vec<usize>>,
pub yul_functions: Vec<usize>,
pub variables: Vec<Variable>,
/// List of contracts this contract instantiates
Expand Down
4 changes: 3 additions & 1 deletion src/sema/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,9 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
if cur.is_override.is_some() || cur.is_virtual {
ns.contracts[contract_no]
.virtual_functions
.insert(signature, function_no);
.entry(signature)
.or_insert_with(Vec::new)
.push(function_no); // there is always at least 1 element in the vector
}

ns.contracts[contract_no]
Expand Down
3 changes: 3 additions & 0 deletions src/sema/mutability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ fn check_mutability(func: &Function, ns: &Namespace) -> Vec<Diagnostic> {
{
let function_no = if let Some(signature) = signature {
state.ns.contracts[contract_no].virtual_functions[signature]
.last()
.copied()
.unwrap()
} else {
*function_no
};
Expand Down
138 changes: 99 additions & 39 deletions vscode/src/test/suite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ suite('Extension Test Suite', function () {
await testtypedefs(typedefdoc1);
});

// Tests for goto-declaration
this.timeout(20000);
const declsdoc1 = getDocUri('impls.sol');
test('Testing for GoToDeclaration', async () => {
await testdecls(declsdoc1);
});

// Tests for goto-impls
this.timeout(20000);
const implsdoc1 = getDocUri('impls.sol');
Expand Down Expand Up @@ -119,8 +126,8 @@ async function testdefs(docUri: vscode.Uri) {
'vscode.executeDefinitionProvider',
docUri,
pos1
)) as vscode.Definition[];
const loc1 = actualdef1[0] as vscode.Location;
)) as vscode.Location[];
const loc1 = actualdef1[0];
assert.strictEqual(loc1.range.start.line, 27);
assert.strictEqual(loc1.range.start.character, 24);
assert.strictEqual(loc1.range.end.line, 27);
Expand All @@ -132,8 +139,8 @@ async function testdefs(docUri: vscode.Uri) {
'vscode.executeDefinitionProvider',
docUri,
pos2
)) as vscode.Definition[];
const loc2 = actualdef2[0] as vscode.Location;
)) as vscode.Location[];
const loc2 = actualdef2[0];
assert.strictEqual(loc2.range.start.line, 27);
assert.strictEqual(loc2.range.start.character, 50);
assert.strictEqual(loc2.range.end.line, 27);
Expand All @@ -145,8 +152,8 @@ async function testdefs(docUri: vscode.Uri) {
'vscode.executeDefinitionProvider',
docUri,
pos3
)) as vscode.Definition[];
const loc3 = actualdef3[0] as vscode.Location;
)) as vscode.Location[];
const loc3 = actualdef3[0];
assert.strictEqual(loc3.range.start.line, 19);
assert.strictEqual(loc3.range.start.character, 8);
assert.strictEqual(loc3.range.end.line, 19);
Expand All @@ -158,8 +165,8 @@ async function testdefs(docUri: vscode.Uri) {
'vscode.executeDefinitionProvider',
docUri,
pos4
)) as vscode.Definition[];
const loc4 = actualdef4[0] as vscode.Location;
)) 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.end.line, 23);
Expand All @@ -171,8 +178,8 @@ async function testdefs(docUri: vscode.Uri) {
'vscode.executeDefinitionProvider',
docUri,
pos5
)) as vscode.Definition[];
const loc5 = actualdef5[0] as vscode.Location;
)) 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.end.line, 24);
Expand All @@ -188,8 +195,8 @@ async function testtypedefs(docUri: vscode.Uri) {
'vscode.executeTypeDefinitionProvider',
docUri,
pos0,
)) as vscode.Definition[];
const loc0 = actualtypedef0[0] as vscode.Location;
)) as vscode.Location[];
const loc0 = actualtypedef0[0];
assert.strictEqual(loc0.range.start.line, 22);
assert.strictEqual(loc0.range.start.character, 11);
assert.strictEqual(loc0.range.end.line, 22);
Expand All @@ -201,15 +208,68 @@ async function testtypedefs(docUri: vscode.Uri) {
'vscode.executeTypeDefinitionProvider',
docUri,
pos1,
)) as vscode.Definition[];
const loc1 = actualtypedef1[0] as vscode.Location;
)) 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.uri.path, docUri.path);
}

async function testdecls(docUri: vscode.Uri) {
await activate(docUri);

const pos0 = new vscode.Position(6, 14);
const actualdecl0 = (await vscode.commands.executeCommand(
'vscode.executeDeclarationProvider',
docUri,
pos0,
)) as vscode.Location[];
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.end.line, 12);
assert.strictEqual(loc00.range.end.character, 61);
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.end.line, 22);
assert.strictEqual(loc01.range.end.character, 61);
assert.strictEqual(loc01.uri.path, docUri.path);

const pos1 = new vscode.Position(12, 14);
const actualdecl1 = (await vscode.commands.executeCommand(
'vscode.executeDeclarationProvider',
docUri,
pos1,
)) as vscode.Location[];
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.end.line, 32);
assert.strictEqual(loc10.range.end.character, 52);
assert.strictEqual(loc10.uri.path, docUri.path);

const pos2 = new vscode.Position(22, 14);
const actualdecl2 = (await vscode.commands.executeCommand(
'vscode.executeDeclarationProvider',
docUri,
pos2,
)) as vscode.Location[];
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.end.line, 32);
assert.strictEqual(loc20.range.end.character, 52);
assert.strictEqual(loc20.uri.path, docUri.path);
}


async function testimpls(docUri: vscode.Uri) {
await activate(docUri);

Expand All @@ -218,19 +278,19 @@ async function testimpls(docUri: vscode.Uri) {
'vscode.executeImplementationProvider',
docUri,
pos0,
)) as vscode.Definition[];
)) as vscode.Location[];
assert.strictEqual(actualimpl0.length, 2);
const loc00 = actualimpl0[0] as vscode.Location;
const loc00 = actualimpl0[0];
assert.strictEqual(loc00.range.start.line, 1);
assert.strictEqual(loc00.range.start.character, 4);
assert.strictEqual(loc00.range.end.line, 1);
assert.strictEqual(loc00.range.end.character, 42);
assert.strictEqual(loc00.uri.path, docUri.path);
const loc01 = actualimpl0[1] as vscode.Location;
const loc01 = actualimpl0[1];
assert.strictEqual(loc01.range.start.line, 6);
assert.strictEqual(loc01.range.start.character, 4);
assert.strictEqual(loc01.range.end.line, 6);
assert.strictEqual(loc01.range.end.character, 61);
assert.strictEqual(loc01.range.end.character, 65);
assert.strictEqual(loc01.uri.path, docUri.path);


Expand All @@ -239,15 +299,15 @@ async function testimpls(docUri: vscode.Uri) {
'vscode.executeImplementationProvider',
docUri,
pos1,
)) as vscode.Definition[];
)) as vscode.Location[];
assert.strictEqual(actualimpl1.length, 2);
const loc10 = actualimpl1[0] as vscode.Location;
const loc10 = actualimpl1[0];
assert.strictEqual(loc10.range.start.line, 12);
assert.strictEqual(loc10.range.start.character, 4);
assert.strictEqual(loc10.range.end.line, 12);
assert.strictEqual(loc10.range.end.character, 52);
assert.strictEqual(loc10.range.end.character, 61);
assert.strictEqual(loc10.uri.path, docUri.path);
const loc11 = actualimpl1[1] as vscode.Location;
const loc11 = actualimpl1[1];
assert.strictEqual(loc11.range.start.line, 16);
assert.strictEqual(loc11.range.start.character, 4);
assert.strictEqual(loc11.range.end.line, 16);
Expand All @@ -260,15 +320,15 @@ async function testimpls(docUri: vscode.Uri) {
'vscode.executeImplementationProvider',
docUri,
pos2,
)) as vscode.Definition[];
)) as vscode.Location[];
assert.strictEqual(actualimpl2.length, 2);
const loc20 = actualimpl2[0] as vscode.Location;
const loc20 = actualimpl2[0];
assert.strictEqual(loc20.range.start.line, 22);
assert.strictEqual(loc20.range.start.character, 4);
assert.strictEqual(loc20.range.end.line, 22);
assert.strictEqual(loc20.range.end.character, 52);
assert.strictEqual(loc20.range.end.character, 61);
assert.strictEqual(loc20.uri.path, docUri.path);
const loc21 = actualimpl2[1] as vscode.Location;
const loc21 = actualimpl2[1];
assert.strictEqual(loc21.range.start.line, 26);
assert.strictEqual(loc21.range.start.character, 4);
assert.strictEqual(loc21.range.end.line, 26);
Expand All @@ -284,33 +344,33 @@ async function testrefs(docUri: vscode.Uri) {
'vscode.executeReferenceProvider',
docUri,
pos0,
)) as vscode.Definition[];
)) as vscode.Location[];
assert.strictEqual(actualref0.length, 5);
const loc00 = actualref0[0] as vscode.Location;
const loc00 = actualref0[0];
assert.strictEqual(loc00.range.start.line, 27);
assert.strictEqual(loc00.range.start.character, 50);
assert.strictEqual(loc00.range.end.line, 27);
assert.strictEqual(loc00.range.end.character, 55);
assert.strictEqual(loc00.uri.path, docUri.path);
const loc01 = actualref0[1] as vscode.Location;
const loc01 = actualref0[1];
assert.strictEqual(loc01.range.start.line, 30);
assert.strictEqual(loc01.range.start.character, 16);
assert.strictEqual(loc01.range.end.line, 30);
assert.strictEqual(loc01.range.end.character, 21);
assert.strictEqual(loc01.uri.path, docUri.path);
const loc02 = actualref0[2] as vscode.Location;
const loc02 = actualref0[2];
assert.strictEqual(loc02.range.start.line, 33);
assert.strictEqual(loc02.range.start.character, 16);
assert.strictEqual(loc02.range.end.line, 33);
assert.strictEqual(loc02.range.end.character, 21);
assert.strictEqual(loc02.uri.path, docUri.path);
const loc03 = actualref0[3] as vscode.Location;
const loc03 = actualref0[3];
assert.strictEqual(loc03.range.start.line, 36);
assert.strictEqual(loc03.range.start.character, 16);
assert.strictEqual(loc03.range.end.line, 36);
assert.strictEqual(loc03.range.end.character, 21);
assert.strictEqual(loc03.uri.path, docUri.path);
const loc04 = actualref0[4] as vscode.Location;
const loc04 = actualref0[4];
assert.strictEqual(loc04.range.start.line, 39);
assert.strictEqual(loc04.range.start.character, 16);
assert.strictEqual(loc04.range.end.line, 39);
Expand All @@ -322,39 +382,39 @@ async function testrefs(docUri: vscode.Uri) {
'vscode.executeReferenceProvider',
docUri,
pos1,
)) as vscode.Definition[];
)) as vscode.Location[];
assert.strictEqual(actualref1.length, 6);
const loc10 = actualref1[0] as vscode.Location;
const loc10 = actualref1[0];
assert.strictEqual(loc10.range.start.line, 27);
assert.strictEqual(loc10.range.start.character, 24);
assert.strictEqual(loc10.range.end.line, 27);
assert.strictEqual(loc10.range.end.character, 25);
assert.strictEqual(loc10.uri.path, docUri.path);
const loc11 = actualref1[1] as vscode.Location;
const loc11 = actualref1[1];
assert.strictEqual(loc11.range.start.line, 28);
assert.strictEqual(loc11.range.start.character, 12);
assert.strictEqual(loc11.range.end.line, 28);
assert.strictEqual(loc11.range.end.character, 13);
assert.strictEqual(loc11.uri.path, docUri.path);
const loc12 = actualref1[2] as vscode.Location;
const loc12 = actualref1[2];
assert.strictEqual(loc12.range.start.line, 29);
assert.strictEqual(loc12.range.start.character, 16);
assert.strictEqual(loc12.range.end.line, 29);
assert.strictEqual(loc12.range.end.character, 17);
assert.strictEqual(loc12.uri.path, docUri.path);
const loc13 = actualref1[3] as vscode.Location;
const loc13 = actualref1[3];
assert.strictEqual(loc13.range.start.line, 32);
assert.strictEqual(loc13.range.start.character, 16);
assert.strictEqual(loc13.range.end.line, 32);
assert.strictEqual(loc13.range.end.character, 17);
assert.strictEqual(loc13.uri.path, docUri.path);
const loc14 = actualref1[4] as vscode.Location;
const loc14 = actualref1[4];
assert.strictEqual(loc14.range.start.line, 35);
assert.strictEqual(loc14.range.start.character, 16);
assert.strictEqual(loc14.range.end.line, 35);
assert.strictEqual(loc14.range.end.character, 17);
assert.strictEqual(loc14.uri.path, docUri.path);
const loc15 = actualref1[5] as vscode.Location;
const loc15 = actualref1[5];
assert.strictEqual(loc15.range.start.line, 38);
assert.strictEqual(loc15.range.start.character, 16);
assert.strictEqual(loc15.range.end.line, 38);
Expand Down
Loading

0 comments on commit b972858

Please sign in to comment.