@@ -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 */
15911597async 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