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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/Program.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3200,6 +3200,54 @@ describe('Program', () => {
expect(signatureHelp[0].index, `failed on col ${col}`).to.equal(0);
}
});

it('does not crash on malformed function declaration with trailing comma', () => {
program.setFile('source/main.bs', `
sub test(p1 = invalid,
result = 1
end sub
`);
program.validate();

// Try to get signature help at the position after the comma
// This should not crash even though the function declaration is malformed
const signatureHelp = program.getSignatureHelp(`${rootDir}/source/main.bs`, Position.create(1, 34));

// We don't necessarily expect specific results, just that it doesn't crash
expect(signatureHelp).to.be.an('array');
});

it('does not crash on incomplete function call', () => {
program.setFile('source/main.bs', `
function main()
someFunction(
end function
function someFunction(param1 as string)
end function
`);
program.validate();

// Try to get signature help after the opening parenthesis
const signatureHelp = program.getSignatureHelp(`${rootDir}/source/main.bs`, Position.create(2, 32));

// We don't necessarily expect specific results, just that it doesn't crash
expect(signatureHelp).to.be.an('array');
});

it('does not crash on malformed function declaration with missing closing paren', () => {
program.setFile('source/main.bs', `
sub test(p1 = invalid
result = 1
end sub
`);
program.validate();

// Try to get signature help at the position where closing paren should be
const signatureHelp = program.getSignatureHelp(`${rootDir}/source/main.bs`, Position.create(1, 33));

// We don't necessarily expect specific results, just that it doesn't crash
expect(signatureHelp).to.be.an('array');
});
});

describe('plugins', () => {
Expand Down
11 changes: 8 additions & 3 deletions src/bscPlugin/CallExpressionInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ export class CallExpressionInfo {
}

isPositionBetweenParentheses() {
if (!this.callExpression) {
return false;
}
let boundingRange = util.createBoundingRange(this.callExpression.openingParen, this.callExpression.closingParen);
return util.rangeContains(boundingRange, this.position);
}
Expand All @@ -106,8 +109,8 @@ export class CallExpressionInfo {
let callExpression = expression?.findAncestor<CallExpression | CallfuncExpression>(isCallFuncOrCallExpression);
if (callExpression && callExpression.callee === expression) {
//this expression is the NAME of a CallExpression
callExpression = expression.parent.findAncestor<CallExpression | CallfuncExpression>(isCallFuncOrCallExpression);
} else if (isDottedGetExpression(expression.parent) && expression?.parent?.parent === callExpression) {
callExpression = expression.parent?.findAncestor<CallExpression | CallfuncExpression>(isCallFuncOrCallExpression);
} else if (isDottedGetExpression(expression?.parent) && expression?.parent?.parent === callExpression) {
callExpression = callExpression.findAncestor<CallExpression | CallfuncExpression>(isCallFuncOrCallExpression);
}

Expand Down Expand Up @@ -151,6 +154,9 @@ export class CallExpressionInfo {
}

private getParameterIndex() {
if (!this.callExpression || !this.callExpression.args) {
return 0;
}
for (let i = this.callExpression.args.length - 1; i > -1; i--) {
let argExpression = this.callExpression.args[i];
let comparison = util.comparePositionToRange(this.position, argExpression.range);
Expand All @@ -160,7 +166,6 @@ export class CallExpressionInfo {
}

return 0;

}

}
4 changes: 2 additions & 2 deletions src/lsp/ProjectManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,11 +497,11 @@ export class ProjectManager {

//if the request has been cancelled since originally requested due to idle time being slow, skip the rest of the wor
if (options?.cancellationToken?.isCancellationRequested) {
this.logger.log('ProjectManager getCompletions cancelled', options);
this.logger.debug('ProjectManager getCompletions cancelled', options);
return;
}

this.logger.log('ProjectManager getCompletions', options);
this.logger.debug('ProjectManager getCompletions', options);
//Ask every project for results, keep whichever one responds first that has a valid response
let result = await util.promiseRaceMatch(
this.projects.map(x => x.getCompletions(options)),
Expand Down
Loading