diff --git a/langserver/java/parser9.js b/langserver/java/parser9.js index 5558791..4709db5 100644 --- a/langserver/java/parser9.js +++ b/langserver/java/parser9.js @@ -373,10 +373,12 @@ class MCBlock extends DeclarationBlock { super(section, simplified); const sm = section.sourcemap(); this.paramBlock = section.blocks[sm.map[match[0].indexOf('R')]]; + this.typevarsBlock = section.blocks[sm.map[match[0].indexOf('T')]]; this.parsed = { + typevars: null, parameters: null, /** @type {TextBlock[]} */ - errors: null, + errors: [], } } @@ -384,30 +386,7 @@ class MCBlock extends DeclarationBlock { * @return {ParameterBlock[]} */ get parameters() { - if (!this.parsed.parameters) { - const param_block = this.paramBlock.blockArray(); - parseArrayTypes(param_block); - parseAnnotations(param_block); - parseTypeArgs(param_block); - const vars = group(param_block, 'var-decl', VarDeclBlock.parseRE, markers.varDecl, false, VarDeclBlock); - this.parsed.parameters = group(param_block, 'param', ParameterBlock.parseRE, markers.parameter, false, ParameterBlock); - // parameters must be a comma-separated list - const sm = param_block.sourcemap(); - if (sm.simplified.search(/^\((\s*F(\s*,\s*F)*)?\s*\)/) === 0) { - return this.parsed.parameters; - } - let invalid = sm.simplified.match(/^(\(\s*)(F?)(?:\s*,\s*F)*\s*/); - if (!invalid) { - // should never happen, but ignore - return this.parsed.parameters; - } - const token_idx = invalid[2] - ? sm.map[invalid[0].length] // there's a problem with a subsequent declaration - : sm.map[invalid[1].length] // there's a problem with the first declaration - const token = param_block.blocks[token_idx]; - if (!token) return this.parsed.parameters; - this.parsed.errors = [token]; - } + this._ensureParsed(); return this.parsed.parameters; } @@ -433,9 +412,43 @@ class MCBlock extends DeclarationBlock { } get parseErrors() { - this.parameters; + this._ensureParsed(); return this.parsed.errors; } + + get typeVariables() { + this._ensureParsed(); + return this.parsed.typevars; + } + + _ensureParsed() { + if (this.parsed.parameters) { + return; + } + const param_block = this.paramBlock.blockArray(); + parseArrayTypes(param_block); + parseAnnotations(param_block); + parseTypeArgs(param_block); + const vars = group(param_block, 'var-decl', VarDeclBlock.parseRE, markers.varDecl, false, VarDeclBlock); + this.parsed.parameters = group(param_block, 'param', ParameterBlock.parseRE, markers.parameter, false, ParameterBlock); + // parameters must be a comma-separated list + const sm = param_block.sourcemap(); + if (sm.simplified.search(/^\((\s*F(\s*,\s*F)*)?\s*\)/) !== 0) { + let invalid = sm.simplified.match(/^(\(\s*)(F?)(?:\s*,\s*F)*\s*/); + if (invalid) { + const token_idx = invalid[2] + ? sm.map[invalid[0].length] // there's a problem with a subsequent declaration + : sm.map[invalid[1].length] // there's a problem with the first declaration + const token = param_block.blocks[token_idx]; + if (token) { + this.parsed.errors.push(token); + } + } + } + + // parse type arguments + this.parsed.typevars = this.typevarsBlock ? parseTypeVariables(this.typevarsBlock.blockArray()) : []; + } } class MethodBlock extends MCBlock { @@ -631,29 +644,9 @@ class TypeDeclBlock extends DeclarationBlock { if (this.parsed.fields) { return; } - this.parsed.typevars = []; - if (this.typevars_token) { - // split the token into a list of typevars - // - each type var must be a simple ident (W), a bounded var (I) - // or anonymous (?) - this.parsed.typevars = this.typevars_token.blockArray() - .blocks.reduce((arr,b) => { - if (/^[WI?]/.test(b.simplified)) { - arr.push({ - decl: b, - get name_token() { - return this.decl instanceof BoundedTypeVar - ? this.decl.range.blocks[0] - : this.decl - }, - get name() { - return this.name_token.source; - }, - }) - } - return arr; - }, []); - } + this.parsed.typevars = this.typevars_token + ? parseTypeVariables(this.typevars_token.blockArray()) + : []; const body = this.body().blockArray(); parseArrayTypes(body); parseTypeArgs(body); @@ -834,6 +827,33 @@ function parseArrayTypes(sourceblocks) { group(sourceblocks, 'array-type', /\[ *\](( *\[ *\])*)/g, markers.arrayQualifier); } +/** + * @param {TextBlockArray} sourceblocks + * @returns {{decl: TextBlock|BoundedTypeVar, name_token: TextBlockArray, name: string}[]} + */ +function parseTypeVariables(sourceblocks) { + // split the token into a list of typevars + // - each type var must be a simple ident (W), a bounded var (I) + // or a wildcard (?) + return sourceblocks.blocks.reduce((arr,b) => { + if (/^[WI?]/.test(b.simplified)) { + arr.push({ + decl: b, + get name_token() { + return this.decl instanceof BoundedTypeVar + ? this.decl.range.blocks[0] + : this.decl + }, + get name() { + return this.name_token.source; + }, + }) + } + return arr; + }, []); + +} + function parseTypeArgs(sourceblocks) { // sort out type parameters + type arguments // re = /< *[PWD?]( *T)?( *A)?( *, *[PWD]( *T)?( *A)?)* *>/g; diff --git a/langserver/java/source-type.js b/langserver/java/source-type.js index c289d32..0e71d76 100644 --- a/langserver/java/source-type.js +++ b/langserver/java/source-type.js @@ -209,6 +209,12 @@ class SourceMethod extends Method { this._decl = decl; this._parameters = decl.parameters.map((p,i) => new SourceParameter(p)); this._returnType = new ResolvableType(decl); + this._typevars = decl.typeVariables.map(tv => { + const typevar = new TypeVariable(owner, tv.name); + // automatically add the Object bound + typevar.bounds.push(new TypeVariable.Bound(owner, 'Ljava/lang/Object;', false)); + return typevar; + }); } /** @@ -221,6 +227,10 @@ class SourceMethod extends Method { get returnType() { return this._returnType.resolved; } + + get typeVariables() { + return this._typevars; + } } class SourceParameter extends Parameter {