From 198317a5c27dbad06a0b22ab1dd35685bc118e5c Mon Sep 17 00:00:00 2001 From: Dave Holoway Date: Tue, 16 Jun 2020 20:22:19 +0100 Subject: [PATCH] add support for static member imports --- langserver/java/body-parser3.js | 15 +++++++- langserver/java/import-resolver.js | 38 +++++++++++++++---- langserver/java/parsetypes/resolved-import.js | 12 +++++- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 22004ec..12bae99 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -4,7 +4,7 @@ * * Each token also contains detailed state information used for completion suggestions. */ -const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, NullType, TypeVariable } = require('java-mti'); +const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, NullType, TypeVariable, Field, Method } = require('java-mti'); const { SourceType, SourceTypeIdent, SourceField, SourceMethod, SourceConstructor, SourceInitialiser, SourceParameter, SourceAnnotation, SourceUnit, SourcePackage, SourceImport } = require('./source-types2'); const ResolvedImport = require('./parsetypes/resolved-import'); @@ -2063,6 +2063,19 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) { }); } + // check static imports + imports.forEach(imp => { + imp.members.forEach(member => { + if (member.name === ident) { + if (member instanceof Field) { + matches.variables.push(member); + } else if (member instanceof Method) { + matches.methods.push(member); + } + } + }) + }); + const type = mdecls.types.find(t => t.simpleTypeName === ident); if (type) { matches.types = [type]; diff --git a/langserver/java/import-resolver.js b/langserver/java/import-resolver.js index 9cdecdc..8352e95 100644 --- a/langserver/java/import-resolver.js +++ b/langserver/java/import-resolver.js @@ -33,7 +33,7 @@ function resolveImportTypes(typenames, import_decl) { /** * Resolve a single parsed import * - * @param {Map} typemap + * @param {Map} typemap * @param {string} dotted_name * @param {boolean} is_static * @param {boolean} on_demand @@ -42,9 +42,31 @@ function resolveImportTypes(typenames, import_decl) { function resolveSingleImport(typemap, dotted_name, is_static, on_demand, import_kind) { // construct the list of typenames const typenames = [...typemap.keys()].join('\n'); - const matches = fetchImportedTypes(typenames, dotted_name, on_demand); - if (matches) { - return new ResolvedImport(null, matches, typemap, import_kind); + + if (is_static) { + if (on_demand) { + // import all static members - the dotted name must be an exact type + const matches = fetchImportedTypes(typenames, dotted_name, false); + if (matches) { + return new ResolvedImport(null, matches, '*', typemap, import_kind); + } + } else if (dotted_name.includes('.')) { + // the final ident is the static member - the rest is the exact type + const split_name = dotted_name.match(/(.+)\.([^.]+)$/); + const matches = fetchImportedTypes(typenames, split_name[1], false); + if (matches) { + const i = new ResolvedImport(null, matches, split_name[2], typemap, import_kind); + // if there's no matching member, treat it as an invalid import + if (i.members.length > 0) { + return i; + } + } + } + } else { + const matches = fetchImportedTypes(typenames, dotted_name, on_demand); + if (matches) { + return new ResolvedImport(null, matches, null, typemap, import_kind); + } } return null; } @@ -57,7 +79,7 @@ function resolveSingleImport(typemap, dotted_name, is_static, on_demand, import_ * - followed by import declarations (in order of declaration), * - followed by implicit packages * - * @param {Map} androidLibrary + * @param {Map} androidLibrary * @param {import('./source-type').SourceType[]} sourceTypes * @param {ImportBlock[]} imports list of declared imports in the module * @param {string} package_name package name of the module @@ -88,14 +110,14 @@ function resolveImports(androidLibrary, sourceTypes, imports, package_name, impl if (package_name) { const matches = fetchImportedTypes(typenames, package_name, true); if (matches) - resolved.push(new ResolvedImport(null, matches, typemap, 'owner-package')); + resolved.push(new ResolvedImport(null, matches, null, typemap, 'owner-package')); } // import types from each import declaration imports.forEach(import_decl => { const matches = resolveImportTypes(typenames, import_decl); if (matches) { - resolved.push(new ResolvedImport(import_decl, matches, typemap, 'import')); + resolved.push(new ResolvedImport(import_decl, matches, null, typemap, 'import')); } else { // if we cannot match the import to any types, add it to the unresolved list so // we can flag it as a warning later. @@ -109,7 +131,7 @@ function resolveImports(androidLibrary, sourceTypes, imports, package_name, impl implicitPackages.forEach(package_name => { const matches = fetchImportedTypes(typenames, package_name, true); if (matches) - resolved.push(new ResolvedImport(null, matches, typemap, 'implicit-import')); + resolved.push(new ResolvedImport(null, matches, null, typemap, 'implicit-import')); }) /** diff --git a/langserver/java/parsetypes/resolved-import.js b/langserver/java/parsetypes/resolved-import.js index 9ee8052..fd59763 100644 --- a/langserver/java/parsetypes/resolved-import.js +++ b/langserver/java/parsetypes/resolved-import.js @@ -14,10 +14,11 @@ const { ImportBlock } = require('../parser9'); /** * @param {ImportBlock} import_decl * @param {RegExpMatchArray} matches + * @param {string} static_ident * @param {Map} typemap * @param {'owner-package'|'import'|'implicit-import'} import_kind */ - constructor(import_decl, matches, typemap, import_kind) { + constructor(import_decl, matches, static_ident, typemap, import_kind) { /** * The associated import declaration. * - this value is null for owner-package and implicit-imports @@ -34,6 +35,15 @@ const { ImportBlock } = require('../parser9'); */ this.types = new Map(matches.map(name => [name, typemap.get(name)])); + this.members = []; + if (static_ident) { + const type = typemap.get(matches[0]); + if (type) { + type.fields.forEach(f => f.modifiers.includes('static') && (static_ident === '*' || static_ident === f.name) && this.members.push(f)); + type.methods.forEach(m => m.modifiers.includes('static') && (static_ident === '*' || static_ident === m.name) && this.members.push(m)); + } + } + /** * What kind of import this is: * - `"owner-package"`: types that are implicitly imported from the same package as the declared module