From 2172fc50617566b830fbc62b0e63c09f67bf343e Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Thu, 28 Nov 2024 21:30:32 +0800 Subject: [PATCH 01/20] member access funciton overloading --- packages/zenscript/src/module.ts | 3 + .../src/reference/dynamic-provider.ts | 9 +- .../src/reference/member-provider.ts | 2 +- .../zenscript/src/reference/scope-provider.ts | 16 +- .../zenscript/src/typing/overload-resolver.ts | 323 ++++++++++++++++++ .../zenscript/src/typing/type-computer.ts | 17 +- .../zenscript/src/typing/type-features.ts | 2 +- packages/zenscript/src/utils/ast.ts | 12 +- 8 files changed, 369 insertions(+), 15 deletions(-) create mode 100644 packages/zenscript/src/typing/overload-resolver.ts diff --git a/packages/zenscript/src/module.ts b/packages/zenscript/src/module.ts index f9ccc6a1..5610e228 100644 --- a/packages/zenscript/src/module.ts +++ b/packages/zenscript/src/module.ts @@ -14,6 +14,7 @@ import { ZenScriptMemberProvider } from './reference/member-provider' import { ZenScriptNameProvider } from './reference/name-provider' import { ZenScriptScopeComputation } from './reference/scope-computation' import { ZenScriptScopeProvider } from './reference/scope-provider' +import { ZenScriptOverloadResolver } from './typing/overload-resolver' import { ZenScriptTypeComputer } from './typing/type-computer' import { ZenScriptTypeFeatures } from './typing/type-features' import { registerValidationChecks, ZenScriptValidator } from './validation/validator' @@ -38,6 +39,7 @@ export interface ZenScriptAddedServices { typing: { TypeComputer: ZenScriptTypeComputer TypeFeatures: ZenScriptTypeFeatures + OverloadResolver: ZenScriptOverloadResolver } workspace: { PackageManager: ZenScriptPackageManager @@ -90,6 +92,7 @@ export const ZenScriptModule: Module new ZenScriptTypeComputer(services), TypeFeatures: services => new ZenScriptTypeFeatures(services), + OverloadResolver: services => new ZenScriptOverloadResolver(services), }, lsp: { CompletionProvider: services => new ZenScriptCompletionProvider(services), diff --git a/packages/zenscript/src/reference/dynamic-provider.ts b/packages/zenscript/src/reference/dynamic-provider.ts index df26b112..c6cf2c9d 100644 --- a/packages/zenscript/src/reference/dynamic-provider.ts +++ b/packages/zenscript/src/reference/dynamic-provider.ts @@ -1,6 +1,7 @@ import type { AstNode, AstNodeDescription, Stream } from 'langium' import type { ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' +import type { ZenScriptOverloadResolver } from '../typing/overload-resolver' import type { TypeComputer } from '../typing/type-computer' import type { DescriptionIndex } from '../workspace/description-index' import { AstUtils, EMPTY_STREAM, stream } from 'langium' @@ -19,10 +20,12 @@ type RuleMap = { [K in keyof SourceMap]?: (source: SourceMap[K]) => Stream { @@ -41,9 +44,9 @@ export class ZenScriptDynamicProvider implements DynamicProvider { // dynamic arguments if (isCallExpression(source.$container) && source.$containerProperty === 'arguments') { const index = source.$containerIndex! - const receiverType = this.typeComputer.inferType(source.$container.receiver) - if (isFunctionType(receiverType)) { - const paramType = receiverType.paramTypes[index] + const callType = this.overloadResolver.predictCallType(source.$container) + if (isFunctionType(callType)) { + const paramType = callType.paramTypes[index] if (isClassType(paramType)) { yield * streamDeclaredFunctions(paramType.declaration) .filter(isStatic) diff --git a/packages/zenscript/src/reference/member-provider.ts b/packages/zenscript/src/reference/member-provider.ts index 5da1e2fa..e2b16286 100644 --- a/packages/zenscript/src/reference/member-provider.ts +++ b/packages/zenscript/src/reference/member-provider.ts @@ -160,4 +160,4 @@ export class ZenScriptMemberProvider implements MemberProvider { .filter(it => !isStatic(it)) }, }) -} +}/* */ diff --git a/packages/zenscript/src/reference/scope-provider.ts b/packages/zenscript/src/reference/scope-provider.ts index 6e0e13ce..7c783a5a 100644 --- a/packages/zenscript/src/reference/scope-provider.ts +++ b/packages/zenscript/src/reference/scope-provider.ts @@ -1,13 +1,14 @@ import type { AstNode, AstNodeDescription, ReferenceInfo, Scope, ScopeOptions, Stream } from 'langium' import type { ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' +import type { ZenScriptOverloadResolver } from '../typing/overload-resolver' import type { ZenScriptDescriptionIndex } from '../workspace/description-index' import type { PackageManager } from '../workspace/package-manager' import type { DynamicProvider } from './dynamic-provider' import type { MemberProvider } from './member-provider' import { substringBeforeLast } from '@intellizen/shared' import { AstUtils, DefaultScopeProvider, EMPTY_SCOPE, stream, StreamScope } from 'langium' -import { ClassDeclaration, ImportDeclaration, isClassDeclaration, TypeParameter } from '../generated/ast' +import { ClassDeclaration, ImportDeclaration, isCallExpression, isClassDeclaration, TypeParameter } from '../generated/ast' import { getPathAsString } from '../utils/ast' import { defineRules } from '../utils/rule' import { generateStream } from '../utils/stream' @@ -20,6 +21,7 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { private readonly memberProvider: MemberProvider private readonly dynamicProvider: DynamicProvider private readonly descriptionIndex: ZenScriptDescriptionIndex + private readonly overloadResolver: ZenScriptOverloadResolver constructor(services: ZenScriptServices) { super(services) @@ -27,6 +29,7 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { this.memberProvider = services.references.MemberProvider this.dynamicProvider = services.references.DynamicProvider this.descriptionIndex = services.workspace.DescriptionIndex + this.overloadResolver = services.typing.OverloadResolver } override getScope(context: ReferenceInfo): Scope { @@ -116,7 +119,16 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { MemberAccess: (source) => { const outer = this.dynamicScope(source.container) const members = this.memberProvider.streamMembers(source.container.receiver) - return this.createScopeForNodes(members, outer) + + if (source.reference.$refText === '' || !isCallExpression(source.container.$container) || source.container.$containerProperty !== 'receiver') { + return this.createScopeForNodes(members, outer) + } + + const overload = this.overloadResolver.findOverloadMethod(members, source.container.$container, source.reference.$refText) + if (!overload) { + return outer + } + return this.createScopeForNodes(stream([overload]), outer) }, NamedTypeReference: (source) => { diff --git a/packages/zenscript/src/typing/overload-resolver.ts b/packages/zenscript/src/typing/overload-resolver.ts new file mode 100644 index 00000000..5eddea33 --- /dev/null +++ b/packages/zenscript/src/typing/overload-resolver.ts @@ -0,0 +1,323 @@ +import type { AstNode, AstNodeDescription, NameProvider, Stream } from 'langium' +import type { CallExpression, ClassDeclaration, ConstructorDeclaration, ExpandFunctionDeclaration, Expression, FunctionDeclaration } from '../generated/ast' +import type { ZenScriptServices } from '../module' +import type { ZenScriptMemberProvider } from '../reference/member-provider' +import type { ZenScriptNameProvider } from '../reference/name-provider' +import type { ClassType, IntersectionType, Type, UnionType } from '../typing/type-description' +import type { ZenScriptDescriptionIndex } from '../workspace/description-index' +import type { ZenScriptTypeComputer } from './type-computer' +import type { ZenScriptTypeFeatures } from './type-features' +import { AstUtils, stream } from 'langium' +import { isClassDeclaration, isConstructorDeclaration, isExpandFunctionDeclaration, isFunctionDeclaration, isFunctionExpression, isMemberAccess, isOperatorFunctionDeclaration, isReferenceExpression, isScript } from '../generated/ast' +import { FunctionType, isClassType, isCompoundType, isTypeVariable } from '../typing/type-description' + +export enum OverloadMatch { + FullMatch = 0, + OptionalMatch = 1, + ImplicitMatch = 2, + PossibleMatch = 3, + NotMatch = 4, +} + +export type CallableDeclaration = ConstructorDeclaration | FunctionDeclaration | ExpandFunctionDeclaration + +export function isOptionalArgs(method: CallableDeclaration, before: number): boolean { + let index = before + if (index < 0) { + index = method.parameters.length + index + } + if (isExpandFunctionDeclaration(method)) { + index++ + } + + return index < method.parameters.length && method.parameters[index].defaultValue !== undefined +} + +export function isVarargs(method: CallableDeclaration): boolean { + return method.parameters.length > 0 && method.parameters[method.parameters.length - 1].varargs +} + +export class ZenScriptOverloadResolver { + private readonly typeComputer: ZenScriptTypeComputer + private readonly memberProvider: ZenScriptMemberProvider + private readonly descriptionIndex: ZenScriptDescriptionIndex + private readonly typeFeatures: ZenScriptTypeFeatures + private readonly nameProvider: NameProvider + + constructor(services: ZenScriptServices) { + this.typeComputer = services.typing.TypeComputer + this.memberProvider = services.references.MemberProvider + this.descriptionIndex = services.workspace.DescriptionIndex + this.typeFeatures = services.typing.TypeFeatures + this.nameProvider = services.references.NameProvider + } + + findOverlaodConstructor(classDecl: ClassDeclaration, callExpr: CallExpression): AstNode | undefined { + const constructors = classDecl.members + .filter(it => isConstructorDeclaration(it)) + + if (constructors.length === 0) { + return + } + + const index = this.resolveOverload(constructors, callExpr.arguments) + + if (index.length === 0) { + return constructors[0] + } + return constructors[index[0]] + } + + findOverloadMethod(members: Stream, callExpr: CallExpression, name?: string): AstNode | undefined { + const found = members.filter(it => !name || this.nameProvider.getName(it) === name).toArray() + + const clazz = found.find(it => isClassDeclaration(it)) + if (clazz) { + return this.findOverlaodConstructor(clazz, callExpr) + } + + const methods = found.filter(it => isFunctionDeclaration(it) || isExpandFunctionDeclaration(it)) + + const index = this.resolveOverload(methods, callExpr.arguments) + + if (index.length === 0) { + return methods[0] + } + return methods[index[0]] + } + + predictCallType(callExpr: CallExpression): Type | undefined { + if (isReferenceExpression(callExpr.receiver)) { + if (callExpr.receiver.target.$nodeDescription) { + return this.typeComputer.inferType(callExpr.receiver.target.$nodeDescription.node) + } + const script = AstUtils.findRootNode(callExpr.receiver) + + if (!isScript(script)) { + return + } + + // TODO(import) + // const refText = callExpr.receiver.target.$refText + + // const imports = stream(script.imports) + // .map(it => this.descriptionIndex.createImportedDescription(it)) + // .flatMap(it => it) + + // const overload = this.findOverloadMethod(imports, callExpr, refText) + + // if (overload) { + // return this.typeComputer.inferType(overload.node) + // } + return + } + + if (isMemberAccess(callExpr.receiver)) { + if (callExpr.receiver.target.$nodeDescription) { + return this.typeComputer.inferType(callExpr.receiver.target.$nodeDescription.node) + } + const receiverType = this.typeComputer.inferType(callExpr.receiver.receiver) + const candidates = this.memberProvider.streamMembers(receiverType) + + const overload = this.predictOverloadMethod(candidates, callExpr, callExpr.receiver.target.$refText) + if (overload) { + return this.typeComputer.inferType(overload.node) + } + } + } + + predictOverloadMethod(members: Stream, callExpr: CallExpression, name: string): AstNodeDescription | undefined { + const found = members.filter(it => this.nameProvider.getName(it) === name) + + const classDesc = found.find(it => isClassDeclaration(it)) + if (classDesc) { + const ctorDecl = classDesc.members.find((it) => { + if (isConstructorDeclaration(it)) { + return true + } + return this.matchSignature(it as CallableDeclaration, callExpr.arguments.length) !== OverloadMatch.NotMatch + }) + + if (!ctorDecl) { + return + } + return this.descriptionIndex.getDescription(ctorDecl) + } + + const functionDecl = found.filter(it => isFunctionDeclaration(it) || isExpandFunctionDeclaration(it)) + .find((it) => { + return this.matchSignature(it, callExpr.arguments.length) !== OverloadMatch.NotMatch + }) + + if (!functionDecl) { + return + } + + return this.descriptionIndex.getDescription(functionDecl) + } + + resolveOverload(methods: Array, args: Array): number[] { + const possible = new Set() + + for (let i = 0; i < methods.length; i++) { + const currentMatch = this.matchSignature(methods[i], args.length) + if (currentMatch !== OverloadMatch.NotMatch) { + possible.add(i) + } + } + + if (possible.size === 0) { + return [] + } + + if (possible.size === 1) { + return [...possible.values()] + } + + const argTypes = args.map((it) => { + if (isFunctionExpression(it)) { + return new FunctionType([], this.typeComputer.classTypeOf('void')) + } + return this.typeComputer.inferType(it) || this.typeComputer.classTypeOf('any') + }) + + let bestMatch = OverloadMatch.NotMatch + let matchIndexes: number[] = [] + for (let i = 0; i < methods.length; i++) { + if (!possible.has(i)) { + continue + } + const currentMatch = this.matchSignature(methods[i], argTypes) + if (currentMatch === OverloadMatch.FullMatch) { + return [i] + } + if (currentMatch < bestMatch) { + matchIndexes = [i] + bestMatch = currentMatch + } + else if (currentMatch === bestMatch) { + // duplicate match + matchIndexes.push(i) + } + } + + if (matchIndexes.length > 1) { + this.logAmbiguousOverload(methods, argTypes, bestMatch, matchIndexes) + } + + if (bestMatch === OverloadMatch.NotMatch) { + return [] + } + + return matchIndexes + } + + private logAmbiguousOverload(methods: ArrayLike, argTypes: Type[], bestMatch: OverloadMatch, matchIndexes: number[]) { + let methodName = '' + if (isConstructorDeclaration(methods[0])) { + methodName = ((methods[0]).$container as ClassDeclaration).name + } + else { + methodName = (methods[0]).name + } + + const MATCH_NAMES = ['FullMatch', 'OptionalMatch', 'ImplicitMatch', 'PossibleMatch', 'NotMatch'] + + const argTypeStrings = argTypes.map(it => it?.toString() ?? 'undefined').join(', ') + console.warn(`ambiguous overload for ${methodName} with arguments (${argTypeStrings}), match: ${MATCH_NAMES[bestMatch]}`) + for (const index of matchIndexes) { + const params = (methods[index]).parameters.map(it => this.typeComputer.inferType(it)) + .map(it => it?.toString() ?? 'undefined').join(', ') + console.warn(`----- ${methodName} (${params})`) + } + } + + matchSignature(method: CallableDeclaration, args: Type[] | number): OverloadMatch { + const paramters = method.parameters + if (isExpandFunctionDeclaration(method)) { + // remove first parameter + paramters.shift() + } + const checkType = Array.isArray(args) + + let match = OverloadMatch.FullMatch + + const argumentLength = checkType ? args.length : args + + let checkLength = Math.min(argumentLength, paramters.length) + + const paramterTypes = checkType ? paramters.map(p => this.typeComputer.inferType(p) || this.typeComputer.classTypeOf('any')) : [] + + if (argumentLength > paramters.length) { + if (!isVarargs(method)) { + return OverloadMatch.NotMatch + } + + if (!checkType) { + return OverloadMatch.PossibleMatch + } + + const varargsType = paramterTypes[paramterTypes.length - 1] + match = OverloadMatch.ImplicitMatch + for (let i = paramters.length - 1; i < argumentLength; i++) { + if (this.typeFeatures.areTypesEqual(varargsType, args[i])) { + continue + } + match = OverloadMatch.ImplicitMatch + if (!this.typeFeatures.isAssignable(varargsType, args[i])) { + return OverloadMatch.NotMatch + } + } + } + else if (argumentLength < paramters.length) { + if (!isOptionalArgs(method, argumentLength)) { + return OverloadMatch.NotMatch + } + + if (!checkType) { + return OverloadMatch.PossibleMatch + } + + match = OverloadMatch.OptionalMatch + } + else if (isVarargs(method)) { + if (!checkType) { + return OverloadMatch.PossibleMatch + } + const varargsType = paramterTypes[paramterTypes.length - 1] + if (!varargsType) { + return OverloadMatch.NotMatch + } + + const lastArgType = args.at(-1)! + const varargsTypeArray = this.typeComputer.arrayTypeOf(varargsType) + + if (this.typeFeatures.areTypesEqual(varargsType, lastArgType) || this.typeFeatures.areTypesEqual(varargsTypeArray, lastArgType)) { + return OverloadMatch.FullMatch + } + + if (!this.typeFeatures.isAssignable(varargsType, lastArgType) && !this.typeFeatures.isAssignable(varargsTypeArray, lastArgType)) { + return OverloadMatch.NotMatch + } + + match = OverloadMatch.ImplicitMatch + checkLength-- + } + + if (!checkType) { + return OverloadMatch.PossibleMatch + } + + for (let i = 0; i < checkLength; i++) { + if (this.typeFeatures.areTypesEqual(paramterTypes[i], args[i])) { + continue + } + match = OverloadMatch.ImplicitMatch + if (!this.typeFeatures.isAssignable(paramterTypes[i], args[i])) { + return OverloadMatch.NotMatch + } + } + + return match + } +} diff --git a/packages/zenscript/src/typing/type-computer.ts b/packages/zenscript/src/typing/type-computer.ts index 0e4d7f95..0cbfb08e 100644 --- a/packages/zenscript/src/typing/type-computer.ts +++ b/packages/zenscript/src/typing/type-computer.ts @@ -12,6 +12,8 @@ import { ClassType, CompoundType, FunctionType, IntersectionType, isAnyType, isC export interface TypeComputer { inferType: (node: AstNode | undefined) => Type | undefined + classTypeOf: (className: BuiltinTypes | string, substitutions?: TypeParameterSubstitutions) => ClassType + arrayTypeOf: (elementType: Type) => ClassType } type SourceMap = ZenScriptAstType & ZenScriptSyntheticAstType @@ -32,7 +34,7 @@ export class ZenScriptTypeComputer implements TypeComputer { return this.rules(node?.$type)?.call(this, node) } - private classTypeOf(className: BuiltinTypes | string, substitutions: TypeParameterSubstitutions = new Map()): ClassType { + public classTypeOf(className: BuiltinTypes | string, substitutions: TypeParameterSubstitutions = new Map()): ClassType { const classDecl = this.classDeclOf(className) if (!classDecl) { throw new Error(`Class "${className}" is not defined.`) @@ -44,13 +46,18 @@ export class ZenScriptTypeComputer implements TypeComputer { return stream(this.packageManager.retrieve(className)).find(isClassDeclaration) } + public arrayTypeOf(elementType: Type): ClassType { + const arrayType = this.classTypeOf('Array') + const T = arrayType.declaration.typeParameters[0] + arrayType.substitutions.set(T, elementType) + return arrayType + } + private readonly rules = defineRules({ // region TypeReference ArrayTypeReference: (source) => { - const arrayType = this.classTypeOf('Array') - const T = arrayType.declaration.typeParameters[0] - arrayType.substitutions.set(T, this.inferType(source.value) ?? this.classTypeOf('any')) - return arrayType + const elementType = this.inferType(source.value) ?? this.classTypeOf('any') + return this.arrayTypeOf(elementType) }, ListTypeReference: (source) => { diff --git a/packages/zenscript/src/typing/type-features.ts b/packages/zenscript/src/typing/type-features.ts index 1d94a21c..dda7a37f 100644 --- a/packages/zenscript/src/typing/type-features.ts +++ b/packages/zenscript/src/typing/type-features.ts @@ -124,7 +124,7 @@ export class ZenScriptTypeFeatures implements TypeFeatures { .filter(it => it.op === 'as') .map(it => this.typeComputer.inferType(it.returnTypeRef)) .nonNullable() - .some(it => this.isAssignable(to, it)) + .some(it => this.isSubType(to, it)) }, CompoundType: (from, to) => { diff --git a/packages/zenscript/src/utils/ast.ts b/packages/zenscript/src/utils/ast.ts index 2bb2ddf1..fa792628 100644 --- a/packages/zenscript/src/utils/ast.ts +++ b/packages/zenscript/src/utils/ast.ts @@ -65,9 +65,9 @@ export function toAstNode(item: AstNode | AstNodeDescription): AstNode | undefin } export function streamClassChain(classDecl: ClassDeclaration): Stream { - const visited = new Set() - return stream(function* () { + const generator = function* () { const deque = [classDecl] + const visited = new Set() while (deque.length) { const head = deque.shift()! if (!visited.has(head)) { @@ -79,7 +79,13 @@ export function streamClassChain(classDecl: ClassDeclaration): Stream deque.push(it)) } } - }()) + } + + return stream({ + [Symbol.iterator]() { + return generator()[Symbol.iterator]() + }, + }) } export function streamDeclaredMembers(classDecl: ClassDeclaration): Stream { From 27f3d71857d7e2006e1e0f34943ae5dffadc3cbe Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Thu, 28 Nov 2024 21:52:13 +0800 Subject: [PATCH 02/20] constructor overload --- packages/zenscript/src/reference/member-provider.ts | 4 ++-- packages/zenscript/src/reference/name-provider.ts | 2 +- packages/zenscript/src/typing/type-computer.ts | 11 ++++++++++- packages/zenscript/src/zenscript.langium | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/zenscript/src/reference/member-provider.ts b/packages/zenscript/src/reference/member-provider.ts index e2b16286..204a09e1 100644 --- a/packages/zenscript/src/reference/member-provider.ts +++ b/packages/zenscript/src/reference/member-provider.ts @@ -4,7 +4,7 @@ import type { ZenScriptServices } from '../module' import type { TypeComputer } from '../typing/type-computer' import type { ZenScriptSyntheticAstType } from './synthetic' import { EMPTY_STREAM, stream } from 'langium' -import { isClassDeclaration, isOperatorFunctionDeclaration, isVariableDeclaration } from '../generated/ast' +import { isClassDeclaration, isOperatorFunctionDeclaration, isScript, isVariableDeclaration } from '../generated/ast' import { ClassType, isAnyType, isClassType, isFunctionType, type Type, type ZenScriptType } from '../typing/type-description' import { isStatic, streamClassChain, streamDeclaredMembers } from '../utils/ast' import { defineRules } from '../utils/rule' @@ -80,7 +80,7 @@ export class ZenScriptMemberProvider implements MemberProvider { return EMPTY_STREAM } - if (isSyntheticAstNode(target)) { + if (isSyntheticAstNode(target) || isScript(target)) { return this.streamMembers(target) } diff --git a/packages/zenscript/src/reference/name-provider.ts b/packages/zenscript/src/reference/name-provider.ts index 928cfc1e..a89b0e9c 100644 --- a/packages/zenscript/src/reference/name-provider.ts +++ b/packages/zenscript/src/reference/name-provider.ts @@ -46,7 +46,7 @@ export class ZenScriptNameProvider extends DefaultNameProvider { Script: source => source.$document ? getName(source.$document) : undefined, ImportDeclaration: source => source.alias || source.path.at(-1)?.$refText, FunctionDeclaration: source => source.name || 'lambda function', - ConstructorDeclaration: _ => 'zenConstructor', + ConstructorDeclaration: source => source.$container.name, OperatorFunctionDeclaration: source => source.op, }) diff --git a/packages/zenscript/src/typing/type-computer.ts b/packages/zenscript/src/typing/type-computer.ts index 0cbfb08e..d1188391 100644 --- a/packages/zenscript/src/typing/type-computer.ts +++ b/packages/zenscript/src/typing/type-computer.ts @@ -6,7 +6,7 @@ import type { BracketManager } from '../workspace/bracket-manager' import type { PackageManager } from '../workspace/package-manager' import type { BuiltinTypes, Type, TypeParameterSubstitutions } from './type-description' import { type AstNode, stream } from 'langium' -import { isAssignment, isCallExpression, isClassDeclaration, isExpression, isFunctionDeclaration, isFunctionExpression, isOperatorFunctionDeclaration, isTypeParameter, isVariableDeclaration } from '../generated/ast' +import { isAssignment, isCallExpression, isClassDeclaration, isConstructorDeclaration, isExpression, isFunctionDeclaration, isFunctionExpression, isMemberAccess, isOperatorFunctionDeclaration, isReferenceExpression, isTypeParameter, isVariableDeclaration } from '../generated/ast' import { defineRules } from '../utils/rule' import { ClassType, CompoundType, FunctionType, IntersectionType, isAnyType, isClassType, isFunctionType, TypeVariable, UnionType } from './type-description' @@ -362,6 +362,15 @@ export class ZenScriptTypeComputer implements TypeComputer { }, CallExpression: (source) => { + if (isReferenceExpression(source.receiver) || isMemberAccess(source.receiver)) { + const receiverRef = source.receiver.target.ref + if (!receiverRef) { + return + } + if (isConstructorDeclaration(receiverRef)) { + return new ClassType(receiverRef.$container, new Map()) + } + } const receiverType = this.inferType(source.receiver) if (isFunctionType(receiverType)) { return receiverType.returnType diff --git a/packages/zenscript/src/zenscript.langium b/packages/zenscript/src/zenscript.langium index e0b7f8ab..81903ed5 100644 --- a/packages/zenscript/src/zenscript.langium +++ b/packages/zenscript/src/zenscript.langium @@ -28,7 +28,7 @@ interface ClassDeclaration extends Declaration { members: ClassMemberDeclaration[]; } -type NamedElement = Script | ClassDeclaration | FunctionDeclaration | ExpandFunctionDeclaration | FieldDeclaration | ValueParameter| VariableDeclaration | LoopParameter | MapEntry | ImportDeclaration; +type NamedElement = Script | ClassDeclaration | FunctionDeclaration | ExpandFunctionDeclaration | FieldDeclaration | ValueParameter| VariableDeclaration | LoopParameter | MapEntry | ImportDeclaration | ConstructorDeclaration; type ClassMemberDeclaration = FunctionDeclaration | FieldDeclaration | ConstructorDeclaration | OperatorFunctionDeclaration; From a7ab96cdfddd361d932573fa1e7a675a1d88da55 Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Thu, 28 Nov 2024 22:20:30 +0800 Subject: [PATCH 03/20] reference expr overloading --- .../zenscript/src/reference/name-provider.ts | 2 +- .../zenscript/src/reference/scope-provider.ts | 45 +++++++++++++++++-- .../zenscript/src/typing/overload-resolver.ts | 4 +- .../src/workspace/description-index.ts | 43 +++++++++++++----- 4 files changed, 77 insertions(+), 17 deletions(-) diff --git a/packages/zenscript/src/reference/name-provider.ts b/packages/zenscript/src/reference/name-provider.ts index a89b0e9c..dc88e7ae 100644 --- a/packages/zenscript/src/reference/name-provider.ts +++ b/packages/zenscript/src/reference/name-provider.ts @@ -52,7 +52,7 @@ export class ZenScriptNameProvider extends DefaultNameProvider { private readonly nameNodeRules = defineRules({ ImportDeclaration: source => GrammarUtils.findNodeForProperty(source.$cstNode, 'alias'), - ConstructorDeclaration: source => GrammarUtils.findNodeForProperty(source.$cstNode, 'zenConstructor'), + ConstructorDeclaration: source => GrammarUtils.findNodeForKeyword(source.$cstNode, 'zenConstructor'), OperatorFunctionDeclaration: source => GrammarUtils.findNodeForProperty(source.$cstNode, 'op'), }) } diff --git a/packages/zenscript/src/reference/scope-provider.ts b/packages/zenscript/src/reference/scope-provider.ts index 7c783a5a..91a6fab7 100644 --- a/packages/zenscript/src/reference/scope-provider.ts +++ b/packages/zenscript/src/reference/scope-provider.ts @@ -1,5 +1,5 @@ import type { AstNode, AstNodeDescription, ReferenceInfo, Scope, ScopeOptions, Stream } from 'langium' -import type { ZenScriptAstType } from '../generated/ast' +import type { CallExpression, ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { ZenScriptOverloadResolver } from '../typing/overload-resolver' import type { ZenScriptDescriptionIndex } from '../workspace/description-index' @@ -8,7 +8,7 @@ import type { DynamicProvider } from './dynamic-provider' import type { MemberProvider } from './member-provider' import { substringBeforeLast } from '@intellizen/shared' import { AstUtils, DefaultScopeProvider, EMPTY_SCOPE, stream, StreamScope } from 'langium' -import { ClassDeclaration, ImportDeclaration, isCallExpression, isClassDeclaration, TypeParameter } from '../generated/ast' +import { ClassDeclaration, ImportDeclaration, isCallExpression, isClassDeclaration, isScript, TypeParameter } from '../generated/ast' import { getPathAsString } from '../utils/ast' import { defineRules } from '../utils/rule' import { generateStream } from '../utils/stream' @@ -71,6 +71,31 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { return this.createScopeForNodes(classes, outside) } + private importScope(source: ReferenceInfo, outer?: Scope) { + const script = AstUtils.findRootNode(source.container) + if (!isScript(script)) { + return EMPTY_SCOPE + } + + const imports = stream(script.imports) + .flatMap(it => this.descriptionIndex.createImportedDescription(it)) + + if (source.reference.$refText === '' || !isCallExpression(source.container.$container) || source.container.$containerProperty !== 'receiver') { + return this.createScope(imports, outer) + } + + // filter here since import may have alias + const candidates = imports.filter(it => it.name === source.reference.$refText).map(it => it.node).nonNullable() + + const overload = this.overloadResolver.findOverloadMethod(candidates, source.container.$container, undefined) + if (!overload) { + return outer || EMPTY_SCOPE + } + + const description = this.descriptionIndex.createDynamicDescription(overload, source.reference.$refText) + return this.createScope([description], outer) + } + override createScopeForNodes(nodes: Stream, outerScope?: Scope, options?: ScopeOptions): Scope { return new StreamScope(nodes.map(it => this.descriptionIndex.getDescription(it)), outerScope, options) } @@ -100,14 +125,26 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { let outer: Scope outer = this.packageScope() outer = this.globalScope(outer) + outer = this.importScope(source, outer) outer = this.dynamicScope(source.container, outer) + const processOverload = source.reference.$refText !== '' && isCallExpression(source.container.$container) && source.container.$containerProperty === 'receiver' + const processor = (desc: AstNodeDescription) => { switch (desc.type) { case TypeParameter: return case ImportDeclaration: { - return this.descriptionIndex.createImportedDescription(desc.node as ImportDeclaration) + return + } + case ClassDeclaration: { + if (processOverload) { + const ctor = this.overloadResolver.findOverloadConstructor(desc.node as ClassDeclaration, source.container.$container as CallExpression) + if (ctor) { + return this.descriptionIndex.getDescription(ctor) + } + } + return desc } default: return desc @@ -140,7 +177,7 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { case ClassDeclaration: return desc case ImportDeclaration: { - return this.descriptionIndex.createImportedDescription(desc.node as ImportDeclaration) + return this.descriptionIndex.createImportedDescription(desc.node as ImportDeclaration)[0] } } } diff --git a/packages/zenscript/src/typing/overload-resolver.ts b/packages/zenscript/src/typing/overload-resolver.ts index 5eddea33..892671ae 100644 --- a/packages/zenscript/src/typing/overload-resolver.ts +++ b/packages/zenscript/src/typing/overload-resolver.ts @@ -52,7 +52,7 @@ export class ZenScriptOverloadResolver { this.nameProvider = services.references.NameProvider } - findOverlaodConstructor(classDecl: ClassDeclaration, callExpr: CallExpression): AstNode | undefined { + findOverloadConstructor(classDecl: ClassDeclaration, callExpr: CallExpression): AstNode | undefined { const constructors = classDecl.members .filter(it => isConstructorDeclaration(it)) @@ -73,7 +73,7 @@ export class ZenScriptOverloadResolver { const clazz = found.find(it => isClassDeclaration(it)) if (clazz) { - return this.findOverlaodConstructor(clazz, callExpr) + return this.findOverloadConstructor(clazz, callExpr) } const methods = found.filter(it => isFunctionDeclaration(it) || isExpandFunctionDeclaration(it)) diff --git a/packages/zenscript/src/workspace/description-index.ts b/packages/zenscript/src/workspace/description-index.ts index b1914c7e..170b737b 100644 --- a/packages/zenscript/src/workspace/description-index.ts +++ b/packages/zenscript/src/workspace/description-index.ts @@ -1,14 +1,14 @@ -import type { AstNode, AstNodeDescription, NameProvider } from 'langium' -import type { ClassDeclaration, ImportDeclaration } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { DescriptionCreator } from './description-creator' -import { getDocumentUri } from '../utils/ast' +import { type AstNode, type AstNodeDescription, type NameProvider, stream } from 'langium' +import { type ClassDeclaration, type ImportDeclaration, isClassDeclaration, isFunctionDeclaration } from '../generated/ast' +import { getDocumentUri, isStatic } from '../utils/ast' export interface DescriptionIndex { getDescription: (astNode: AstNode) => AstNodeDescription getThisDescription: (classDecl: ClassDeclaration) => AstNodeDescription createDynamicDescription: (astNode: AstNode, name: string) => AstNodeDescription - createImportedDescription: (importDecl: ImportDeclaration) => AstNodeDescription + createImportedDescription: (importDecl: ImportDeclaration) => AstNodeDescription[] } export class ZenScriptDescriptionIndex implements DescriptionIndex { @@ -44,28 +44,51 @@ export class ZenScriptDescriptionIndex implements DescriptionIndex { } createDynamicDescription(astNode: AstNode, name: string): AstNodeDescription { - const originalUri = this.astDescriptions.get(astNode)?.documentUri + const existing = this.astDescriptions.get(astNode) + if (existing?.name === name) { + return existing + } + const originalUri = existing?.documentUri return this.creator.createDescriptionWithUri(astNode, originalUri, name) } - createImportedDescription(importDecl: ImportDeclaration): AstNodeDescription { + createImportedDescription(importDecl: ImportDeclaration): AstNodeDescription[] { const targetRef = importDecl.path.at(-1) if (!targetRef) { - return this.getDescription(importDecl) + return [this.getDescription(importDecl)] } const target = targetRef.ref if (!target) { - return this.getDescription(importDecl) + return [this.getDescription(importDecl)] + } + + // handle import overloading + if (isFunctionDeclaration(target)) { + // Find function with same name in the same package + const parentRef = importDecl.path.at(-2)?.ref + if (!parentRef) { + return [] + } + + if (isClassDeclaration(parentRef)) { + const result = stream(parentRef.members) + .filter(it => isFunctionDeclaration(it)) + .filter(it => it.name === target.name) + .filter(it => isStatic(it)) + .map(it => this.createDynamicDescription(it, it.name)) + .toArray() + return result + } } const targetDescription = targetRef.$nodeDescription if (!importDecl.alias && targetDescription) { - return targetDescription + return [targetDescription] } const targetUri = targetDescription?.documentUri const alias = this.nameProvider.getName(importDecl) - return this.creator.createDescriptionWithUri(target, targetUri, alias) + return [this.creator.createDescriptionWithUri(target, targetUri, alias)] } } From a96f3483393a4741ce0f781fece801affc4e36b2 Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Thu, 28 Nov 2024 22:22:10 +0800 Subject: [PATCH 04/20] remove some unexpected comment --- packages/zenscript/src/reference/member-provider.ts | 2 +- packages/zenscript/src/typing/overload-resolver.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/zenscript/src/reference/member-provider.ts b/packages/zenscript/src/reference/member-provider.ts index 204a09e1..a4042919 100644 --- a/packages/zenscript/src/reference/member-provider.ts +++ b/packages/zenscript/src/reference/member-provider.ts @@ -160,4 +160,4 @@ export class ZenScriptMemberProvider implements MemberProvider { .filter(it => !isStatic(it)) }, }) -}/* */ +} diff --git a/packages/zenscript/src/typing/overload-resolver.ts b/packages/zenscript/src/typing/overload-resolver.ts index 892671ae..2c184516 100644 --- a/packages/zenscript/src/typing/overload-resolver.ts +++ b/packages/zenscript/src/typing/overload-resolver.ts @@ -2,14 +2,13 @@ import type { AstNode, AstNodeDescription, NameProvider, Stream } from 'langium' import type { CallExpression, ClassDeclaration, ConstructorDeclaration, ExpandFunctionDeclaration, Expression, FunctionDeclaration } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { ZenScriptMemberProvider } from '../reference/member-provider' -import type { ZenScriptNameProvider } from '../reference/name-provider' -import type { ClassType, IntersectionType, Type, UnionType } from '../typing/type-description' +import type { Type } from '../typing/type-description' import type { ZenScriptDescriptionIndex } from '../workspace/description-index' import type { ZenScriptTypeComputer } from './type-computer' import type { ZenScriptTypeFeatures } from './type-features' -import { AstUtils, stream } from 'langium' -import { isClassDeclaration, isConstructorDeclaration, isExpandFunctionDeclaration, isFunctionDeclaration, isFunctionExpression, isMemberAccess, isOperatorFunctionDeclaration, isReferenceExpression, isScript } from '../generated/ast' -import { FunctionType, isClassType, isCompoundType, isTypeVariable } from '../typing/type-description' +import { AstUtils } from 'langium' +import { isClassDeclaration, isConstructorDeclaration, isExpandFunctionDeclaration, isFunctionDeclaration, isFunctionExpression, isMemberAccess, isReferenceExpression, isScript } from '../generated/ast' +import { FunctionType } from '../typing/type-description' export enum OverloadMatch { FullMatch = 0, From b1b286d6b3801f241400c3286c3dff877f770693 Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Fri, 29 Nov 2024 00:53:10 +0800 Subject: [PATCH 05/20] add test --- .../zenscript/src/reference/scope-provider.ts | 3 +- .../test/typing/overloading/intellizen.json | 5 + .../typing/overloading/overloading.test.ts | 180 ++++++++++++++++++ .../typing/overloading/scripts/overload.zs | 25 +++ .../overloading/scripts/overload_ctor.zs | 7 + .../overloading/scripts/overload_static.zs | 8 + .../overloading/scripts/overload_type.dzs | 32 ++++ .../overloading/scripts/static_functions.dzs | 6 + packages/zenscript/test/utils.ts | 1 + 9 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 packages/zenscript/test/typing/overloading/intellizen.json create mode 100644 packages/zenscript/test/typing/overloading/overloading.test.ts create mode 100644 packages/zenscript/test/typing/overloading/scripts/overload.zs create mode 100644 packages/zenscript/test/typing/overloading/scripts/overload_ctor.zs create mode 100644 packages/zenscript/test/typing/overloading/scripts/overload_static.zs create mode 100644 packages/zenscript/test/typing/overloading/scripts/overload_type.dzs create mode 100644 packages/zenscript/test/typing/overloading/scripts/static_functions.dzs diff --git a/packages/zenscript/src/reference/scope-provider.ts b/packages/zenscript/src/reference/scope-provider.ts index 91a6fab7..c0ca0a98 100644 --- a/packages/zenscript/src/reference/scope-provider.ts +++ b/packages/zenscript/src/reference/scope-provider.ts @@ -170,7 +170,8 @@ export class ZenScriptScopeProvider extends DefaultScopeProvider { NamedTypeReference: (source) => { if (!source.index) { - const outer = this.classScope() + let outer = this.packageScope() + outer = this.classScope(outer) const processor = (desc: AstNodeDescription) => { switch (desc.type) { case TypeParameter: diff --git a/packages/zenscript/test/typing/overloading/intellizen.json b/packages/zenscript/test/typing/overloading/intellizen.json new file mode 100644 index 00000000..1e293b03 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/intellizen.json @@ -0,0 +1,5 @@ +{ + "srcRoots": [ + "./scripts" + ] +} diff --git a/packages/zenscript/test/typing/overloading/overloading.test.ts b/packages/zenscript/test/typing/overloading/overloading.test.ts new file mode 100644 index 00000000..4be34cd6 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/overloading.test.ts @@ -0,0 +1,180 @@ +import type { AstNode } from 'langium' +import type { MemberAccess, ReferenceExpression, Statement } from '../../../src/generated/ast' +import path from 'node:path' +import { AstUtils } from 'langium' +import { assert, describe, expect, it, suite } from 'vitest' +import { isCallExpression, isConstructorDeclaration, isFunctionDeclaration } from '../../../src/generated/ast' +import { assertNoErrors, createTestServices, getDocument } from '../../utils' + +const services = await createTestServices(__dirname) + +function findOverloadForCall(call: Statement): AstNode { + const callExpr = AstUtils.streamAst(call).find(isCallExpression) + expect(callExpr).toBeDefined() + expect(callExpr?.receiver?.$type).toMatch(/ReferenceExpression|MemberAccess/) + const receiver = callExpr!.receiver as ReferenceExpression | MemberAccess + const target = receiver.target.ref + expect(target).toBeDefined() + return target! +} + +describe('check overload', async () => { + const document_overload_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'overload.zs')) + const script_overload_zs = document_overload_zs.parseResult.value + + it('syntax', () => { + assertNoErrors(document_overload_zs) + expect(script_overload_zs.statements[0].$type).toBe('VariableDeclaration') + }) + + suite('overload_basic', () => { + it('normal', () => { + const foo = findOverloadForCall(script_overload_zs.statements[1]) + assert(isFunctionDeclaration(foo)) + expect(foo.name).toBe('foo') + expect(foo.parameters.length).toBe(0) + + const foo1 = findOverloadForCall(script_overload_zs.statements[2]) + assert(isFunctionDeclaration(foo1)) + expect(foo1.name).toBe('foo') + expect(foo1.parameters.length).toBe(1) + + const foo2 = findOverloadForCall(script_overload_zs.statements[3]) + assert(isFunctionDeclaration(foo2)) + expect(foo2.name).toBe('foo') + expect(foo2.parameters.length).toBe(2) + expect(foo2.parameters[1].typeRef?.$cstNode?.text).toBe('int') + + const foo3 = findOverloadForCall(script_overload_zs.statements[4]) + assert(isFunctionDeclaration(foo3)) + expect(foo3.name).toBe('foo') + expect(foo3.parameters.length).toBe(2) + expect(foo3.parameters[1].typeRef?.$cstNode?.text).toBe('double') + }) + + it('varargs', () => { + const varargs_low_priorty = findOverloadForCall(script_overload_zs.statements[5]) + assert(isFunctionDeclaration(varargs_low_priorty)) + expect(varargs_low_priorty.name).toBe('varargs') + expect(varargs_low_priorty.parameters.length).toBe(1) + + const varargs_full = findOverloadForCall(script_overload_zs.statements[6]) + assert(isFunctionDeclaration(varargs_full)) + expect(varargs_full.name).toBe('varargs') + expect(varargs_full.parameters.length).toBe(2) + expect(varargs_full.parameters[1].varargs).toBe(true) + + const varargs_more = findOverloadForCall(script_overload_zs.statements[7]) + assert(isFunctionDeclaration(varargs_more)) + expect(varargs_more.name).toBe('varargs') + expect(varargs_more.parameters.length).toBe(2) + expect(varargs_more.parameters[1].varargs).toBe(true) + + const varargs_less = findOverloadForCall(script_overload_zs.statements[8]) + assert(isFunctionDeclaration(varargs_less)) + expect(varargs_less.name).toBe('varargs_miss') + expect(varargs_less.parameters.length).toBe(1) + expect(varargs_less.parameters[0].varargs).toBe(true) + }) + + it('optional', () => { + const optional_eq = findOverloadForCall(script_overload_zs.statements[9]) + assert(isFunctionDeclaration(optional_eq)) + expect(optional_eq.name).toBe('optional') + expect(optional_eq.parameters.length).toBe(2) + expect(optional_eq.parameters[1].defaultValue).toBeDefined() + + const optional_low_priorty = findOverloadForCall(script_overload_zs.statements[10]) + assert(isFunctionDeclaration(optional_low_priorty)) + expect(optional_low_priorty.name).toBe('optional') + expect(optional_low_priorty.parameters.length).toBe(1) + + const optional_less = findOverloadForCall(script_overload_zs.statements[11]) + assert(isFunctionDeclaration(optional_less)) + expect(optional_less.name).toBe('optional_miss') + expect(optional_less.parameters.length).toBe(1) + expect(optional_less.parameters[0].defaultValue).toBeDefined() + + const optional_convert_1 = findOverloadForCall(script_overload_zs.statements[12]) + assert(isFunctionDeclaration(optional_convert_1)) + expect(optional_convert_1.name).toBe('optional_convert') + expect(optional_convert_1.parameters.length).toBe(2) + expect(optional_convert_1.parameters[1].defaultValue).toBeDefined() + + const optional_convert_2 = findOverloadForCall(script_overload_zs.statements[13]) + assert(isFunctionDeclaration(optional_convert_2)) + expect(optional_convert_2.name).toBe('optional_convert') + expect(optional_convert_2.parameters.length).toBe(2) + expect(optional_convert_2.parameters[1].defaultValue).toBeDefined() + + const optional_vs_varargs = findOverloadForCall(script_overload_zs.statements[14]) + assert(isFunctionDeclaration(optional_vs_varargs)) + expect(optional_vs_varargs.name).toBe('varargs_vs_optional') + expect(optional_vs_varargs.parameters.length).toBe(1) + expect(optional_vs_varargs.parameters[0].varargs).toBe(false) + expect(optional_vs_varargs.parameters[0].defaultValue).toBeDefined() + }) + }) +}) + +describe('check static overload', async () => { + const document_overload_static_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'overload_static.zs')) + const script_overload_zs = document_overload_static_zs.parseResult.value + + it('syntax', () => { + assertNoErrors(document_overload_static_zs) + }) + + it('import overload', () => { + const foo1 = findOverloadForCall(script_overload_zs.statements[0]) + assert(isFunctionDeclaration(foo1)) + expect(foo1.name).toBe('foo') + expect(foo1.parameters.length).toBe(0) + + const foo2 = findOverloadForCall(script_overload_zs.statements[1]) + assert(isFunctionDeclaration(foo2)) + expect(foo2.name).toBe('foo') + expect(foo2.parameters.length).toBe(1) + }) + + it('member access', () => { + const foo1 = findOverloadForCall(script_overload_zs.statements[2]) + assert(isFunctionDeclaration(foo1)) + expect(foo1.name).toBe('foo') + expect(foo1.parameters.length).toBe(0) + + const foo2 = findOverloadForCall(script_overload_zs.statements[3]) + assert(isFunctionDeclaration(foo2)) + expect(foo2.name).toBe('foo') + expect(foo2.parameters.length).toBe(1) + }) +}) + +describe('check ctor overload', async () => { + const document_overload_ctor_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'overload_ctor.zs')) + const script_overload_zs = document_overload_ctor_zs.parseResult.value + + it('syntax', () => { + assertNoErrors(document_overload_ctor_zs) + }) + + it('ctor overload import', () => { + const foo1 = findOverloadForCall(script_overload_zs.statements[0]) + assert(isConstructorDeclaration(foo1)) + expect(foo1.parameters.length).toBe(0) + + const foo2 = findOverloadForCall(script_overload_zs.statements[1]) + assert(isConstructorDeclaration(foo2)) + expect(foo2.parameters.length).toBe(1) + }) + + it('ctor overload member access', () => { + const foo1 = findOverloadForCall(script_overload_zs.statements[2]) + assert(isConstructorDeclaration(foo1)) + expect(foo1.parameters.length).toBe(0) + + const foo2 = findOverloadForCall(script_overload_zs.statements[3]) + assert(isConstructorDeclaration(foo2)) + expect(foo2.parameters.length).toBe(1) + }) +}) diff --git a/packages/zenscript/test/typing/overloading/scripts/overload.zs b/packages/zenscript/test/typing/overloading/scripts/overload.zs new file mode 100644 index 00000000..a76afe06 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/scripts/overload.zs @@ -0,0 +1,25 @@ + +val obj as intellizen.test.Overload; + +obj.foo(); +obj.foo(1); + +obj.foo(1, 1); +obj.foo(1, 1.0); + +obj.varargs(1); +obj.varargs(1, 2); +obj.varargs(1, 2, 3); + +obj.varargs_miss(); + +obj.optional(1, 2); +obj.optional(1); + +obj.optional_miss(); + +obj.optional_convert(1); +obj.optional_convert(1, 2.0); + + +obj.varargs_vs_optional(1); \ No newline at end of file diff --git a/packages/zenscript/test/typing/overloading/scripts/overload_ctor.zs b/packages/zenscript/test/typing/overloading/scripts/overload_ctor.zs new file mode 100644 index 00000000..916ef237 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/scripts/overload_ctor.zs @@ -0,0 +1,7 @@ +import intellizen.test.Overload + +val obj1 = Overload(); +val obj2 = Overload(1); + +val obj3 = intellizen.test.Overload(); +val obj4 = intellizen.test.Overload(1); diff --git a/packages/zenscript/test/typing/overloading/scripts/overload_static.zs b/packages/zenscript/test/typing/overloading/scripts/overload_static.zs new file mode 100644 index 00000000..4205d854 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/scripts/overload_static.zs @@ -0,0 +1,8 @@ +import intellizen.test.StaticOverload.foo; + +foo(); +foo(1); + +intellizen.test.StaticOverload.foo(); +intellizen.test.StaticOverload.foo(1); + diff --git a/packages/zenscript/test/typing/overloading/scripts/overload_type.dzs b/packages/zenscript/test/typing/overloading/scripts/overload_type.dzs new file mode 100644 index 00000000..cd2a3570 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/scripts/overload_type.dzs @@ -0,0 +1,32 @@ +package intellizen.test; + +zenClass Overload { + function foo(); + function foo(intVal as int); + + function foo(intVal as int, doubleVal as double); + function foo(intVal as int, intVal as int); + + function varargs(); + function varargs(intVal as int, ...rest as int); + function varargs(intVal as int); + + function varargs_miss(...rest as int); + function varargs_miss(...rest as int); + + function optional(intVal as int, optionalInt as int = 1); + function optional(intVal as int); + function optional_miss(optionalInt as int = 1); + + function optional_convert(intVal as int, optionalInt as int = 1); + function optional_convert(doubleVal as double); + + function varargs_vs_optional(optionalInt as int = 1); + function varargs_vs_optional(...intVarargs as int); + + + zenConstructor (); + zenConstructor (intVal as int); + + +} \ No newline at end of file diff --git a/packages/zenscript/test/typing/overloading/scripts/static_functions.dzs b/packages/zenscript/test/typing/overloading/scripts/static_functions.dzs new file mode 100644 index 00000000..f33408b3 --- /dev/null +++ b/packages/zenscript/test/typing/overloading/scripts/static_functions.dzs @@ -0,0 +1,6 @@ +package intellizen.test; + +zenClass StaticOverload { + static function foo(); + static function foo(intVal as int); +} \ No newline at end of file diff --git a/packages/zenscript/test/utils.ts b/packages/zenscript/test/utils.ts index f83d2dc2..15d8123f 100644 --- a/packages/zenscript/test/utils.ts +++ b/packages/zenscript/test/utils.ts @@ -29,6 +29,7 @@ export async function getDocument(services: ZenScriptServices, docPath: string) } export async function assertNoErrors(model: LangiumDocument