Skip to content

Commit 212a1ba

Browse files
committed
Fixes #370
1 parent 71d6be6 commit 212a1ba

File tree

2 files changed

+24
-11
lines changed

2 files changed

+24
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [2.7.3] - 2025-XX-XX
44
- Fix issue [#366](https://github.com/intersystems/language-server/issues/366): Language Server can return invalid DocumentSymbols in some rare circumstances
55
- Fix issue [#367](https://github.com/intersystems/language-server/issues/367): Support `objectscript.multilineMethodArgs` being set per workspace folder
6+
- Fix issue [#370](https://github.com/intersystems/language-server/issues/370): Infinite recursion when a variable is set to the result of one of its instance methods
67
- Parser changes:
78
- DP-438845, DP-440271: Support new keywords for deferred filing of indices and computed fields
89

server/src/utils/functions.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,7 +1578,13 @@ async function determineDeclaredLocalVarClass(
15781578

15791579
/**
15801580
* Parse a Set command's arguments and look to see if `selector` was set.
1581-
* If so, attempt to determine the class of `selector`.
1581+
* If so, attempt to determine the class of `selector`. If the token at
1582+
* `[endLn,endTkn]` is reached, the function will immediately terminate
1583+
* to prevent infinite recursion when encountering commands like:
1584+
*
1585+
* ```objectscript
1586+
* Set a = a.MyMethod()
1587+
* ```
15821588
*
15831589
* @param doc The TextDocument that the Set is in.
15841590
* @param parsed The tokenized representation of `doc`.
@@ -1590,7 +1596,7 @@ async function determineDeclaredLocalVarClass(
15901596
*/
15911597
async function parseSetCommand(
15921598
doc: TextDocument, parsed: compressedline[], line: number, token: number,
1593-
selector: string, server: ServerSpec, diagnostic: boolean
1599+
selector: string, server: ServerSpec, diagnostic: boolean, endLn: number, endTkn: number
15941600
): Promise<string> {
15951601
let result = "";
15961602
let brk = false;
@@ -1605,6 +1611,12 @@ async function parseSetCommand(
16051611
for (let ln = line; ln < parsed.length; ln++) {
16061612
if (!parsed[ln]?.length) continue;
16071613
for (let tkn = (ln == line ? token + 1 : 0); tkn < parsed[ln].length; tkn++) {
1614+
if (ln > endLn || (ln == endLn && tkn >= endTkn)) {
1615+
// We reached the token of the variable that we are trying to
1616+
// resolve the type for, so exit to prevent infinite recursion
1617+
brk = true;
1618+
break;
1619+
}
16081620
if (parsed[ln][tkn].l == ld.cos_langindex && (parsed[ln][tkn].s === ld.cos_command_attrindex || parsed[ln][tkn].s === ld.cos_zcom_attrindex)) {
16091621
// This is the next command, so stop looping
16101622
brk = true;
@@ -1959,13 +1971,13 @@ async function determineUndeclaredLocalVarClass(
19591971
}
19601972
else {
19611973
// Loop through the line looking for Sets or this variable passed by reference
1962-
for (let tkn = 0; tkn < parsed[j].length; tkn++) {
1974+
for (let k = 0; k < parsed[j].length; k++) {
19631975
if (
1964-
parsed[j][tkn].l == ld.cos_langindex && parsed[j][tkn].s === ld.cos_command_attrindex &&
1965-
["s","set"].includes(doc.getText(Range.create(j,parsed[j][tkn].p,j,parsed[j][tkn].p+parsed[j][tkn].c)).toLowerCase())
1976+
parsed[j][k].l == ld.cos_langindex && parsed[j][k].s === ld.cos_command_attrindex &&
1977+
["s","set"].includes(doc.getText(Range.create(j,parsed[j][k].p,j,parsed[j][k].p+parsed[j][k].c)).toLowerCase())
19661978
) {
19671979
// This is a Set command
1968-
const setCls = await parseSetCommand(doc,parsed,j,tkn,thisvar,server,Array.isArray(allfiles));
1980+
const setCls = await parseSetCommand(doc,parsed,j,k,thisvar,server,Array.isArray(allfiles),line,tkn);
19691981
if (setCls) {
19701982
result = {
19711983
baseclass: await normalizeClassname(doc,parsed,setCls,server,j,allfiles,undefined,inheritedpackages),
@@ -1976,10 +1988,10 @@ async function determineUndeclaredLocalVarClass(
19761988
}
19771989
// Don't check for by reference syntax if we're calculating diagnostics for performance reasons
19781990
if (
1979-
!allfiles && parsed[j][tkn].l == ld.cos_langindex && parsed[j][tkn].s == ld.cos_oper_attrindex &&
1980-
doc.getText(Range.create(j,parsed[j][tkn].p,j,parsed[j][tkn].p+parsed[j][tkn].c)) == "."
1991+
!allfiles && parsed[j][k].l == ld.cos_langindex && parsed[j][k].s == ld.cos_oper_attrindex &&
1992+
doc.getText(Range.create(j,parsed[j][k].p,j,parsed[j][k].p+parsed[j][k].c)) == "."
19811993
) {
1982-
const next = nextToken(parsed,j,tkn);
1994+
const next = nextToken(parsed,j,k);
19831995
// Check if the variable passed by reference is the one we care about
19841996
if (next && parsed[next[0]][next[1]].l == ld.cos_langindex &&
19851997
(
@@ -1993,7 +2005,7 @@ async function determineUndeclaredLocalVarClass(
19932005
)) == thisvar
19942006
) {
19952007
// Find the start of the method
1996-
const [startLn, startTkn] = findOpenParen(doc,parsed,j,tkn);
2008+
const [startLn, startTkn] = findOpenParen(doc,parsed,j,k);
19972009
if (startLn != -1 && startTkn != -1 &&
19982010
parsed[startLn][startTkn-1].l == ld.cos_langindex &&
19992011
(
@@ -2004,7 +2016,7 @@ async function determineUndeclaredLocalVarClass(
20042016
// Determine which argument number this is
20052017
const argNum = determineActiveParam(doc.getText(Range.create(
20062018
startLn,parsed[startLn][startTkn].p+1,
2007-
j,parsed[j][tkn].p
2019+
j,parsed[j][k].p
20082020
))) + 1;
20092021

20102022
// Get the full text of the member

0 commit comments

Comments
 (0)