diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 84369e7..0ccf892 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -554,19 +554,19 @@ function typeBody(type, tokens, owner, imports, typemap) { /** * @param {Token[]} modifiers * @param {SourceAnnotation[]} annotations - * @param {TypeVariable[]} type_variables + * @param {TypeVariable[]} type_vars * @param {SourceType} type * @param {TokenList} tokens * @param {ResolvedImport[]} imports * @param {Map} typemap */ -function fmc(modifiers, annotations, type_variables, type, tokens, imports, typemap) { - let decl_type_ident = typeIdent(tokens, type, imports, typemap); +function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap) { + let decl_type_ident = typeIdent(tokens, type, imports, typemap, { no_array_qualifiers: false, type_vars }); if (decl_type_ident.resolved.rawTypeSignature === type.rawTypeSignature) { if (tokens.current.value === '(') { // constructor - const { parameters, throws, body } = methodDeclaration(type, tokens, imports, typemap); - const ctr = new SourceConstructor(type, modifiers, parameters, throws, body); + const { parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap); + const ctr = new SourceConstructor(type, type_vars, modifiers, parameters, throws, body); type.constructors.push(ctr); return; } @@ -577,15 +577,15 @@ function fmc(modifiers, annotations, type_variables, type, tokens, imports, type addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`)) } if (tokens.current.value === '(') { - const { postnamearrdims, parameters, throws, body } = methodDeclaration(type, tokens, imports, typemap); + const { postnamearrdims, parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap); if (postnamearrdims > 0) { decl_type_ident.resolved = new ArrayType(decl_type_ident.resolved, postnamearrdims); } - const method = new SourceMethod(type, modifiers, annotations, decl_type_ident, name, parameters, throws, body); + const method = new SourceMethod(type, type_vars, modifiers, annotations, decl_type_ident, name, parameters, throws, body); type.methods.push(method); } else { if (name) { - if (type_variables.length) { + if (type_vars.length) { addproblem(tokens, ParseProblem.Error(tokens.current, `Fields cannot declare type variables`)); } const locals = var_ident_list(modifiers, decl_type_ident, name, tokens, new MethodDeclarations(), type, imports, typemap); @@ -641,7 +641,7 @@ function annotation(tokens, scope, imports, typemap) { addproblem(tokens, ParseProblem.Error(tokens.current, `Type identifier expected`)); return; } - let annotation_type = typeIdent(tokens, scope, imports, typemap, false); + let annotation_type = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers: true, type_vars:[]}); if (tokens.isValue('(')) { if (!tokens.isValue(')')) { expressionList(tokens, new MethodDeclarations(), scope, imports, typemap); @@ -729,17 +729,18 @@ function typeVariableList(owner, tokens, scope, imports, typemap) { /** + * @param {TypeVariable[]} type_vars * @param {SourceType} owner * @param {TokenList} tokens * @param {ResolvedImport[]} imports * @param {Map} typemap */ -function methodDeclaration(owner, tokens, imports, typemap) { +function methodDeclaration(type_vars, owner, tokens, imports, typemap) { tokens.expectValue('('); let parameters = [], throws = [], postnamearrdims = 0, body = null; if (!tokens.isValue(')')) { for(;;) { - const p = parameterDeclaration(owner, tokens, imports, typemap); + const p = parameterDeclaration(type_vars, owner, tokens, imports, typemap); parameters.push(p); if (tokens.isValue(',')) { continue; @@ -767,19 +768,20 @@ function methodDeclaration(owner, tokens, imports, typemap) { } /** + * @param {TypeVariable[]} type_vars * @param {SourceType} owner * @param {TokenList} tokens * @param {ResolvedImport[]} imports * @param {Map} typemap */ -function parameterDeclaration(owner, tokens, imports, typemap) { +function parameterDeclaration(type_vars, owner, tokens, imports, typemap) { const modifiers = []; while (tokens.current.kind === 'modifier') { modifiers.push(tokens.current); tokens.inc(); } checkLocalModifiers(tokens, modifiers); - let type_ident = typeIdent(tokens, owner, imports, typemap); + let type_ident = typeIdent(tokens, owner, imports, typemap, { no_array_qualifiers: false, type_vars }); const varargs = tokens.isValue('...'); let name_token = tokens.current; if (!tokens.isKind('ident')) { @@ -2353,7 +2355,7 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) { function newTerm(tokens, mdecls, scope, imports, typemap) { tokens.expectValue('new'); const type_start_token = tokens.idx; - const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, false); + const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); if (ctr_type instanceof AnyType) { const toks = tokens.tokens.slice(type_start_token, tokens.idx); addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`)); @@ -2843,7 +2845,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) { if (type) { matches.types = [type]; } else { - const { types, package_name } = resolveTypeOrPackage(ident, scope, imports, typemap); + const { types, package_name } = resolveTypeOrPackage(ident, [], scope, imports, typemap); matches.types = types; matches.package_name = package_name; } diff --git a/langserver/java/source-types2.js b/langserver/java/source-types2.js index cf3d1dc..cbef1bd 100644 --- a/langserver/java/source-types2.js +++ b/langserver/java/source-types2.js @@ -1,4 +1,4 @@ -const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter } = require('java-mti'); +const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable } = require('java-mti'); const { Token } = require('./tokenizer'); /** @@ -127,14 +127,16 @@ class SourceField extends Field { class SourceConstructor extends Constructor { /** * @param {SourceType} owner + * @param {TypeVariable[]} type_vars * @param {Token[]} modifiers * @param {SourceParameter[]} parameters * @param {JavaType[]} throws * @param {Token[]} body */ - constructor(owner, modifiers, parameters, throws, body) { + constructor(owner, type_vars, modifiers, parameters, throws, body) { super(owner, modifiers.map(m => m.value), ''); this.owner = owner; + this.typeVars = type_vars; this.modifierTokens = modifiers; this.sourceParameters = parameters; this.throws = throws; @@ -162,11 +164,16 @@ class SourceConstructor extends Constructor { get returnType() { return this.owner; } + + get typeVariables() { + return this.typeVars; + } } class SourceMethod extends Method { /** * @param {SourceType} owner + * @param {TypeVariable[]} type_vars * @param {Token[]} modifiers * @param {SourceAnnotation[]} annotations * @param {SourceTypeIdent} method_type_ident @@ -175,10 +182,11 @@ class SourceMethod extends Method { * @param {JavaType[]} throws * @param {Token[]} body */ - constructor(owner, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) { + constructor(owner, type_vars, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) { super(owner, name_token ? name_token.value : '', modifiers.map(m => m.value), ''); this.annotations = annotations; this.owner = owner; + this.typeVars = type_vars; this.modifierTokens = modifiers; this.returnTypeIdent = method_type_ident; this.nameToken = name_token; @@ -202,12 +210,13 @@ class SourceMethod extends Method { return this.sourceParameters; } - /** - * @returns {JavaType} - */ get returnType() { return this.returnTypeIdent.resolved; } + + get typeVariables() { + return this.typeVars; + } } class SourceInitialiser extends MethodBase { diff --git a/langserver/java/type-resolver.js b/langserver/java/type-resolver.js index 9f8b46e..12cc449 100644 --- a/langserver/java/type-resolver.js +++ b/langserver/java/type-resolver.js @@ -1,7 +1,7 @@ /** * @typedef {Map} TypeMap */ -const { JavaType, PrimitiveType, ArrayType, CEIType, MethodBase } = require('java-mti'); +const { JavaType, PrimitiveType, ArrayType, CEIType, MethodBase, TypeVariable } = require('java-mti'); const { ResolvedImport } = require('./import-resolver'); const ResolvedType = require('./parsetypes/resolved-type'); @@ -260,15 +260,21 @@ function resolveTypeIdents(types, fully_qualified_scope, resolved_imports, typem /** * * @param {string} ident + * @param {TypeVariable[]} type_variables * @param {CEIType|MethodBase} scope * @param {ResolvedImport[]} imports * @param {Map} typemap */ -function resolveTypeOrPackage(ident, scope, imports, typemap) { +function resolveTypeOrPackage(ident, type_variables, scope, imports, typemap) { const types = []; let package_name = ''; - if (scope instanceof MethodBase) { + const tv = type_variables.find(tv => tv.name === ident); + if (tv) { + types.push(tv.type); + } + + if (!types[0] && scope instanceof MethodBase) { // is it a type variable in the current scope const tv = scope.typeVariables.find(tv => tv.name === ident); if (tv) { @@ -276,7 +282,7 @@ function resolveTypeOrPackage(ident, scope, imports, typemap) { } } - if (scope) { + if (!types[0] && scope) { // is it an enclosed type of the currently scoped type or any outer type const scoped_type = scope instanceof CEIType ? scope : scope.owner; const scopes = scoped_type.shortSignature.split('$'); @@ -293,7 +299,7 @@ function resolveTypeOrPackage(ident, scope, imports, typemap) { } } - if (scope instanceof CEIType) { + if (!types[0] && scope instanceof CEIType) { // is it a type variable of the currently scoped type const tv = scope.typeVariables.find(tv => tv.name === ident); if (tv) { diff --git a/langserver/java/typeident.js b/langserver/java/typeident.js index 2a6624e..acee60f 100644 --- a/langserver/java/typeident.js +++ b/langserver/java/typeident.js @@ -1,4 +1,4 @@ -const { ArrayType, CEIType, JavaType, PrimitiveType, MethodBase, WildcardType } = require('java-mti'); +const { ArrayType, CEIType, JavaType, PrimitiveType, MethodBase, WildcardType, TypeVariable } = require('java-mti'); const { SourceTypeIdent, SourceMethod, SourceConstructor, SourceInitialiser } = require('./source-type'); const ResolvedImport = require('./parsetypes/resolved-import'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); @@ -32,11 +32,11 @@ function typeIdentList(tokens, scope, imports, typemap) { * @param {CEIType|MethodBase} scope * @param {ResolvedImport[]} imports * @param {Map} typemap - * @param {boolean} allow_array_qualifiers + * @param {{no_array_qualifiers:boolean, type_vars:TypeVariable[]}} [opts] */ -function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) { +function typeIdent(tokens, scope, imports, typemap, opts) { tokens.mark(); - const type = singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers); + const type = singleTypeIdent(tokens, scope, imports, typemap, opts); return new SourceTypeIdent(tokens.markEnd(), type); } @@ -45,15 +45,15 @@ function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = tru * @param {CEIType|MethodBase} scope * @param {ResolvedImport[]} imports * @param {Map} typemap - * @param {boolean} allow_array_qualifiers + * @param {{no_array_qualifiers:boolean, type_vars: TypeVariable[]}} [opts] */ -function singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) { +function singleTypeIdent(tokens, scope, imports, typemap, opts) { /** @type {JavaType[]} */ let types = [], package_name = ''; tokens.mark(); switch(tokens.current.kind) { case 'ident': - ({ types, package_name } = resolveTypeOrPackage(tokens.current.value, scope, imports, typemap)); + ({ types, package_name } = resolveTypeOrPackage(tokens.current.value, opts ? opts.type_vars : [], scope, imports, typemap)); break; case 'primitive-type': types.push(PrimitiveType.fromName(tokens.current.value)); @@ -84,7 +84,9 @@ function singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers types.push(anytype); } - if (allow_array_qualifiers && tokens.isValue('[')) { + // allow array qualifiers unless specifically disabled + const allow_array_qualifiers = !opts || !opts.no_array_qualifiers; + if ( allow_array_qualifiers && tokens.isValue('[')) { let arrdims = 0; for(;;) { arrdims++;