diff --git a/langserver/java/expression-resolver.js b/langserver/java/expression-resolver.js index 349a135..50cd6a0 100644 --- a/langserver/java/expression-resolver.js +++ b/langserver/java/expression-resolver.js @@ -304,8 +304,10 @@ function isTypeAssignable(dest_type, value_type) { } } } + } else if (dest_type instanceof TypeVariableType) { + is_assignable = !(value_type instanceof PrimitiveType || value_type instanceof NullType); } - return is_assignable; +return is_assignable; } /** diff --git a/langserver/java/source-types.js b/langserver/java/source-types.js index de4e81f..cf09d28 100644 --- a/langserver/java/source-types.js +++ b/langserver/java/source-types.js @@ -136,7 +136,6 @@ class SourceType extends CEIType { } /** - * * @param {JavaType[]} types * @returns {CEIType} */ @@ -178,6 +177,7 @@ class SpecialisedSourceType extends CEIType { */ constructor(source_type, typeKind, raw_short_signature, types) { super(raw_short_signature, typeKind, source_type.modifiers, source_type.docs); + this.source_type = source_type; this.typemap = source_type.typemap; /** @type {TypeArgument[]} */ // @ts-ignore @@ -262,6 +262,25 @@ class SpecialisedSourceType extends CEIType { }; }); } + + /** + * @param {JavaType[]} types + * @returns {CEIType} + */ + specialise(types) { + const short_sig = `${this._rawShortSignature}<${types.map(t => t.typeSignature).join('')}>`; + if (this.typemap.has(short_sig)) { + // @ts-ignore + return this.typemap.get(short_sig); + } + /** @type {'class'|'enum'|'interface'|'@interface'} */ + // @ts-ignore + const typeKind = this.typeKind; + const specialised_type = new SpecialisedSourceType(this.source_type, typeKind, this._rawShortSignature, types); + this.typemap.set(short_sig, specialised_type); + return specialised_type; + } + } class SourceEnumValue extends Field { diff --git a/langserver/java/validation/non-implemented-interfaces.js b/langserver/java/validation/non-implemented-interfaces.js index e0fff29..be905d9 100644 --- a/langserver/java/validation/non-implemented-interfaces.js +++ b/langserver/java/validation/non-implemented-interfaces.js @@ -1,11 +1,27 @@ const ParseProblem = require('../parsetypes/parse-problem'); const { SourceType } = require('../source-types'); -const {CEIType} = require('java-mti'); +const { CEIType, Method} = require('java-mti'); +const {isTypeAssignable} = require('../expression-resolver'); function nonAbstractLabel(label) { return label.replace(/\babstract /g, ''); } +/** + * + * @param {Method} impl method implementation + * @param {Method} method interface method + */ +function isMethodCompatible(impl, method) { + const impl_params = impl.parameters; + const method_params = method.parameters; + if (impl_params.length !== method_params.length) { + return false; + } + return impl_params.every((p,idx) => isTypeAssignable(method_params[idx].type, p.type)) + && isTypeAssignable(method.returnType, impl.returnType); +} + /** * @param {SourceType} source_type * @param {*} probs @@ -44,7 +60,10 @@ function checkImplementedInterfaces(source_type, probs) { } const namedsig = `${m.name}${m.methodSignature}` if (implemented.indexOf(namedsig) < 0) { - missing_methods.push(nonAbstractLabel(m.label)); + // perform a more detailed search for a compatible match + if (!source_type.methods.find(source_method => source_method.name === m.name && isMethodCompatible(source_method, m))) { + missing_methods.push(nonAbstractLabel(m.label)); + } } }) if (missing_methods.length) {