diff --git a/grammars/UCLexer.g4 b/grammars/UCLexer.g4 index 2b5efe2c..e7ee4bd2 100644 --- a/grammars/UCLexer.g4 +++ b/grammars/UCLexer.g4 @@ -190,6 +190,8 @@ KW_SELF: 'self'; KW_SUPER: 'super'; KW_POINTER: 'pointer'; KW_EXPANDS: 'expands'; +KW_IMPLEMENTS: 'implements'; +KW_DEPENDSON: 'dependson'; KW_K2CALL: 'k2call'; KW_K2PURE: 'k2pure'; KW_K2OVERRIDE: 'k2override'; diff --git a/grammars/UCParser.g4 b/grammars/UCParser.g4 index e8338cac..44a5075c 100644 --- a/grammars/UCParser.g4 +++ b/grammars/UCParser.g4 @@ -111,7 +111,7 @@ identifier // |'nousercreate' // |'notplaceable' // |'safereplace' - // |'dependson' + | 'dependson' // |'showcategories' // |'hidecategories' // |'guid' @@ -156,7 +156,7 @@ identifier | 'k2override' // |'collapsecategories' // |'dontcollapsecategories' - // |'implements' + | 'implements' // |'classgroup' // |'autoexpandcategories' // |'autocollapsecategories' @@ -260,52 +260,55 @@ classDecl extendsClause: ('extends' | 'expands') id=qualifiedIdentifier; withinClause: 'within' id=qualifiedIdentifier; -classModifier: identifier modifierArguments?; +classModifier + : // in UC3 a class can have a custom native name. - // (kwNATIVE modifierArgument?) - // | kwNATIVEREPLICATION - // | kwLOCALIZED // UC1 - // | kwABSTRACT - // | kwPEROBJECTCONFIG - // | kwTRANSIENT - // | kwEXPORT - // | kwNOEXPORT - // | kwNOUSERCREATE - // | kwSAFEREPLACE - // | (kwCONFIG modifierArgument?) + (KW_NATIVE modifierArgument?) #nativeModifier + // | KW_NATIVEREPLICATION + // | KW_LOCALIZED // UC1 + // | KW_ABSTRACT + // | KW_PEROBJECTCONFIG + | KW_TRANSIENT #transientModifier + | KW_EXPORT #exportModifier + // | KW_NOEXPORT + // | KW_NOUSERCREATE + // | KW_SAFEREPLACE + // | (KW_CONFIG modifierArgument?) // // UC2+ - // | kwPLACEABLE - // | kwNOTPLACEABLE - // | kwCACHEEXEMPT // UT2004 - // | kwHIDEDROPDOWN - // | kwEXPORTSTRUCTS - // | kwINSTANCED - // | kwPARSECONFIG - // | kwEDITINLINENEW - // | kwNOTEDITINLINENEW - // | (kwDEPENDSON modifierArguments) - // | (kwCOLLAPSECATEGORIES modifierArguments) - // | (kwDONTCOLLAPSECATEGORIES modifierArguments?) - // | (kwSHOWCATEGORIES modifierArguments) - // | (kwHIDECATEGORIES modifierArguments) - // | (kwGUID (LPAREN INTEGER COMMA INTEGER COMMA INTEGER COMMA INTEGER RPAREN)) + // | KW_PLACEABLE + // | KW_NOTPLACEABLE + // | KW_CACHEEXEMPT // UT2004 + // | KW_HIDEDROPDOWN + // | KW_EXPORTSTRUCTS + // | KW_INSTANCED + // | KW_PARSECONFIG + // | KW_EDITINLINENEW + // | KW_NOTEDITINLINENEW + | (KW_DEPENDSON OPEN_PARENS identifierArguments CLOSE_PARENS) #dependsOnModifier + // | (KW_COLLAPSECATEGORIES modifierArguments) + // | (KW_DONTCOLLAPSECATEGORIES modifierArguments?) + // | (KW_SHOWCATEGORIES modifierArguments) + // | (KW_HIDECATEGORIES modifierArguments) + // | (KW_GUID (LPAREN INTEGER COMMA INTEGER COMMA INTEGER COMMA INTEGER RPAREN)) // // UC3+ - // | kwNATIVEONLY - // | kwNONTRANSIENT - // | kwPEROBJECTLOCALIZED - // | kwDEPRECATED - // | (kwCLASSREDIRECT modifierArguments) - // | (kwDLLBIND modifierArgument) - // | (kwIMPLEMENTS modifierArgument) - // | (kwCLASSGROUP modifierArguments) - // | (kwAUTOEXPANDCATEGORIES modifierArguments) - // | (kwAUTOCOLLAPSECATEGORIES modifierArguments) - // | (kwDONTAUTOCOLLAPSECATEGORIES modifierArguments) - // | (kwDONTSORTCATEGORIES modifierArguments) - // | (kwINHERITS modifierArguments) + // | KW_NATIVEONLY + // | KW_NONTRANSIENT + // | KW_PEROBJECTLOCALIZED + // | KW_DEPRECATED + // | (KW_CLASSREDIRECT modifierArgument) + // | (KW_DLLBIND modifierArgument) + | (KW_IMPLEMENTS OPEN_PARENS qualifiedIdentifierArguments CLOSE_PARENS) #implementsModifier + // | (KW_CLASSGROUP modifierArguments) + // | (KW_AUTOEXPANDCATEGORIES modifierArguments) + // | (KW_AUTOCOLLAPSECATEGORIES modifierArguments) + // | (KW_DONTAUTOCOLLAPSECATEGORIES modifierArguments) + // | (KW_DONTSORTCATEGORIES modifierArguments) + // | (KW_INHERITS modifierArguments) // // true/false only - // | (kwFORCESCRIPTORDER modifierArgument) + // | (KW_FORCESCRIPTORDER modifierArgument) // ; //ID (LPARENT ID (COMMA ID)* RPARENT)? + | (identifier modifierArguments?) #unidentifiedModifier + ; modifierValue : identifier @@ -320,6 +323,14 @@ modifierArguments : OPEN_PARENS (modifierValue COMMA?)* CLOSE_PARENS ; +identifierArguments + : (identifier COMMA?)* + ; + +qualifiedIdentifierArguments + : (qualifiedIdentifier COMMA?)* + ; + constDecl : 'const' identifier (ASSIGNMENT expr=constValue)? SEMICOLON ; @@ -831,7 +842,7 @@ assignmentExpression primaryExpression : primaryExpression (OPEN_BRACKET arg=expression? CLOSE_BRACKET) #elementAccessExpression | primaryExpression '.' classPropertyAccessSpecifier '.' identifier #propertyClassAccessExpression - | primaryExpression '.' identifier #propertyAccessExpression + | primaryExpression '.' identifier? #propertyAccessExpression | primaryExpression (OPEN_PARENS arguments? CLOSE_PARENS) #callExpression | 'new' (OPEN_PARENS arguments? CLOSE_PARENS)? expr=primaryExpression #newExpression diff --git a/server/src/UC/Parser/ErrorStrategy.ts b/server/src/UC/Parser/ErrorStrategy.ts index eb75f48a..62e45357 100644 --- a/server/src/UC/Parser/ErrorStrategy.ts +++ b/server/src/UC/Parser/ErrorStrategy.ts @@ -1,28 +1,48 @@ -import { DefaultErrorStrategy, Parser, ParserRuleContext, RecognitionException } from 'antlr4ts'; +import { DefaultErrorStrategy, Parser, RecognitionException } from 'antlr4ts'; import { UCParser } from '../antlr/generated/UCParser'; -export class UCMissingSemicolonException extends Error { - constructor(private context: ParserRuleContext) { - super(); - } -} - export class UCErrorStrategy extends DefaultErrorStrategy { - reportError(recognizer: Parser, e: RecognitionException) { - if (e.expectedTokens && e.expectedTokens.contains(UCParser.SEMICOLON)) { - const token = this.constructToken( - recognizer.inputStream.tokenSource, - UCParser.SEMICOLON, ';', - recognizer.currentToken - ); + reportError(recognizer: Parser, e: RecognitionException) { + if (typeof e.expectedTokens === 'undefined') { + super.reportError(recognizer, e); + return; + } + + if (e.expectedTokens.contains(UCParser.SEMICOLON)) { + const token = this.constructToken( + recognizer.inputStream.tokenSource, + UCParser.SEMICOLON, ';', + recognizer.currentToken + ); + + const errorNode = recognizer.createErrorNode(recognizer.context, token); + recognizer.context.addErrorNode(errorNode); + // } else if (e.expectedTokens.contains(UCParser.OPEN_PARENS)) { + // const openToken = this.constructToken( + // recognizer.inputStream.tokenSource, + // UCParser.OPEN_PARENS, '(', + // recognizer.currentToken + // ); + // recognizer.context.addErrorNode(recognizer.createErrorNode(recognizer.context, openToken)); - const errorNode = recognizer.createErrorNode(recognizer.context, token); - recognizer.context.addErrorNode(errorNode); - } + // const closeToken = this.constructToken( + // recognizer.inputStream.tokenSource, + // UCParser.CLOSE_PARENS, ')', + // recognizer.currentToken + // ); + // recognizer.context.addErrorNode(recognizer.createErrorNode(recognizer.context, closeToken)); + // } else if (e.expectedTokens.contains(UCParser.CLOSE_PARENS)) { + // const closeToken = this.constructToken( + // recognizer.inputStream.tokenSource, + // UCParser.CLOSE_PARENS, ')', + // recognizer.currentToken + // ); + // recognizer.context.addErrorNode(recognizer.createErrorNode(recognizer.context, closeToken)); + } - super.reportError(recognizer, e); - } + super.reportError(recognizer, e); + } } export const ERROR_STRATEGY = new UCErrorStrategy(); \ No newline at end of file diff --git a/server/src/UC/Symbols/MethodSymbol.ts b/server/src/UC/Symbols/MethodSymbol.ts index dfd604df..b0916b87 100644 --- a/server/src/UC/Symbols/MethodSymbol.ts +++ b/server/src/UC/Symbols/MethodSymbol.ts @@ -200,6 +200,10 @@ export class UCMethodLikeSymbol extends UCMethodSymbol implements IWithReference return true; } + getTypeFlags() { + return UCTypeFlags.Function | UCTypeFlags.Delegate; + } + protected getTypeKeyword(): string { return '(intrinsic)'; } diff --git a/server/src/UC/Symbols/Package.ts b/server/src/UC/Symbols/Package.ts index b2547279..6cee2c47 100644 --- a/server/src/UC/Symbols/Package.ts +++ b/server/src/UC/Symbols/Package.ts @@ -19,6 +19,16 @@ export class SymbolsTable implements ISymbolContainer { return this.symbols.values() as IterableIterator; } + *getTypes(type: UCTypeFlags): Generator { + for (let symbol of this.symbols.values()) { + if ((symbol.getTypeFlags() & type) === 0) { + continue; + } + yield symbol as C; + } + return []; + } + addSymbol(symbol: T): number { return this.addKey(getSymbolHash(symbol), symbol); } diff --git a/server/src/UC/Symbols/ScriptStructSymbol.ts b/server/src/UC/Symbols/ScriptStructSymbol.ts index 629926cb..6f95d93c 100644 --- a/server/src/UC/Symbols/ScriptStructSymbol.ts +++ b/server/src/UC/Symbols/ScriptStructSymbol.ts @@ -27,6 +27,34 @@ export class UCScriptStructSymbol extends UCStructSymbol { return `struct ${this.getPath()}`; } + getCompletionSymbols(document: UCDocument, _context: string, type?: UCTypeFlags) { + const symbols: ISymbol[] = []; + for (let child = this.children; child; child = child.next) { + if (typeof type !== 'undefined' && (child.getTypeFlags() & type) === 0) { + continue; + } + if (child.acceptCompletion(document, this)) { + symbols.push(child); + } + } + + for (let parent = this.super; parent; parent = parent.super) { + if ((parent.getTypeFlags() & UCTypeFlags.Struct) === 0) { + break; + } + + for (let child = parent.children; child; child = child.next) { + if (typeof type !== 'undefined' && (child.getTypeFlags() & type) === 0) { + continue; + } + if (child.acceptCompletion(document, this)) { + symbols.push(child); + } + } + } + return symbols as C[]; + } + acceptCompletion(_document: UCDocument, context: UCSymbol): boolean { return (context instanceof UCPropertySymbol || context instanceof UCMethodSymbol); } diff --git a/server/src/UC/Symbols/StructSymbol.ts b/server/src/UC/Symbols/StructSymbol.ts index 7d82f418..104c3b65 100644 --- a/server/src/UC/Symbols/StructSymbol.ts +++ b/server/src/UC/Symbols/StructSymbol.ts @@ -24,10 +24,10 @@ export class UCStructSymbol extends UCFieldSymbol implements ISymbolContainer(document: UCDocument, _context: string, kind?: UCTypeFlags) { + getCompletionSymbols(document: UCDocument, _context: string, type?: UCTypeFlags) { const symbols: ISymbol[] = []; for (let child = this.children; child; child = child.next) { - if (typeof kind !== 'undefined' && child.getTypeFlags() !== kind) { + if (typeof type !== 'undefined' && (child.getTypeFlags() & type) === 0) { continue; } if (child.acceptCompletion(document, this)) { @@ -38,7 +38,7 @@ export class UCStructSymbol extends UCFieldSymbol implements ISymbolContainer implements UCPreprocessorParserVisitor, UCParserVisitor, ANTLRErrorListener { +export class DocumentASTWalker extends AbstractParseTreeVisitor implements UCPreprocessorParserVisitor, UCParserVisitor, ANTLRErrorListener { private scopes: ISymbolContainer[] = []; tokenStream: CommonTokenStream | undefined; @@ -242,7 +242,7 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor(); + const modifierArgumentNodes = ctx.identifierArguments(); + if (modifierArgumentNodes) { + symbol.dependsOnTypes = modifierArgumentNodes + .identifier() + .map(valueNode => { + const identifier: Identifier = valueNode.accept(this); + const typeSymbol = new UCObjectTypeSymbol(identifier, undefined, UCTypeFlags.Class); + return typeSymbol; + }); + } + } + + visitImplementsModifier(ctx: UCGrammar.ImplementsModifierContext) { + const symbol = this.scope(); + const modifierArgumentNodes = ctx.qualifiedIdentifierArguments(); + if (modifierArgumentNodes) { + symbol.implementsTypes = modifierArgumentNodes + .qualifiedIdentifier() + .map(valueNode => { + const typeSymbol = createQualifiedType(valueNode, UCTypeFlags.Class); + return typeSymbol; + }); + } + } + visitConstDecl(ctx: UCGrammar.ConstDeclContext) { const identifier: Identifier = idFromCtx(ctx.identifier()); const symbol = new UCConstSymbol(identifier, rangeFromBounds(ctx.start, ctx.stop)); @@ -754,34 +747,33 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor(this); + expression.expression = ctx._expr?.accept(this); return expression; } @@ -1307,10 +1299,16 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor(this); + expression.left = primaryNode.accept(this); const idNode = ctx.identifier(); - expression.member = memberFromIdCtx(idNode); + if (idNode) { + expression.member = memberFromIdCtx(idNode); + } else { + this.document.nodes.push(new ErrorDiagnostic(rangeFromBound(ctx.stop!), + `Identifier expected.` + )); + } return expression; } @@ -1318,10 +1316,16 @@ export class DocumentASTWalker extends AbstractParseTreeVisitor(this); + expression.left = primaryNode.accept(this); const idNode = ctx.identifier(); - expression.member = memberFromIdCtx(idNode); + if (idNode) { + expression.member = memberFromIdCtx(idNode); + } else { + this.document.nodes.push(new ErrorDiagnostic(rangeFromBound(ctx.stop!), + `Identifier expected.` + )); + } return expression; } diff --git a/server/src/UC/expressions.ts b/server/src/UC/expressions.ts index 711daa5e..4c753ff5 100644 --- a/server/src/UC/expressions.ts +++ b/server/src/UC/expressions.ts @@ -16,401 +16,407 @@ import { import { SymbolWalker } from './symbolWalker'; export interface IExpression { - getRange(): Range; - getMemberSymbol(): ISymbol | undefined; - getType(): ITypeSymbol | undefined; - getSymbolAtPos(position: Position): ISymbol | undefined; - getValue(): number | undefined; + getRange(): Range; + getMemberSymbol(): ISymbol | undefined; + getType(): ITypeSymbol | undefined; + getSymbolAtPos(position: Position): ISymbol | undefined; + getValue(): number | undefined; - // TODO: Consider using visitor pattern to indexize. - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo): void; - accept(visitor: SymbolWalker): Result; + // TODO: Consider using visitor pattern to indexize. + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo): void; + accept(visitor: SymbolWalker): Result; } export abstract class UCExpression implements IExpression { - constructor(protected range: Range) { - } + constructor(protected range: Range) { + } - getRange(): Range { - return this.range; - } + getRange(): Range { + return this.range; + } - getMemberSymbol(): ISymbol | undefined { - return undefined; - } + getMemberSymbol(): ISymbol | undefined { + return undefined; + } - getType(): ITypeSymbol | undefined { - return undefined; - } + getType(): ITypeSymbol | undefined { + return undefined; + } - getSymbolAtPos(position: Position): ISymbol | undefined { - if (!intersectsWith(this.getRange(), position)) { - return undefined; - } - const symbol = this.getContainedSymbolAtPos(position); - return symbol; - } + getSymbolAtPos(position: Position): ISymbol | undefined { + if (!intersectsWith(this.getRange(), position)) { + return undefined; + } + const symbol = this.getContainedSymbolAtPos(position); + return symbol; + } - getValue(): number | undefined { - return undefined; - } + getValue(): number | undefined { + return undefined; + } - abstract getContainedSymbolAtPos(position: Position): ISymbol | undefined; - index(_document: UCDocument, _context?: UCStructSymbol, _info?: IContextInfo): void { } + abstract getContainedSymbolAtPos(position: Position): ISymbol | undefined; + index(_document: UCDocument, _context?: UCStructSymbol, _info?: IContextInfo): void { } - accept(visitor: SymbolWalker): Result { - return visitor.visitExpression(this); - } + accept(visitor: SymbolWalker): Result { + return visitor.visitExpression(this); + } } export class UCParenthesizedExpression extends UCExpression { - public expression?: IExpression; + public expression?: IExpression; - getMemberSymbol() { - return this.expression?.getMemberSymbol(); - } + getMemberSymbol() { + return this.expression?.getMemberSymbol(); + } - getType() { - return this.expression?.getType(); - } + getType() { + return this.expression?.getType(); + } - getContainedSymbolAtPos(position: Position) { - const symbol = this.expression?.getSymbolAtPos(position); - return symbol; - } + getContainedSymbolAtPos(position: Position) { + const symbol = this.expression?.getSymbolAtPos(position); + return symbol; + } - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.expression?.index(document, context, info); - } + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.expression?.index(document, context, info); + } } export class UCArrayCountExpression extends UCExpression { - public argument?: IExpression; + public argument?: IExpression; - getMemberSymbol() { - return this.argument?.getMemberSymbol(); - } + getMemberSymbol() { + return this.argument?.getMemberSymbol(); + } - getType() { - return this.argument?.getType(); - } + getType() { + return this.argument?.getType(); + } - getContainedSymbolAtPos(position: Position) { - const symbol = this.argument?.getSymbolAtPos(position); - return symbol; - } + getContainedSymbolAtPos(position: Position) { + const symbol = this.argument?.getSymbolAtPos(position); + return symbol; + } - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.argument?.index(document, context, info); - } + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.argument?.index(document, context, info); + } } export class UCEmptyArgument extends UCExpression { - getContainedSymbolAtPos(_position: Position) { - return undefined; - } + getContainedSymbolAtPos(_position: Position) { + return undefined; + } } export class UCCallExpression extends UCExpression { - public expression!: IExpression; - public arguments?: Array; - - getMemberSymbol() { - return this.expression.getMemberSymbol(); - } - - getType() { - const type = this.expression.getType(); - if (type) { - const symbol = type.getRef(); - if (symbol instanceof UCMethodSymbol) { - const returnValue = symbol.returnValue; - if (returnValue) { - if (returnValue.isCoerced() && this.arguments) { - const firstArgumentType = this.arguments[0]?.getType(); - return resolveType(firstArgumentType); - } - return resolveType(returnValue.getType()); - } - return undefined; - } - } - return type; - } - - getContainedSymbolAtPos(position: Position) { - const symbol = this.expression.getSymbolAtPos(position); - if (symbol) { - return symbol; - } - - if (this.arguments) for (let arg of this.arguments) { - const symbol = arg.getSymbolAtPos(position); - if (symbol) { - return symbol; - } - } - } - - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - // Note: Intentionally passing a clean info object. - this.expression.index(document, context, { hasArguments: true }); - - const type = this.expression.getType(); - const symbol = type?.getRef(); - if (symbol instanceof UCMethodSymbol) { - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { - const arg = this.arguments[i]; - const param = symbol.params?.[i]; - const expectedFlags = param?.getType()?.getTypeFlags() || UCTypeFlags.Error; - arg.index(document, context, { - typeFlags: expectedFlags, - inAssignment: param ? param.isOut() : undefined - }); - } - } else { - // TODO: Handle call on array?? - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { - const arg = this.arguments[i]; - arg.index(document, context, info); - } - } - } + public expression!: IExpression; + public arguments?: Array; + + getMemberSymbol() { + return this.expression.getMemberSymbol(); + } + + getType() { + const type = this.expression.getType(); + if (type) { + const symbol = type.getRef(); + if (symbol instanceof UCMethodSymbol) { + const returnValue = symbol.returnValue; + if (returnValue) { + if (returnValue.isCoerced() && this.arguments) { + const firstArgumentType = this.arguments[0]?.getType(); + return resolveType(firstArgumentType); + } + return resolveType(returnValue.getType()); + } + return undefined; + } + } + return type; + } + + // FIXME: Yeah... easier than re-doing how symbols are fetched :P + static hack_getTypeIfNoSymbol: boolean | undefined; + getContainedSymbolAtPos(position: Position) { + const symbol = this.expression.getSymbolAtPos(position); + if (symbol) { + return symbol; + } + + if (this.arguments) for (let arg of this.arguments) { + const symbol = arg.getSymbolAtPos(position); + if (symbol) { + return symbol; + } + } + + if (UCCallExpression.hack_getTypeIfNoSymbol) { + return this.getType(); + } + } + + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + // Note: Intentionally passing a clean info object. + this.expression.index(document, context, { hasArguments: true }); + + const type = this.expression.getType(); + const symbol = type?.getRef(); + if (symbol instanceof UCMethodSymbol) { + if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { + const arg = this.arguments[i]; + const param = symbol.params?.[i]; + const expectedFlags = param?.getType()?.getTypeFlags() || UCTypeFlags.Error; + arg.index(document, context, { + typeFlags: expectedFlags, + inAssignment: param ? param.isOut() : undefined + }); + } + } else { + // TODO: Handle call on array?? + if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { + const arg = this.arguments[i]; + arg.index(document, context, info); + } + } + } } export class UCElementAccessExpression extends UCExpression { - public expression!: IExpression; - public argument?: IExpression; - - getMemberSymbol() { - return this.expression?.getMemberSymbol(); - } - - // Returns the type we are working with after [] has taken affect, this means we return undefined if the type is invalid. - getType() { - const type = this.expression?.getType(); - if (type instanceof UCArrayTypeSymbol) { - // Resolve metaclass class to Actor - if (type.baseType instanceof UCObjectTypeSymbol && type.baseType.baseType) { - return type.baseType.baseType; - } - return type.baseType; - } else { - const symbol = this.getMemberSymbol(); - if (symbol instanceof UCPropertySymbol && symbol.isFixedArray()) { - // metaclass is resolved in @UCMemberExpression's .getType - return type; - } - } - return undefined; - } - - getContainedSymbolAtPos(position: Position) { - return this.expression?.getSymbolAtPos(position) ?? this.argument?.getSymbolAtPos(position); - } - - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.expression?.index(document, context, info); - this.argument?.index(document, context, info); - } + public expression!: IExpression; + public argument?: IExpression; + + getMemberSymbol() { + return this.expression?.getMemberSymbol(); + } + + // Returns the type we are working with after [] has taken affect, this means we return undefined if the type is invalid. + getType() { + const type = this.expression?.getType(); + if (type instanceof UCArrayTypeSymbol) { + // Resolve metaclass class to Actor + if (type.baseType instanceof UCObjectTypeSymbol && type.baseType.baseType) { + return type.baseType.baseType; + } + return type.baseType; + } else { + const symbol = this.getMemberSymbol(); + if (symbol instanceof UCPropertySymbol && symbol.isFixedArray()) { + // metaclass is resolved in @UCMemberExpression's .getType + return type; + } + } + return undefined; + } + + getContainedSymbolAtPos(position: Position) { + return this.expression?.getSymbolAtPos(position) ?? this.argument?.getSymbolAtPos(position); + } + + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.expression?.index(document, context, info); + this.argument?.index(document, context, info); + } } export class UCDefaultElementAccessExpression extends UCElementAccessExpression { - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.expression?.index(document, context, info); - // this.argument?.index(document, context, info); - - if (this.argument && this.argument instanceof UCIdentifierLiteralExpression) { - const id = this.argument.id; - const symbol = (context instanceof UCStructSymbol && context.findSuperSymbol(id.name)) ?? getEnumMember(id.name); - if (symbol) { - const type = new UCObjectTypeSymbol(id); - type.setReference(symbol, document); - this.argument.typeRef = type; - } - } - } + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.expression?.index(document, context, info); + // this.argument?.index(document, context, info); + + if (this.argument && this.argument instanceof UCIdentifierLiteralExpression) { + const id = this.argument.id; + const symbol = (context instanceof UCStructSymbol && context.findSuperSymbol(id.name)) ?? getEnumMember(id.name); + if (symbol) { + const type = new UCObjectTypeSymbol(id); + type.setReference(symbol, document); + this.argument.typeRef = type; + } + } + } } export class UCPropertyAccessExpression extends UCExpression { - public left!: IExpression; - public member!: UCMemberExpression; + public left!: IExpression; + public member: UCMemberExpression | undefined; - getMemberSymbol() { - return this.member.getMemberSymbol(); - } + getMemberSymbol() { + return this.member?.getMemberSymbol(); + } - getType() { - return this.member.getType(); - } + getType() { + return this.member?.getType(); + } - getContainedSymbolAtPos(position: Position) { - return this.left.getSymbolAtPos(position) ?? this.member.getSymbolAtPos(position); - } + getContainedSymbolAtPos(position: Position) { + return this.member?.getSymbolAtPos(position) ?? this.left.getSymbolAtPos(position); + } - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - // DO NOT PASS @info, only our right expression needs access to @info. - this.left.index(document, context); + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + // DO NOT PASS @info, only our right expression needs access to @info. + this.left.index(document, context); - const memberContext = this.left.getType()?.getRef(); - this.member.index(document, memberContext as UCStructSymbol, info); - } + const memberContext = this.left.getType()?.getRef(); + this.member?.index(document, memberContext as UCStructSymbol, info); + } } export class UCConditionalExpression extends UCExpression { - public condition!: IExpression; - public true?: IExpression; - public false?: IExpression; + public condition!: IExpression; + public true?: IExpression; + public false?: IExpression; - getMemberSymbol() { - return this.true?.getMemberSymbol(); - } + getMemberSymbol() { + return this.true?.getMemberSymbol(); + } - getType() { - return this.true?.getType(); - } + getType() { + return this.true?.getType(); + } - getContainedSymbolAtPos(position: Position) { - return this.condition.getSymbolAtPos(position) ?? this.true?.getSymbolAtPos(position) ?? this.false?.getSymbolAtPos(position); - } + getContainedSymbolAtPos(position: Position) { + return this.condition.getSymbolAtPos(position) ?? this.true?.getSymbolAtPos(position) ?? this.false?.getSymbolAtPos(position); + } - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.condition.index(document, context, info); - this.true?.index(document, context, info); - this.false?.index(document, context, info); - } + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.condition.index(document, context, info); + this.true?.index(document, context, info); + this.false?.index(document, context, info); + } } export abstract class UCBaseOperatorExpression extends UCExpression { - public expression!: IExpression; - public operator!: UCSymbolReference; + public expression!: IExpression; + public operator!: UCSymbolReference; - getMemberSymbol() { - return this.expression?.getMemberSymbol(); - } + getMemberSymbol() { + return this.expression?.getMemberSymbol(); + } - getType() { - const operatorSymbol = this.operator.getRef(); - if (operatorSymbol instanceof UCBaseOperatorSymbol) { - return operatorSymbol.getType(); - } - } + getType() { + const operatorSymbol = this.operator.getRef(); + if (operatorSymbol instanceof UCBaseOperatorSymbol) { + return operatorSymbol.getType(); + } + } - getContainedSymbolAtPos(position: Position) { - const symbol = this.operator.getSymbolAtPos(position); - if (symbol && this.operator.getRef()) { - return symbol; - } - return this.expression.getSymbolAtPos(position); - } + getContainedSymbolAtPos(position: Position) { + const symbol = this.operator.getSymbolAtPos(position); + if (symbol && this.operator.getRef()) { + return symbol; + } + return this.expression.getSymbolAtPos(position); + } - index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { - this.expression.index(document, context, info); - } + index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { + this.expression.index(document, context, info); + } } export class UCPostOperatorExpression extends UCBaseOperatorExpression { - index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { - super.index(document, context, info); - if (this.operator && this.expression) { - const type = this.expression.getType(); - if (type) { - const opId = this.operator.getName(); - const operatorSymbol = findSymbol(context, opId, ( - symbol => symbol instanceof UCBaseOperatorSymbol - && symbol.isPostOperator() - && symbol.params !== undefined - && symbol.params.length === 1 - && typeMatchesFlags(symbol.params[0].getType(), type, symbol.params[0].isCoerced()) - )); - if (operatorSymbol) { - this.operator.setReference(operatorSymbol, document); - } - } - } - } + index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { + super.index(document, context, info); + if (this.operator && this.expression) { + const type = this.expression.getType(); + if (type) { + const opId = this.operator.getName(); + const operatorSymbol = findSymbol(context, opId, ( + symbol => symbol instanceof UCBaseOperatorSymbol + && symbol.isPostOperator() + && symbol.params !== undefined + && symbol.params.length === 1 + && typeMatchesFlags(symbol.params[0].getType(), type, symbol.params[0].isCoerced()) + )); + if (operatorSymbol) { + this.operator.setReference(operatorSymbol, document); + } + } + } + } } export class UCPreOperatorExpression extends UCBaseOperatorExpression { - index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { - super.index(document, context, info); - if (this.operator && this.expression) { - const type = this.expression.getType(); - if (type) { - const opId = this.operator.getName(); - const operatorSymbol = findSymbol(context, opId, ( - symbol => symbol instanceof UCBaseOperatorSymbol - && symbol.isPreOperator() - && symbol.params !== undefined - && symbol.params.length === 1 - && typeMatchesFlags(symbol.params[0].getType(), type, symbol.params[0].isCoerced()) - )); - if (operatorSymbol) { - this.operator.setReference(operatorSymbol, document); - } - } - } - } + index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { + super.index(document, context, info); + if (this.operator && this.expression) { + const type = this.expression.getType(); + if (type) { + const opId = this.operator.getName(); + const operatorSymbol = findSymbol(context, opId, ( + symbol => symbol instanceof UCBaseOperatorSymbol + && symbol.isPreOperator() + && symbol.params !== undefined + && symbol.params.length === 1 + && typeMatchesFlags(symbol.params[0].getType(), type, symbol.params[0].isCoerced()) + )); + if (operatorSymbol) { + this.operator.setReference(operatorSymbol, document); + } + } + } + } } export class UCBinaryOperatorExpression extends UCExpression { - public left?: IExpression; - public operator?: UCSymbolReference; - public right?: IExpression; - - getMemberSymbol() { - return this.operator?.getRef(); - } - - getType() { - const operatorSymbol = this.operator?.getRef(); - if (operatorSymbol instanceof UCBinaryOperatorSymbol) { - return operatorSymbol.getType(); - } - } - - getContainedSymbolAtPos(position: Position) { - const symbol = this.operator?.getSymbolAtPos(position); - if (symbol && this.operator!.getRef()) { - return symbol; - } - return this.left?.getSymbolAtPos(position) ?? this.right?.getSymbolAtPos(position); - } - - index(document: UCDocument, context: UCStructSymbol, info: IContextInfo = {}) { - if (this.left) { - this.left.index(document, context, info); - - const type = this.left.getType(); - info.typeFlags = type?.getTypeFlags(); - } - this.right?.index(document, context, info); - - if (this.operator) { - const leftType = this.left?.getType(); - const rightType = this.right?.getType(); - if (leftType && rightType) { - const opId = this.operator.getName(); - const operatorSymbol = findSymbol(context, opId, ( - symbol => symbol instanceof UCBaseOperatorSymbol - && symbol.isOperator() - && symbol.params !== undefined - && symbol.params.length === 2 - && typeMatchesFlags(symbol.params[0].getType(), leftType, symbol.params[0].isCoerced()) - && typeMatchesFlags(symbol.params[1].getType(), rightType, symbol.params[0].isCoerced()) - )); - if (operatorSymbol) { - this.operator.setReference(operatorSymbol, document); - } - } - } - } + public left?: IExpression; + public operator?: UCSymbolReference; + public right?: IExpression; + + getMemberSymbol() { + return this.operator?.getRef(); + } + + getType() { + const operatorSymbol = this.operator?.getRef(); + if (operatorSymbol instanceof UCBinaryOperatorSymbol) { + return operatorSymbol.getType(); + } + } + + getContainedSymbolAtPos(position: Position) { + const symbol = this.operator?.getSymbolAtPos(position); + if (symbol && this.operator!.getRef()) { + return symbol; + } + return this.left?.getSymbolAtPos(position) ?? this.right?.getSymbolAtPos(position); + } + + index(document: UCDocument, context: UCStructSymbol, info: IContextInfo = {}) { + if (this.left) { + this.left.index(document, context, info); + + const type = this.left.getType(); + info.typeFlags = type?.getTypeFlags(); + } + this.right?.index(document, context, info); + + if (this.operator) { + const leftType = this.left?.getType(); + const rightType = this.right?.getType(); + if (leftType && rightType) { + const opId = this.operator.getName(); + const operatorSymbol = findSymbol(context, opId, ( + symbol => symbol instanceof UCBaseOperatorSymbol + && symbol.isOperator() + && symbol.params !== undefined + && symbol.params.length === 2 + && typeMatchesFlags(symbol.params[0].getType(), leftType, symbol.params[0].isCoerced()) + && typeMatchesFlags(symbol.params[1].getType(), rightType, symbol.params[0].isCoerced()) + )); + if (operatorSymbol) { + this.operator.setReference(operatorSymbol, document); + } + } + } + } } export class UCAssignmentExpression extends UCBinaryOperatorExpression { - getType() { - return undefined; - } + getType() { + return undefined; + } } export class UCAssignmentOperatorExpression extends UCBinaryOperatorExpression { @@ -418,9 +424,9 @@ export class UCAssignmentOperatorExpression extends UCBinaryOperatorExpression { } export class UCDefaultAssignmentExpression extends UCBinaryOperatorExpression { - getType() { - return undefined; - } + getType() { + return undefined; + } } /** @@ -430,183 +436,183 @@ export class UCDefaultAssignmentExpression extends UCBinaryOperatorExpression { * @method getType will always return the type of instance "propertyMember", because our array operations have no return type. */ export class UCDefaultMemberCallExpression extends UCExpression { - public propertyMember!: UCMemberExpression; - public methodMember!: UCMemberExpression; - public arguments?: Array; - - getMemberSymbol() { - return this.methodMember.getMemberSymbol(); - } - - getType() { - const type = this.propertyMember.getType(); - if (type instanceof UCArrayTypeSymbol) { - // Resolve metaclass class to Actor - if (type.baseType instanceof UCObjectTypeSymbol && type.baseType.baseType) { - return type.baseType.baseType; - } - return type.baseType; - } - return type; - } - - getContainedSymbolAtPos(position: Position) { - const symbol = this.propertyMember.getSymbolAtPos(position) ?? this.methodMember.getSymbolAtPos(position); - if (symbol) { - return symbol; - } - - if (this.arguments) for (let arg of this.arguments) { - const symbol = arg.getSymbolAtPos(position); - if (symbol) { - return symbol; - } - } - } - - index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { - this.propertyMember.index(document, context, info); - - const type = this.propertyMember.getType(); - if (type) { - // As far as I know, default operations are only allowed on arrays. - if (type instanceof UCArrayTypeSymbol) { - const id = this.methodMember.id; - const symbol = DefaultArray.findSuperSymbol(id.name); - if (symbol) { - const type = new UCObjectTypeSymbol(id); - type.setReference(symbol, document); - this.methodMember.typeRef = type; - } - } // else, we don't have to index UCMemberExpressions here. - } - - if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { - const arg = this.arguments[i]; - arg.index(document, context, info); - } - } + public propertyMember!: UCMemberExpression; + public methodMember!: UCMemberExpression; + public arguments?: Array; + + getMemberSymbol() { + return this.methodMember.getMemberSymbol(); + } + + getType() { + const type = this.propertyMember.getType(); + if (type instanceof UCArrayTypeSymbol) { + // Resolve metaclass class to Actor + if (type.baseType instanceof UCObjectTypeSymbol && type.baseType.baseType) { + return type.baseType.baseType; + } + return type.baseType; + } + return type; + } + + getContainedSymbolAtPos(position: Position) { + const symbol = this.propertyMember.getSymbolAtPos(position) ?? this.methodMember.getSymbolAtPos(position); + if (symbol) { + return symbol; + } + + if (this.arguments) for (let arg of this.arguments) { + const symbol = arg.getSymbolAtPos(position); + if (symbol) { + return symbol; + } + } + } + + index(document: UCDocument, context: UCStructSymbol, info?: IContextInfo) { + this.propertyMember.index(document, context, info); + + const type = this.propertyMember.getType(); + if (type) { + // As far as I know, default operations are only allowed on arrays. + if (type instanceof UCArrayTypeSymbol) { + const id = this.methodMember.id; + const symbol = DefaultArray.findSuperSymbol(id.name); + if (symbol) { + const type = new UCObjectTypeSymbol(id); + type.setReference(symbol, document); + this.methodMember.typeRef = type; + } + } // else, we don't have to index UCMemberExpressions here. + } + + if (this.arguments) for (let i = 0; i < this.arguments.length; ++i) { + const arg = this.arguments[i]; + arg.index(document, context, info); + } + } } export class UCMemberExpression extends UCExpression { - public typeRef?: ITypeSymbol; - - constructor(readonly id: Identifier) { - super(id.range); - } - - getMemberSymbol() { - return this.typeRef?.getRef(); - } - - getType() { - const symbol = this.typeRef?.getRef(); - // We resolve UCMethodSymbols in UCCallExpression, because we don't want to return the function's type in assignment expressions... - if (symbol instanceof UCPropertySymbol) { - return symbol.getType(); - } else if (symbol instanceof UCConstSymbol) { - return symbol.expression?.getType(); - } - return this.typeRef; - } - - getContainedSymbolAtPos(_position: Position) { - // To return static types, but! We are not storing the ranges for primitive casts anyway... - // if (this.typeRef instanceof UCPredefinedTypeSymbol) { - // return this.typeRef; - // } - // Only return if we have a RESOLVED reference. - return this.typeRef?.getRef() && this.typeRef; - } - - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - const id = this.id.name; - if (info?.hasArguments) { - // Casting to a int, byte, bool? etc... - const primitiveType = CastTypeSymbolMap.get(id); - if (primitiveType) { - this.typeRef = primitiveType; - return; - } - - // Casting to a Class, Struct, or Enum? - const structSymbol = tryFindClassSymbol(id) || ObjectsTable.getSymbol(id); - if (structSymbol) { - const type = new UCObjectTypeSymbol(this.id); - type.setReference(structSymbol, document); - this.typeRef = type; - return; - } - } - - let member = context instanceof UCStructSymbol && context.findSuperSymbol(id); - if (!member && (!config.checkTypes || (info && !info.hasArguments && (info.typeFlags && info.typeFlags & UCTypeFlags.EnumCoerce) !== 0))) { - member = getEnumMember(id); - } - - if (member) { - const type = new UCObjectTypeSymbol(this.id); - const symbolRef = type.setReference(member, document); - if (symbolRef && info) { - symbolRef.inAssignment = info.inAssignment; - } - this.typeRef = type; - } - } + public typeRef?: ITypeSymbol; + + constructor(readonly id: Identifier) { + super(id.range); + } + + getMemberSymbol() { + return this.typeRef?.getRef(); + } + + getType() { + const symbol = this.typeRef?.getRef(); + // We resolve UCMethodSymbols in UCCallExpression, because we don't want to return the function's type in assignment expressions... + if (symbol instanceof UCPropertySymbol) { + return symbol.getType(); + } else if (symbol instanceof UCConstSymbol) { + return symbol.expression?.getType(); + } + return this.typeRef; + } + + getContainedSymbolAtPos(_position: Position) { + // To return static types, but! We are not storing the ranges for primitive casts anyway... + // if (this.typeRef instanceof UCPredefinedTypeSymbol) { + // return this.typeRef; + // } + // Only return if we have a RESOLVED reference. + return this.typeRef?.getRef() && this.typeRef; + } + + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + const id = this.id.name; + if (info?.hasArguments) { + // Casting to a int, byte, bool? etc... + const primitiveType = CastTypeSymbolMap.get(id); + if (primitiveType) { + this.typeRef = primitiveType; + return; + } + + // Casting to a Class, Struct, or Enum? + const structSymbol = tryFindClassSymbol(id) || ObjectsTable.getSymbol(id); + if (structSymbol) { + const type = new UCObjectTypeSymbol(this.id); + type.setReference(structSymbol, document); + this.typeRef = type; + return; + } + } + + let member = context instanceof UCStructSymbol && context.findSuperSymbol(id); + if (!member && (!config.checkTypes || (info && !info.hasArguments && (info.typeFlags && info.typeFlags & UCTypeFlags.EnumCoerce) !== 0))) { + member = getEnumMember(id); + } + + if (member) { + const type = new UCObjectTypeSymbol(this.id); + const symbolRef = type.setReference(member, document); + if (symbolRef && info) { + symbolRef.inAssignment = info.inAssignment; + } + this.typeRef = type; + } + } } /** * Represents an identifier in a defaultproperties block. e.g. "Class=ClassName", here "ClassName" would be represented by this expression. **/ export class UCIdentifierLiteralExpression extends UCMemberExpression { - index(document: UCDocument, context?: UCObjectSymbol, info?: IContextInfo) { - if (!context || !info || !info.typeFlags) { - return; - } else if ((info.typeFlags & UCTypeFlags.IdentifierTypes) === 0) { - return; - } - // We don't support objects, although this may be true in the check above due the fact that a class is also an Object. - else if (info.typeFlags === UCTypeFlags.Object) { - return; - } - - const id = this.id.name; - - let member: UCSymbol | undefined; - const expectingEnum = (info.typeFlags & UCTypeFlags.EnumCoerce) !== 0; - if (expectingEnum) { - member = getEnumMember(id); - } - - if (!member) { - const expectingClass = info.typeFlags === UCTypeFlags.Class; - if (expectingClass) { - member = tryFindClassSymbol(id); - } - } - - if (!member) { - const expectingDelegate = info.typeFlags === UCTypeFlags.Delegate; - if (expectingDelegate) { - member = context?.findSuperSymbol(id); - } - } - - if (member) { - const type = new UCObjectTypeSymbol(this.id); - type.setReference(member, document); - this.typeRef = type; - } - } + index(document: UCDocument, context?: UCObjectSymbol, info?: IContextInfo) { + if (!context || !info || !info.typeFlags) { + return; + } else if ((info.typeFlags & UCTypeFlags.IdentifierTypes) === 0) { + return; + } + // We don't support objects, although this may be true in the check above due the fact that a class is also an Object. + else if (info.typeFlags === UCTypeFlags.Object) { + return; + } + + const id = this.id.name; + + let member: UCSymbol | undefined; + const expectingEnum = (info.typeFlags & UCTypeFlags.EnumCoerce) !== 0; + if (expectingEnum) { + member = getEnumMember(id); + } + + if (!member) { + const expectingClass = info.typeFlags === UCTypeFlags.Class; + if (expectingClass) { + member = tryFindClassSymbol(id); + } + } + + if (!member) { + const expectingDelegate = info.typeFlags === UCTypeFlags.Delegate; + if (expectingDelegate) { + member = context?.findSuperSymbol(id); + } + } + + if (member) { + const type = new UCObjectTypeSymbol(this.id); + type.setReference(member, document); + this.typeRef = type; + } + } } // Resolves the member for predefined specifiers such as (self, default, static, and global) export class UCPredefinedAccessExpression extends UCMemberExpression { - index(document: UCDocument, _context?: UCStructSymbol) { - const typeRef = new UCObjectTypeSymbol(this.id); - typeRef.setReference(document.class!, document, true); - this.typeRef = typeRef; - } + index(document: UCDocument, _context?: UCStructSymbol) { + const typeRef = new UCObjectTypeSymbol(this.id); + typeRef.setReference(document.class!, document, true); + this.typeRef = typeRef; + } } // Resolves the context for predefined specifiers such as (default, static, and const). @@ -615,351 +621,351 @@ export class UCPropertyClassAccessExpression extends UCPropertyAccessExpression } export class UCSuperExpression extends UCExpression { - public structTypeRef?: UCObjectTypeSymbol; - - // Resolved structRef. - private superStruct?: UCStructSymbol; - - getMemberSymbol() { - return this.superStruct; - } - - getType() { - return this.structTypeRef; - } - - getContainedSymbolAtPos(position: Position) { - if (this.structTypeRef?.getSymbolAtPos(position)) { - return this.structTypeRef; - } - } - - index(document: UCDocument, context: UCStructSymbol) { - context = (context instanceof UCMethodSymbol && context.outer instanceof UCStateSymbol && context.outer.super) - ? context.outer - : document.class!; - - if (this.structTypeRef) { - // FIXME: UE2 doesn't verify inheritance, thus particular exploits are possible by calling a super function through an unrelated class, - // -- this let's programmers write data in different parts of the memory. - // -- Thus should we just be naive and match any type instead? - const symbol = findSuperStruct(context, this.structTypeRef.getName()) || tryFindClassSymbol(this.structTypeRef.getName()); - if (symbol instanceof UCStructSymbol) { - this.structTypeRef.setReference(symbol, document); - this.superStruct = symbol; - } - } else { - this.superStruct = context.super; - if (this.superStruct) { - const type = new UCObjectTypeSymbol(this.superStruct.id, undefined, UCTypeFlags.Class | UCTypeFlags.State); - type.setReference(this.superStruct, document, false); - this.structTypeRef = type; - } - } - } + public structTypeRef?: UCObjectTypeSymbol; + + // Resolved structRef. + private superStruct?: UCStructSymbol; + + getMemberSymbol() { + return this.superStruct; + } + + getType() { + return this.structTypeRef; + } + + getContainedSymbolAtPos(position: Position) { + if (this.structTypeRef?.getSymbolAtPos(position)) { + return this.structTypeRef; + } + } + + index(document: UCDocument, context: UCStructSymbol) { + context = (context instanceof UCMethodSymbol && context.outer instanceof UCStateSymbol && context.outer.super) + ? context.outer + : document.class!; + + if (this.structTypeRef) { + // FIXME: UE2 doesn't verify inheritance, thus particular exploits are possible by calling a super function through an unrelated class, + // -- this let's programmers write data in different parts of the memory. + // -- Thus should we just be naive and match any type instead? + const symbol = findSuperStruct(context, this.structTypeRef.getName()) || tryFindClassSymbol(this.structTypeRef.getName()); + if (symbol instanceof UCStructSymbol) { + this.structTypeRef.setReference(symbol, document); + this.superStruct = symbol; + } + } else { + this.superStruct = context.super; + if (this.superStruct) { + const type = new UCObjectTypeSymbol(this.superStruct.id, undefined, UCTypeFlags.Class | UCTypeFlags.State); + type.setReference(this.superStruct, document, false); + this.structTypeRef = type; + } + } + } } export class UCNewExpression extends UCCallExpression { - // TODO: Implement pseudo new operator for hover info? + // TODO: Implement pseudo new operator for hover info? } export abstract class UCLiteral extends UCExpression { - getContainedSymbolAtPos(_position: Position): ISymbol | undefined { - return undefined; - } + getContainedSymbolAtPos(_position: Position): ISymbol | undefined { + return undefined; + } } export class UCNoneLiteral extends UCLiteral { - getType() { - return StaticNoneType; - } + getType() { + return StaticNoneType; + } - toString() { - return 'None'; - } + toString() { + return 'None'; + } } export class UCStringLiteral extends UCLiteral { - getType() { - return StaticStringType; - } + getType() { + return StaticStringType; + } - toString() { - return '""'; - } + toString() { + return '""'; + } } export class UCNameLiteral extends UCLiteral { - getType() { - return StaticNameType; - } + getType() { + return StaticNameType; + } - toString() { - return "''"; - } + toString() { + return "''"; + } } export class UCBoolLiteral extends UCLiteral { - value!: boolean; + value!: boolean; - getType() { - return StaticBoolType; - } + getType() { + return StaticBoolType; + } - toString() { - return String(this.value); - } + toString() { + return String(this.value); + } } export class UCFloatLiteral extends UCLiteral { - value!: number; + value!: number; - getType() { - return StaticFloatType; - } + getType() { + return StaticFloatType; + } - getValue(): number { - return this.value; - } + getValue(): number { + return this.value; + } } export class UCIntLiteral extends UCLiteral { - value!: number; + value!: number; - getType() { - return StaticIntType; - } + getType() { + return StaticIntType; + } - getValue(): number { - return this.value; - } + getValue(): number { + return this.value; + } } export class UCByteLiteral extends UCLiteral { - value!: number; + value!: number; - getType() { - return StaticByteType; - } + getType() { + return StaticByteType; + } - getValue(): number { - return this.value; - } + getValue(): number { + return this.value; + } } export class UCObjectLiteral extends UCExpression { - public castRef!: ITypeSymbol; - public objectRef?: UCObjectTypeSymbol | UCQualifiedTypeSymbol; - - getMemberSymbol() { - return this.objectRef?.getRef() || this.castRef.getRef(); - } - - getType() { - // TODO: Coerce to specified class if castRef is of type 'Class' - if ((this.castRef.getTypeFlags() & UCTypeFlags.Class) === UCTypeFlags.Class) { - return this.objectRef || this.castRef; - } - return this.castRef; - } - - getContainedSymbolAtPos(position: Position) { - if (intersectsWith(this.castRef.getRange(), position)) { - return this.castRef.getRef() && this.castRef; - } - return this.objectRef?.getSymbolAtPos(position); - } - - index(document: UCDocument, context: UCStructSymbol) { - this.castRef.index(document, context); - - if (this.objectRef) { - if (this.objectRef instanceof UCQualifiedTypeSymbol) { - for (let next: UCQualifiedTypeSymbol | undefined = this.objectRef; next; next = next.left) { - let symbol: ISymbol | undefined; - if (next.left) { - const hash = getSymbolOuterHash(getSymbolHash(next), getSymbolHash(next.left)); - symbol = OuterObjectsTable.getSymbol(hash, this.castRef.getTypeFlags()); - // if (!symbol) { - // break; - // } - } else { - const hash = getSymbolHash(next); - symbol = ObjectsTable.getSymbol(hash, UCTypeFlags.Package); - } - if (symbol) { - next.type.setReference(symbol, document); - } - } - } else { - const id = this.objectRef.getName(); - const symbol = ObjectsTable.getSymbol(id, this.castRef.getTypeFlags()) - || findOrIndexClassSymbol(id) - // FIXME: Hacky case for literals like Property'TempColor', only enums and structs are added to the objects table. - || context.findSuperSymbol(id); - if (symbol) { - this.objectRef.setReference(symbol, document); - } - } - this.objectRef.index(document, context); - } - } + public castRef!: ITypeSymbol; + public objectRef?: UCObjectTypeSymbol | UCQualifiedTypeSymbol; + + getMemberSymbol() { + return this.objectRef?.getRef() || this.castRef.getRef(); + } + + getType() { + // TODO: Coerce to specified class if castRef is of type 'Class' + if ((this.castRef.getTypeFlags() & UCTypeFlags.Class) === UCTypeFlags.Class) { + return this.objectRef || this.castRef; + } + return this.castRef; + } + + getContainedSymbolAtPos(position: Position) { + if (intersectsWith(this.castRef.getRange(), position)) { + return this.castRef.getRef() && this.castRef; + } + return this.objectRef?.getSymbolAtPos(position); + } + + index(document: UCDocument, context: UCStructSymbol) { + this.castRef.index(document, context); + + if (this.objectRef) { + if (this.objectRef instanceof UCQualifiedTypeSymbol) { + for (let next: UCQualifiedTypeSymbol | undefined = this.objectRef; next; next = next.left) { + let symbol: ISymbol | undefined; + if (next.left) { + const hash = getSymbolOuterHash(getSymbolHash(next), getSymbolHash(next.left)); + symbol = OuterObjectsTable.getSymbol(hash, this.castRef.getTypeFlags()); + // if (!symbol) { + // break; + // } + } else { + const hash = getSymbolHash(next); + symbol = ObjectsTable.getSymbol(hash, UCTypeFlags.Package); + } + if (symbol) { + next.type.setReference(symbol, document); + } + } + } else { + const id = this.objectRef.getName(); + const symbol = ObjectsTable.getSymbol(id, this.castRef.getTypeFlags()) + || findOrIndexClassSymbol(id) + // FIXME: Hacky case for literals like Property'TempColor', only enums and structs are added to the objects table. + || context.findSuperSymbol(id); + if (symbol) { + this.objectRef.setReference(symbol, document); + } + } + this.objectRef.index(document, context); + } + } } // Struct literals are limited to Vector, Rotator, and Range. export abstract class UCStructLiteral extends UCExpression { - structType!: UCObjectTypeSymbol; + structType!: UCObjectTypeSymbol; - getMemberSymbol() { - return this.structType.getRef(); - } + getMemberSymbol() { + return this.structType.getRef(); + } - getType() { - return this.structType; - } + getType() { + return this.structType; + } - getContainedSymbolAtPos(_position: Position) { - // Only return if we have a RESOLVED reference. - return this.structType.getRef() && this.structType as ISymbol; - } + getContainedSymbolAtPos(_position: Position) { + // Only return if we have a RESOLVED reference. + return this.structType.getRef() && this.structType as ISymbol; + } - index(document: UCDocument, _context?: UCStructSymbol) { - if (this.structType.getRef()) { - return; - } + index(document: UCDocument, _context?: UCStructSymbol) { + if (this.structType.getRef()) { + return; + } - const symbol = ObjectsTable.getSymbol(this.structType.getName(), UCTypeFlags.Struct, CORE_PACKAGE); - if (symbol) { - this.structType.setReference(symbol, document, true); - } - } + const symbol = ObjectsTable.getSymbol(this.structType.getName(), UCTypeFlags.Struct, CORE_PACKAGE); + if (symbol) { + this.structType.setReference(symbol, document, true); + } + } } export class UCDefaultStructLiteral extends UCExpression { - public arguments?: Array; + public arguments?: Array; - getContainedSymbolAtPos(position: Position) { - if (this.arguments) for (let arg of this.arguments) { - const symbol = arg?.getSymbolAtPos(position); - if (symbol) { - return symbol; - } - } - } + getContainedSymbolAtPos(position: Position) { + if (this.arguments) for (let arg of this.arguments) { + const symbol = arg?.getSymbolAtPos(position); + if (symbol) { + return symbol; + } + } + } - index(document: UCDocument, context?: UCStructSymbol) { - if (this.arguments) for (let arg of this.arguments) { - arg?.index(document, context); - } - } + index(document: UCDocument, context?: UCStructSymbol) { + if (this.arguments) for (let arg of this.arguments) { + arg?.index(document, context); + } + } } export class UCVectLiteral extends UCStructLiteral { - structType = StaticVectorType; + structType = StaticVectorType; - getContainedSymbolAtPos(_position: Position) { - return VectMethodLike as unknown as UCSymbolReference; - } + getContainedSymbolAtPos(_position: Position) { + return VectMethodLike as unknown as UCSymbolReference; + } } export class UCRotLiteral extends UCStructLiteral { - structType = StaticRotatorType; + structType = StaticRotatorType; - getContainedSymbolAtPos(_position: Position) { - return RotMethodLike as unknown as UCSymbolReference; - } + getContainedSymbolAtPos(_position: Position) { + return RotMethodLike as unknown as UCSymbolReference; + } } export class UCRngLiteral extends UCStructLiteral { - structType = StaticRangeType; + structType = StaticRangeType; - getContainedSymbolAtPos(_position: Position) { - return RngMethodLike as unknown as UCSymbolReference; - } + getContainedSymbolAtPos(_position: Position) { + return RngMethodLike as unknown as UCSymbolReference; + } } // See also @UCArrayCountExpression, this literal is restricted to const value tokens. export class UCArrayCountLiteral extends UCLiteral { - public argumentRef?: ITypeSymbol; + public argumentRef?: ITypeSymbol; - getValue() { - const symbol = this.argumentRef?.getRef(); - return symbol instanceof UCPropertySymbol && symbol.getArrayDimSize() || undefined; - } + getValue() { + const symbol = this.argumentRef?.getRef(); + return symbol instanceof UCPropertySymbol && symbol.getArrayDimSize() || undefined; + } - getType() { - return this.argumentRef; - } + getType() { + return this.argumentRef; + } - getContainedSymbolAtPos(position: Position) { - return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; - } + getContainedSymbolAtPos(position: Position) { + return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; + } - index(document: UCDocument, context?: UCStructSymbol) { - super.index(document, context); - this.argumentRef?.index(document, context!); - } + index(document: UCDocument, context?: UCStructSymbol) { + super.index(document, context); + this.argumentRef?.index(document, context!); + } } export class UCNameOfLiteral extends UCLiteral { - public argumentRef?: ITypeSymbol; + public argumentRef?: ITypeSymbol; - getType() { - return this.argumentRef; - } + getType() { + return this.argumentRef; + } - getContainedSymbolAtPos(position: Position) { - return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; - } + getContainedSymbolAtPos(position: Position) { + return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; + } - index(document: UCDocument, context?: UCStructSymbol) { - super.index(document, context); - this.argumentRef?.index(document, context!); - } + index(document: UCDocument, context?: UCStructSymbol) { + super.index(document, context); + this.argumentRef?.index(document, context!); + } } export class UCSizeOfLiteral extends UCLiteral { - public argumentRef?: ITypeSymbol; + public argumentRef?: ITypeSymbol; - getValue() { - // FIXME: We don't have the data to calculate a class's size. - // const symbol = this.argumentRef?.getReference(); - return undefined; - } + getValue() { + // FIXME: We don't have the data to calculate a class's size. + // const symbol = this.argumentRef?.getReference(); + return undefined; + } - getType() { - return this.argumentRef; - } + getType() { + return this.argumentRef; + } - getContainedSymbolAtPos(position: Position) { - return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; - } + getContainedSymbolAtPos(position: Position) { + return this.argumentRef?.getSymbolAtPos(position) && this.argumentRef; + } - index(document: UCDocument, context?: UCStructSymbol) { - super.index(document, context); - this.argumentRef?.index(document, context!); - } + index(document: UCDocument, context?: UCStructSymbol) { + super.index(document, context); + this.argumentRef?.index(document, context!); + } } export class UCMetaClassExpression extends UCExpression { - public expression?: IExpression; - public classRef?: UCObjectTypeSymbol; - - getMemberSymbol() { - return this.classRef?.getRef(); - } - - getType() { - return this.classRef; - } - - getContainedSymbolAtPos(position: Position) { - const subSymbol = this.classRef?.getSymbolAtPos(position) as UCObjectTypeSymbol; - return this.expression?.getSymbolAtPos(position) || subSymbol?.getRef() && this.classRef; - } - - index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { - this.expression?.index(document, context, info); - this.classRef?.index(document, context!); - } + public expression?: IExpression; + public classRef?: UCObjectTypeSymbol; + + getMemberSymbol() { + return this.classRef?.getRef(); + } + + getType() { + return this.classRef; + } + + getContainedSymbolAtPos(position: Position) { + const subSymbol = this.classRef?.getSymbolAtPos(position) as UCObjectTypeSymbol; + return this.expression?.getSymbolAtPos(position) || subSymbol?.getRef() && this.classRef; + } + + index(document: UCDocument, context?: UCStructSymbol, info?: IContextInfo) { + this.expression?.index(document, context, info); + this.classRef?.index(document, context!); + } } \ No newline at end of file diff --git a/server/src/UC/helpers.ts b/server/src/UC/helpers.ts index 899c8326..2ed5f336 100644 --- a/server/src/UC/helpers.ts +++ b/server/src/UC/helpers.ts @@ -1,21 +1,14 @@ -import * as c3 from 'antlr4-c3'; import { ParserRuleContext, Token, TokenStream } from 'antlr4ts'; -import { TerminalNode } from 'antlr4ts/tree/TerminalNode'; import { - CodeAction, CompletionItem, CompletionItemKind, DocumentHighlight, DocumentHighlightKind, Hover, - Location, Position, Range, SymbolInformation + CodeAction, DocumentHighlight, DocumentHighlightKind, Hover, Location, Position, Range, + SymbolInformation } from 'vscode-languageserver'; import { UCLexer } from './antlr/generated/UCLexer'; -import { UCParser } from './antlr/generated/UCParser'; -import { DocumentParseData, UCDocument } from './document'; +import { UCDocument } from './document'; import { DocumentCodeActionsBuilder } from './documentCodeActionsBuilder'; -import { config, getDocumentByURI, getIndexedReferences, UCGeneration } from './indexer'; -import { TokenExt } from './Parser/CommonTokenStreamExt'; -import { - Identifier, ISymbol, IWithReference, ObjectsTable, tryFindClassSymbol, UCClassSymbol, - UCFieldSymbol, UCMethodSymbol, UCStructSymbol, UCSymbol, UCTypeFlags -} from './Symbols'; +import { getDocumentByURI, getIndexedReferences } from './indexer'; +import { ISymbol, IWithReference, UCFieldSymbol, UCStructSymbol, UCSymbol } from './Symbols'; export const VALID_ID_REGEXP = RegExp(/^([a-zA-Z_][a-zA-Z_0-9]*)$/); @@ -85,357 +78,196 @@ export function intersectsWithRange(position: Position, range: Range): boolean { && position.character <= range.end.character; } -function getDocumentSymbol(document: UCDocument, position: Position): ISymbol | undefined { - const symbols = document.getSymbols(); - for (let symbol of symbols) { - const child = symbol.getSymbolAtPos(position); - if (child) { - return child; - } - } - return undefined; +export function getDocumentSymbol(document: UCDocument, position: Position): ISymbol | undefined { + const symbols = document.getSymbols(); + for (let symbol of symbols) { + const child = symbol.getSymbolAtPos(position); + if (child) { + return child; + } + } + return undefined; } /** * Returns the deepest UCStructSymbol that is intersecting with @param position **/ -function getDocumentContext(document: UCDocument, position: Position): ISymbol | undefined { - const symbols = document.getSymbols(); - for (let symbol of symbols) { - if (symbol instanceof UCFieldSymbol) { - const child = symbol.getCompletionContext(position); - if (child) { - return child; - } - } - } - return undefined; +export function getDocumentContext(document: UCDocument, position: Position): ISymbol | undefined { + const symbols = document.getSymbols(); + for (let symbol of symbols) { + if (symbol instanceof UCFieldSymbol) { + const child = symbol.getCompletionContext(position); + if (child) { + return child; + } + } + } + return undefined; } export async function getSymbolTooltip(uri: string, position: Position): Promise { - const document = getDocumentByURI(uri); - const ref = document && getDocumentSymbol(document, position); - if (ref && ref instanceof UCSymbol) { - const contents = [{ language: 'unrealscript', value: ref.getTooltip() }]; - - const documentation = ref.getDocumentation(); - if (documentation) { - contents.push({ language: 'unrealscript', value: documentation }); - } - - return { - contents, - range: ref.id.range - }; - } + const document = getDocumentByURI(uri); + const ref = document && getDocumentSymbol(document, position); + if (ref && ref instanceof UCSymbol) { + const contents = [{ language: 'unrealscript', value: ref.getTooltip() }]; + + const documentation = ref.getDocumentation(); + if (documentation) { + contents.push({ language: 'unrealscript', value: documentation }); + } + + return { + contents, + range: ref.id.range + }; + } } export async function getSymbolDefinition(uri: string, position: Position): Promise { - const document = getDocumentByURI(uri); - const ref = document && getDocumentSymbol(document, position) as unknown as IWithReference; - if (!ref) { - return undefined; - } - - const symbol = ref.getRef?.(); - if (symbol instanceof UCSymbol) { - return symbol; - } - return ref; + const document = getDocumentByURI(uri); + const ref = document && getDocumentSymbol(document, position) as unknown as IWithReference; + if (!ref) { + return undefined; + } + + const symbol = ref.getRef?.(); + if (symbol instanceof UCSymbol) { + return symbol; + } + return ref; } export async function getSymbol(uri: string, position: Position): Promise { - const document = getDocumentByURI(uri); - return document && getDocumentSymbol(document, position); + const document = getDocumentByURI(uri); + return document && getDocumentSymbol(document, position); } export async function getSymbols(uri: string): Promise { - const document = getDocumentByURI(uri); - if (!document) { - return undefined; - } - - const contextSymbols: SymbolInformation[] = document.getSymbols().map(s => s.toSymbolInfo()); - const buildSymbolsList = (container: UCStructSymbol) => { - for (let child = container.children; child; child = child.next) { - contextSymbols.push(child.toSymbolInfo()); - if (child instanceof UCStructSymbol) { - buildSymbolsList(child as UCStructSymbol); - } - } - }; - - for (let symbol of contextSymbols) { - if (symbol instanceof UCStructSymbol) { - buildSymbolsList(symbol); - } - } - return contextSymbols; + const document = getDocumentByURI(uri); + if (!document) { + return undefined; + } + + const contextSymbols: SymbolInformation[] = document.getSymbols().map(s => s.toSymbolInfo()); + const buildSymbolsList = (container: UCStructSymbol) => { + for (let child = container.children; child; child = child.next) { + contextSymbols.push(child.toSymbolInfo()); + if (child instanceof UCStructSymbol) { + buildSymbolsList(child as UCStructSymbol); + } + } + }; + + for (let symbol of contextSymbols) { + if (symbol instanceof UCStructSymbol) { + buildSymbolsList(symbol); + } + } + return contextSymbols; } export async function getSymbolReferences(uri: string, position: Position): Promise { - const symbol = await getSymbolDefinition(uri, position); - if (!symbol) { - return undefined; - } - - const references = getIndexedReferences(symbol.getHash()); - if (!references) { - return undefined; - } - return Array.from(references.values()) - .map(ref => ref.location); + const symbol = await getSymbolDefinition(uri, position); + if (!symbol) { + return undefined; + } + + const references = getIndexedReferences(symbol.getHash()); + if (!references) { + return undefined; + } + return Array.from(references.values()) + .map(ref => ref.location); } export async function getSymbolHighlights(uri: string, position: Position): Promise { - const symbol = await getSymbolDefinition(uri, position); - if (!symbol) { - return undefined; - } - - const references = getIndexedReferences(symbol.getHash()); - if (!references) { - return undefined; - } - - return Array - .from(references.values()) - .filter(loc => loc.location.uri === uri) - .map(ref => DocumentHighlight.create( - ref.location.range, - ref.inAssignment - ? DocumentHighlightKind.Write - : DocumentHighlightKind.Read - )); + const symbol = await getSymbolDefinition(uri, position); + if (!symbol) { + return undefined; + } + + const references = getIndexedReferences(symbol.getHash()); + if (!references) { + return undefined; + } + + return Array + .from(references.values()) + .filter(loc => loc.location.uri === uri) + .map(ref => DocumentHighlight.create( + ref.location.range, + ref.inAssignment + ? DocumentHighlightKind.Write + : DocumentHighlightKind.Read + )); } -export async function getCompletableSymbolItems(uri: string, data: DocumentParseData | undefined, position: Position): Promise { - const document = getDocumentByURI(uri); - if (!document || !data) { - return undefined; - } - - const getIntersectingContext = (context?: ParserRuleContext): ParserRuleContext | undefined => { - if (!context) { - return undefined; - } - - if (intersectsWith(rangeFromBounds(context.start, context.stop), position)) { - if (context.children) for (let child of context.children) { - if (child instanceof ParserRuleContext) { - const ctx = getIntersectingContext(child); - if (ctx) { - return ctx; - } - } - } - return context; - } - return undefined; - }; - - const getCaretTokenIndex = (context?: ParserRuleContext): number => { - if (!context) { - return 0; - } - if (context.children) for (let child of context.children) { - if (child instanceof TerminalNode) { - if (intersectsWithRange(position, rangeFromBound(child.symbol))) { - return child.symbol.tokenIndex; - } +export function getIntersectingContext(context: ParserRuleContext, position: Position): ParserRuleContext | undefined { + if (!intersectsWith(rangeFromCtx(context), position)) { + return undefined; + } + if (context.children) for (let child of context.children) { + if (child instanceof ParserRuleContext) { + const ctx = getIntersectingContext(child, position); + if (ctx) { + return ctx; } } - return 0; - }; - - const getCaretTokenIndexFromStream = (stream: TokenStream): number => { - let i = 0; - let token: Token | undefined = undefined; - while (token = stream.get(i)) { - if (position.line === token.line - 1 - && position.character >= token.charPositionInLine - && position.character < token.charPositionInLine + token.text!.length) { - return token.tokenIndex; - } - ++ i; - } - return 0; - }; - - const core = new c3.CodeCompletionCore(data.parser); - core.ignoredTokens = new Set([ - UCLexer.ID, - UCLexer.PLUS, UCLexer.MINUS, - UCLexer.STAR, UCLexer.BITWISE_OR, - UCLexer.ASSIGNMENT, - UCLexer.OPEN_PARENS, UCLexer.CLOSE_PARENS, - UCLexer.OPEN_BRACKET, UCLexer.CLOSE_BRACKET, - UCLexer.OPEN_BRACE, UCLexer.CLOSE_BRACE, - UCLexer.LSHIFT, UCLexer.RSHIFT, - UCLexer.SEMICOLON, UCLexer.COLON, UCLexer.COMMA - ]); - - if (config.generation === UCGeneration.UC1) { - core.ignoredTokens.add(UCLexer.KW_EXTENDS); - core.ignoredTokens.add(UCLexer.KW_NATIVE); - } else { - core.ignoredTokens.add(UCLexer.KW_EXPANDS); - core.ignoredTokens.add(UCLexer.KW_INTRINSIC); - } - - if (config.generation === UCGeneration.UC3) { - core.ignoredTokens.add(UCLexer.KW_CPPSTRUCT); - } else { - core.ignoredTokens.add(UCLexer.KW_STRUCTDEFAULTPROPERTIES); - core.ignoredTokens.add(UCLexer.KW_STRUCTCPPTEXT); - } - - core.preferredRules = new Set([ - UCParser.RULE_varDecl, UCParser.RULE_paramDecl, UCParser.RULE_localDecl, - UCParser.RULE_typeDecl, UCParser.RULE_primitiveType, UCParser.RULE_qualifiedIdentifier, UCParser.RULE_identifier, - UCParser.RULE_arrayType, UCParser.RULE_classType, UCParser.RULE_delegateType, UCParser.RULE_mapType, - UCParser.RULE_functionName, UCParser.RULE_functionBody, - UCParser.RULE_codeBlockOptional, UCParser.RULE_statement - ]); - - const context = getIntersectingContext(data.context); - const caret = getCaretTokenIndexFromStream(data.parser.inputStream); - const candidates = core.collectCandidates(caret, context); - - const items: CompletionItem[] = []; - for (let [ type ] of candidates.tokens) { - const displayName = data.parser.vocabulary.getDisplayName(type); - const tokenName = displayName.substring(1, displayName.length - 2); - if (!VALID_ID_REGEXP.test(tokenName)) { - continue; - } - items.push({ - label: tokenName, - kind: CompletionItemKind.Keyword - }); - } - - const contextSymbol = getDocumentContext(document, position); - for (let [ rule, rules ] of candidates.rules) { - switch (rule) { - // TODO: suggest top-level contained symbols (e.g. Actor.ENetMode) - case UCParser.RULE_qualifiedIdentifier: { - if (context?.parent) { - if (context.parent.ruleIndex !== UCParser.RULE_typeDecl) { - break; - } - } - } - - case UCParser.RULE_identifier: { - if (context?.parent?.ruleIndex === UCParser.RULE_functionName) { - if (contextSymbol instanceof UCMethodSymbol) { - const symbolItems = contextSymbol - .getCompletionSymbols(document, '', UCTypeFlags.Function) - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...symbolItems); - } - break; - } - - if (context?.parent?.ruleIndex !== UCParser.RULE_qualifiedIdentifier) { - // Do not suggest any items (except for generic tokens) - return []; - } - } - - case UCParser.RULE_typeDecl: case UCParser.RULE_primitiveType: { - const typeItems = Array - .from(ObjectsTable.getAll()) - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...typeItems); - break; - } - - case UCParser.RULE_classType: { - const typeItems = Array - .from(ObjectsTable.getAll()) - .filter(symbol => symbol.getKind() === UCTypeFlags.Class) - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...typeItems); - } - - case UCParser.RULE_delegateType: { - const typeItems = Array - .from(ObjectsTable.getAll()) - .filter(symbol => symbol.getKind() === UCTypeFlags.Class) - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...typeItems); - - // if (contextSymbol instanceof UCStructSymbol) { - // const symbolItems = contextSymbol - // .getCompletionSymbols(document, '', UCTypeFlags.Function) - // .map(symbol => symbolToCompletionItem(symbol)); - - // items.push(...symbolItems); - // } - } - - case UCParser.RULE_functionName: { - if (contextSymbol instanceof UCMethodSymbol) { - const symbolItems = contextSymbol - .getCompletionSymbols(document, '', UCTypeFlags.Function) - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...symbolItems); - } - break; - } - - case UCParser.RULE_codeBlockOptional: - case UCParser.RULE_statement: { - if (contextSymbol instanceof UCStructSymbol) { - const symbolItems = contextSymbol - .getCompletionSymbols(document, '') - .map(symbol => symbolToCompletionItem(symbol)); - - items.push(...symbolItems); - } - break; - } - } - } - return items; + } + return context; +} + +export function getCaretTokenIndexFromStream(stream: TokenStream, position: Position): number { + let i = 0; + let token: Token | undefined = undefined; + while (i < stream.size && (token = stream.get(i))) { + if (position.line === token.line - 1 + && position.character >= token.charPositionInLine + && position.character < token.charPositionInLine + (token.stopIndex - token.startIndex + 1)) { + return token.tokenIndex; + } + ++i; + } + return 0; } -function symbolToCompletionItem(symbol: ISymbol): CompletionItem { - if (symbol instanceof UCSymbol) { - return { - label: symbol.getName().toString(), - kind: symbol.getCompletionItemKind(), - detail: symbol.getTooltip(), - data: symbol.id - }; - } - - return { - label: symbol.getName().toString() - }; +export function backtrackFirstToken(stream: TokenStream, startTokenIndex: number): Token | undefined { + if (startTokenIndex >= stream.size) { + return undefined; + } + + let i = startTokenIndex; + while (--i) { + const token = stream.get(i); + if (token.channel !== UCLexer.DEFAULT_TOKEN_CHANNEL) { + continue; + } + return token; + } + return undefined; } -export async function getFullCompletionItem(item: CompletionItem): Promise { - // Assuming data has a reference to an @Identifier object. - const id = item.data as Identifier | undefined; - if (typeof id === 'object') { - const symbol = tryFindClassSymbol(id.name); - if (symbol) { - item.documentation = symbol.getDocumentation(); - } - } - return item; +export function backtrackFirstTokenOfType(stream: TokenStream, type: number, startTokenIndex: number): Token | undefined { + if (startTokenIndex >= stream.size) { + return undefined; + } + let i = startTokenIndex; + while (--i) { + const token = stream.get(i); + if (token.channel !== UCLexer.DEFAULT_TOKEN_CHANNEL) { + continue; + } + + if (token.type !== type) { + return undefined; + } + return token; + } + return undefined; } export async function getCodeActions(uri: string, range: Range): Promise { - const document = getDocumentByURI(uri); + const document = getDocumentByURI(uri); if (!document) { return undefined; } diff --git a/server/src/completion.ts b/server/src/completion.ts new file mode 100644 index 00000000..e70c715d --- /dev/null +++ b/server/src/completion.ts @@ -0,0 +1,412 @@ +import * as c3 from 'antlr4-c3'; +import { + CompletionItem, CompletionItemKind, SignatureHelp, SymbolKind +} from 'vscode-languageserver'; +import { Position } from 'vscode-languageserver-textdocument'; + +import { UCLexer } from './UC/antlr/generated/UCLexer'; +import { UCParser } from './UC/antlr/generated/UCParser'; +import { DocumentParseData } from './UC/document'; +import { UCCallExpression } from './UC/expressions'; +import { + backtrackFirstToken, backtrackFirstTokenOfType, getCaretTokenIndexFromStream, + getDocumentContext, getIntersectingContext, rangeFromBound +} from './UC/helpers'; +import { getDocumentByURI } from './UC/indexer'; +import { + Identifier, ISymbol, ObjectsTable, tryFindClassSymbol, UCClassSymbol, UCFieldSymbol, + UCMethodSymbol, UCObjectTypeSymbol, UCStructSymbol, UCSymbol, UCTypeFlags +} from './UC/Symbols'; + +const PreferredRulesSet = new Set([ + UCParser.RULE_typeDecl, + UCParser.RULE_qualifiedIdentifier, UCParser.RULE_identifier, + UCParser.RULE_functionName, UCParser.RULE_functionBody, + UCParser.RULE_codeBlockOptional, + UCParser.RULE_defaultPropertiesBlock +]); + +export const DefaultIgnoredTokensSet = new Set([ + UCLexer.WS, + UCLexer.ID, + UCLexer.INTERR, + UCLexer.SHARP, + UCLexer.PLUS, + UCLexer.MINUS, + UCLexer.DOT, + UCLexer.AT, + UCLexer.DOLLAR, + UCLexer.BANG, + UCLexer.AMP, + UCLexer.BITWISE_OR, + UCLexer.STAR, + UCLexer.CARET, + UCLexer.DIV, + UCLexer.MODULUS, + UCLexer.TILDE, + UCLexer.LT, + UCLexer.GT, + UCLexer.OR, + UCLexer.AND, + UCLexer.EQ, + UCLexer.NEQ, + UCLexer.GEQ, + UCLexer.LEQ, + UCLexer.IEQ, + UCLexer.MEQ, + UCLexer.INCR, + UCLexer.DECR, + UCLexer.EXP, + UCLexer.RSHIFT, + UCLexer.LSHIFT, + UCLexer.SHIFT, + UCLexer.ASSIGNMENT, + UCLexer.ASSIGNMENT_INCR, + UCLexer.ASSIGNMENT_DECR, + UCLexer.ASSIGNMENT_AT, + UCLexer.ASSIGNMENT_DOLLAR, + UCLexer.ASSIGNMENT_AND, + UCLexer.ASSIGNMENT_OR, + UCLexer.ASSIGNMENT_STAR, + UCLexer.ASSIGNMENT_CARET, + UCLexer.ASSIGNMENT_DIV, + UCLexer.OPEN_PARENS, UCLexer.CLOSE_PARENS, + UCLexer.OPEN_BRACKET, UCLexer.CLOSE_BRACKET, + UCLexer.OPEN_BRACE, UCLexer.CLOSE_BRACE, + UCLexer.SEMICOLON, UCLexer.COLON, + UCLexer.COMMA, UCLexer.EOF +]); + +let currentIgnoredTokensSet = DefaultIgnoredTokensSet; + +export function setIgnoredTokensSet(newSet: Set) { + currentIgnoredTokensSet = newSet; +} + +const packageOnlyFlags: UCTypeFlags = UCTypeFlags.Package & ~UCTypeFlags.Object; + +export async function getSignatureHelp(uri: string, data: DocumentParseData | undefined, position: Position): Promise { + // const document = getDocumentByURI(uri); + // if (!document || !data) { + // return undefined; + // } + + // const cc = new c3.CodeCompletionCore(data.parser); + // // cc.showDebugOutput = true; + // cc.ignoredTokens = currentIgnoredTokensSet; + // cc.preferredRules = PreferredRulesSet; + + // const context = data.context && getIntersectingContext(data.context, position); + // const caretTokenIndex = getCaretTokenIndexFromStream(data.parser.inputStream, position); + // const candidates = cc.collectCandidates(caretTokenIndex, context); + + // for (let [type, candiateRule] of candidates.rules) { + // // console.log('signatureHelp::type', data.parser.ruleNames[type]); + + // const stateType = candiateRule.ruleList.length + // ? candiateRule.ruleList[candiateRule.ruleList.length - 1] + // : undefined; + // // console.log('signatureHelp::stateType', candiateRule.ruleList.map(t => t && data.parser.ruleNames[t]).join('.')); + + // // Speculative approach... + // switch (type) { + // case UCParser.RULE_identifier: + // case UCParser.RULE_qualifiedIdentifier: { + // switch (stateType) { + // case UCParser.RULE_qualifiedIdentifierArguments: { + // return { + // signatures: [{ + // label: 'implements (interface, ...interface)', + // parameters: [{ + // label: 'interface', + // }], + // activeParameter: 0 + // }], + // activeSignature: 0, + // activeParameter: null, + // }; + // break; + // } + // } + // } + // } + // } + return undefined; +} + +export async function getCompletableSymbolItems(uri: string, data: DocumentParseData | undefined, position: Position): Promise { + const document = getDocumentByURI(uri); + if (!document || !data) { + return undefined; + } + + const cc = new c3.CodeCompletionCore(data.parser); + // cc.showDebugOutput = true; + cc.ignoredTokens = currentIgnoredTokensSet; + cc.preferredRules = PreferredRulesSet; + + if (typeof data.context === 'undefined') { + return; + } + + const context = getIntersectingContext(data.context, position); + if (typeof context === 'undefined') { + return; + } + + const caretTokenIndex = getCaretTokenIndexFromStream(data.parser.inputStream, position); + const candidates = cc.collectCandidates(caretTokenIndex, context); + + const items: CompletionItem[] = []; + const symbols: ISymbol[] = []; + + const contextSymbol = getDocumentContext(document, position); + // console.log('completionItem::contextSymbol', contextSymbol?.getPath()); + + let globalTypes: UCTypeFlags = UCTypeFlags.Error; + for (let [type, candiateRule] of candidates.rules) { + // console.log('completionItem::type', data.parser.ruleNames[type]); + + const stateType = candiateRule.ruleList.length + ? candiateRule.ruleList[candiateRule.ruleList.length - 1] + : undefined; + + // console.log('completionItem::stateType', candiateRule.ruleList.map(t => t && data.parser.ruleNames[t]).join('.')); + + // Speculative approach... + switch (type) { + case UCParser.RULE_identifier: { + let contexToken = backtrackFirstTokenOfType(data.parser.inputStream, UCParser.DOT, caretTokenIndex); + if (contexToken) { + contexToken = backtrackFirstToken(data.parser.inputStream, contexToken.tokenIndex); + } + if (contexToken && contextSymbol instanceof UCStructSymbol) { + // FIXME: Hacky and assuming for this to only return a typeSymbol in the particular circumstances of this context. + UCCallExpression.hack_getTypeIfNoSymbol = true; + const resolvedTypeSymbol = contextSymbol.block?.getContainedSymbolAtPos(rangeFromBound(contexToken).start); + UCCallExpression.hack_getTypeIfNoSymbol = false; + + // Only object types are allowed + if (resolvedTypeSymbol instanceof UCObjectTypeSymbol) { + const resolvedReference = resolvedTypeSymbol.getRef(); + if (resolvedReference instanceof UCFieldSymbol) { + const symbolItems = resolvedReference + .getCompletionSymbols( + document, '.', + ((UCTypeFlags.Property | UCTypeFlags.Function) & ~UCTypeFlags.Object) + ); + + symbols.push(...symbolItems); + + // A little hack to remove false positive keywords when possible + if (!(resolvedReference instanceof UCClassSymbol)) { + candidates.tokens.delete(UCParser.KW_DEFAULT); + candidates.tokens.delete(UCParser.KW_STATIC); + candidates.tokens.delete(UCParser.KW_CONST); + } + } + break; + } + } + + if (contextSymbol instanceof UCStructSymbol && contextSymbol.block) { + const symbolItems = contextSymbol + .getCompletionSymbols( + document, '', + (( + UCTypeFlags.Property + | UCTypeFlags.Function + | UCTypeFlags.Enum + | UCTypeFlags.Class + | UCTypeFlags.Const + ) & ~UCTypeFlags.Object) + ); + symbols.push(...symbolItems); + } + + switch (stateType) { + case undefined: + case UCParser.RULE_classDecl: { + items.push({ + label: document.name.toString(), + kind: CompletionItemKind.Class + }); + break; + } + + case UCParser.RULE_identifierArguments: { + const typeItems = Array + .from(ObjectsTable.getAll()) + .filter(symbol => (symbol.getTypeFlags() & (UCTypeFlags.Class | UCTypeFlags.IsInterface)) === UCTypeFlags.Class); + + symbols.push(...typeItems); + break; + } + + case UCParser.RULE_typeDecl: { + globalTypes |= ((UCTypeFlags.Enum | UCTypeFlags.Struct | UCTypeFlags.Class) & ~UCTypeFlags.Object); + break; + } + + case UCParser.RULE_classType: { + globalTypes |= ((UCTypeFlags.Class) & ~UCTypeFlags.Object); + break; + } + + case UCParser.RULE_delegateType: { + globalTypes |= (UCTypeFlags.Class & ~UCTypeFlags.Object); + if (contextSymbol instanceof UCStructSymbol) { + const symbolItems = contextSymbol + .getCompletionSymbols( + document, '', + UCTypeFlags.Delegate + ); + symbols.push(...symbolItems); + } + break; + } + } + break; + } + + // TODO: suggest top-level contained symbols (e.g. Actor.ENetMode) + case UCParser.RULE_qualifiedIdentifier: { + switch (stateType) { + case UCParser.RULE_qualifiedIdentifierArguments: { + globalTypes |= UCTypeFlags.IsInterface | (packageOnlyFlags); + break; + } + + case UCParser.RULE_extendsClause: { + switch (contextSymbol?.getKind()) { + case SymbolKind.Class: { + globalTypes |= (UCTypeFlags.Class & ~UCTypeFlags.Object) | (packageOnlyFlags); + break; + } + + case SymbolKind.Struct: { + globalTypes |= ((UCTypeFlags.Struct | UCTypeFlags.Class) & ~UCTypeFlags.Object) | (packageOnlyFlags); + break; + } + } + break; + } + + case UCParser.RULE_delegateType: { + globalTypes |= (UCTypeFlags.Class & ~UCTypeFlags.Object); + if (contextSymbol instanceof UCStructSymbol) { + const symbolItems = contextSymbol + .getCompletionSymbols( + document, '', + UCTypeFlags.Delegate + ); + symbols.push(...symbolItems); + } + break; + } + + case UCParser.RULE_typeDecl: { + globalTypes |= ((UCTypeFlags.Enum | UCTypeFlags.Struct) & ~UCTypeFlags.Object); + break; + } + } + break; + } + + case UCParser.RULE_typeDecl: { + if (stateType === UCParser.RULE_varType) { + globalTypes |= ((UCTypeFlags.Enum | UCTypeFlags.Struct | UCTypeFlags.Class) & ~UCTypeFlags.Object); + } + break; + } + + case UCParser.RULE_functionName: { + if (contextSymbol instanceof UCMethodSymbol) { + const symbolItems = contextSymbol + .getCompletionSymbols(document, '', (UCTypeFlags.Function & ~UCTypeFlags.Object)); + symbols.push(...symbolItems); + } + break; + } + + case UCParser.RULE_functionBody: + case UCParser.RULE_codeBlockOptional: { + if (contextSymbol instanceof UCStructSymbol) { + globalTypes |= ((UCTypeFlags.Enum | UCTypeFlags.Class) & ~UCTypeFlags.Object); + const symbolItems = contextSymbol + .getCompletionSymbols(document, '', + (( + UCTypeFlags.Property + | UCTypeFlags.Function + | UCTypeFlags.Const + ) & ~UCTypeFlags.Object)); + symbols.push(...symbolItems); + } + break; + } + + case UCParser.RULE_defaultPropertiesBlock: { + if (contextSymbol instanceof UCStructSymbol) { + const symbolItems = contextSymbol + .getCompletionSymbols(document, '', + (( + UCTypeFlags.Property + | UCTypeFlags.Delegate + ) & ~UCTypeFlags.Object)); + symbols.push(...symbolItems); + } + } + } + } + + if (globalTypes !== UCTypeFlags.Error) { + const typeItems = Array + .from(ObjectsTable.getTypes(globalTypes)) + .map(symbol => symbolToCompletionItem(symbol)); + + items.push(...typeItems); + } + + for (let [type, tokens] of candidates.tokens) { + const name = data.parser.vocabulary.getLiteralName(type); + if (typeof name === 'undefined') { + continue; + } + + // Assume that the name is single quoted. + const tokenName = name.substring(1, name.length - 1); + items.push({ + label: tokenName, + kind: CompletionItemKind.Keyword + }); + } + + return items.concat(symbols.map(symbol => symbolToCompletionItem(symbol))); +} + +function symbolToCompletionItem(symbol: ISymbol): CompletionItem { + if (symbol instanceof UCSymbol) { + return { + label: symbol.getName().toString(), + kind: symbol.getCompletionItemKind(), + detail: symbol.getTooltip(), + data: symbol.id + }; + } + + return { + label: symbol.getName().toString() + }; +} + +export async function getFullCompletionItem(item: CompletionItem): Promise { + // Assuming data has a reference to an @Identifier object. + const id = item.data as Identifier | undefined; + if (typeof id === 'object') { + const symbol = tryFindClassSymbol(id.name); + if (symbol) { + item.documentation = symbol.getDocumentation(); + } + } + return item; +} \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index d608e485..78d048c4 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,26 +1,32 @@ import * as glob from 'glob'; import * as path from 'path'; -import { BehaviorSubject, interval, Subject, Subscription } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, from, interval, Subject, Subscription } from 'rxjs'; import { debounce, delay, filter, switchMapTo, tap } from 'rxjs/operators'; import * as url from 'url'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { - CodeActionKind, createConnection, ErrorCodes, FileOperationRegistrationOptions, - InitializeParams, Location, Position, ProposedFeatures, Range, ResponseError, TextDocuments, - TextDocumentSyncKind, TextEdit, WorkspaceChange, WorkspaceEdit, WorkspaceFolder + CodeActionKind, CompletionTriggerKind, createConnection, ErrorCodes, + FileOperationRegistrationOptions, InitializeParams, Location, Position, ProposedFeatures, Range, + ResponseError, TextDocuments, TextDocumentSyncKind, TextEdit, WorkspaceChange, WorkspaceEdit, + WorkspaceFolder } from 'vscode-languageserver/node'; import { URI } from 'vscode-uri'; +import { + DefaultIgnoredTokensSet, getCompletableSymbolItems, getFullCompletionItem, getSignatureHelp, + setIgnoredTokensSet +} from './completion'; import { EAnalyzeOption, IntrinsicSymbolItemMap, UCLanguageServerSettings } from './settings'; +import { UCLexer } from './UC/antlr/generated/UCLexer'; import { DocumentParseData, UCDocument } from './UC/document'; import { - getCodeActions, getCompletableSymbolItems, getFullCompletionItem, getSymbolDefinition, - getSymbolHighlights, getSymbolReferences, getSymbols, getSymbolTooltip, VALID_ID_REGEXP + getCodeActions, getSymbolDefinition, getSymbolHighlights, getSymbolReferences, getSymbols, + getSymbolTooltip, VALID_ID_REGEXP } from './UC/helpers'; import { applyMacroSymbols, clearMacroSymbols, config, createDocumentByPath, createPackage, documentsMap, getDocumentById, getDocumentByURI, getIndexedReferences, getPackageByDir, lastIndexedDocuments$, - queuIndexDocument, removeDocumentByPath + queuIndexDocument, removeDocumentByPath, UCGeneration } from './UC/indexer'; import { toName } from './UC/names'; import { @@ -121,9 +127,9 @@ connection.onInitialize((params: InitializeParams) => { triggerCharacters: ['.', '"', '\'', '`', '<'], resolveProvider: true }, - // signatureHelpProvider: { - // triggerCharacters: ['(', ',', '<'] - // }, + signatureHelpProvider: { + triggerCharacters: ['(', ',', '<'] + }, definitionProvider: true, documentSymbolProvider: true, documentHighlightProvider: true, @@ -346,6 +352,7 @@ function initializeConfiguration() { function applyConfiguration(settings: UCLanguageServerSettings) { applyMacroSymbols(settings.macroSymbols); installIntrinsicSymbols(settings.intrinsicSymbols); + setupIgnoredTokens(settings.generation); } function clearIntrinsicSymbols() { @@ -375,6 +382,38 @@ function installIntrinsicSymbols(intrinsicSymbols: IntrinsicSymbolItemMap) { } } +function setupIgnoredTokens(generation: UCGeneration) { + const ignoredTokensSet = new Set(DefaultIgnoredTokensSet); + if (generation === UCGeneration.UC1) { + ignoredTokensSet.add(UCLexer.KW_EXTENDS); + ignoredTokensSet.add(UCLexer.KW_NATIVE); + + // TODO: Context aware ignored tokens. + // ignoredTokensSet.add(UCLexer.KW_TRANSIENT); + ignoredTokensSet.add(UCLexer.KW_LONG); + } else { + ignoredTokensSet.add(UCLexer.KW_EXPANDS); + ignoredTokensSet.add(UCLexer.KW_INTRINSIC); + } + + if (generation === UCGeneration.UC3) { + ignoredTokensSet.add(UCLexer.KW_CPPSTRUCT); + } else { + // Some custom UE2 builds do have implements + ignoredTokensSet.add(UCLexer.KW_IMPLEMENTS); + + ignoredTokensSet.add(UCLexer.KW_STRUCTDEFAULTPROPERTIES); + ignoredTokensSet.add(UCLexer.KW_STRUCTCPPTEXT); + + ignoredTokensSet.add(UCLexer.KW_ATOMIC); + ignoredTokensSet.add(UCLexer.KW_ATOMICWHENCOOKED); + ignoredTokensSet.add(UCLexer.KW_STRICTCONFIG); + ignoredTokensSet.add(UCLexer.KW_IMMUTABLE); + ignoredTokensSet.add(UCLexer.KW_IMMUTABLEWHENCOOKED); + } + setIgnoredTokensSet(ignoredTokensSet); +} + connection.onDocumentSymbol((e) => getSymbols(e.textDocument.uri)); connection.onHover((e) => getSymbolTooltip(e.textDocument.uri, e.position)); @@ -393,11 +432,16 @@ connection.onDefinition(async (e) => { connection.onReferences((e) => getSymbolReferences(e.textDocument.uri, e.position)); connection.onDocumentHighlight((e) => getSymbolHighlights(e.textDocument.uri, e.position)); - connection.onCompletion((e) => { + if (e.context?.triggerKind !== CompletionTriggerKind.Invoked) { + return firstValueFrom(lastIndexedDocuments$).then(documents => { + return getCompletableSymbolItems(e.textDocument.uri, activeDocumentParseData, e.position); + }); + } return getCompletableSymbolItems(e.textDocument.uri, activeDocumentParseData, e.position); }); connection.onCompletionResolve(getFullCompletionItem); +connection.onSignatureHelp((e) => getSignatureHelp(e.textDocument.uri, activeDocumentParseData, e.position)); connection.onPrepareRename(async (e) => { const symbol = await getSymbolDefinition(e.textDocument.uri, e.position);