diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 814e7e1..a6efffc 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -16,6 +16,7 @@ const { TokenList } = require("./TokenList"); const { AnyMethod, AnyType, AnyValue } = require("./anys"); const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types"); const { resolveImports, resolveSingleImport } = require('../java/import-resolver'); +const { getTypeInheritanceList } = require('./expression-resolver'); const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression"); const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression"); @@ -30,6 +31,7 @@ const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression const { NewArray, NewObject } = require("./expressiontypes/NewExpression"); const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression"); const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression"); +const { Variable } = require("./expressiontypes/Variable"); const { BooleanLiteral } = require('./expressiontypes/literals/Boolean'); const { CharacterLiteral } = require('./expressiontypes/literals/Character'); @@ -843,15 +845,15 @@ function enumValueList(type, tokens, imports, typemap) { * @param {Map} typemap */ function statementBlock(tokens, mdecls, method, imports, typemap) { - const b = new Block(); + const block = new Block(tokens.current); tokens.expectValue('{'); mdecls.pushScope(); while (!tokens.isValue('}')) { const s = statement(tokens, mdecls, method, imports, typemap); - b.statements.push(s); + block.statements.push(s); } mdecls.popScope(); - return b; + return block; } /** @@ -1538,15 +1540,16 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) { // the result of a bracketed expression is always a value, never a variable // - this prevents things like: (a) = 5; return new ResolvedIdent(`(${matches.source})`, [new BracketedExpression(matches)]); - case tokens.isValue('{') && 'symbol': + case tokens.current.value === '{' && 'symbol': // array initer - let elements = []; + let elements = [], open = tokens.current; + tokens.expectValue('{'); if (!tokens.isValue('}')) { elements = expressionList(tokens, mdecls, scope, imports, typemap, { isArrayLiteral:true }); tokens.expectValue('}'); } const ident = `{${elements.map(e => e.source).join(',')}}`; - return new ResolvedIdent(ident, [new ArrayValueExpression(elements)]); + return new ResolvedIdent(ident, [new ArrayValueExpression(elements, open)]); default: addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected')); return new ResolvedIdent(''); @@ -1563,9 +1566,10 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) { * @param {Map} typemap */ function newTerm(tokens, mdecls, scope, imports, typemap) { + const new_token = tokens.current; tokens.expectValue('new'); - const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); - let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]); + const ctr_type = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); + let match = new ResolvedIdent(`new ${ctr_type.resolved.simpleTypeName}`, [], [], [ctr_type.resolved]); let ctr_args = [], type_body = null; switch(tokens.current.value) { case '[': @@ -1575,7 +1579,7 @@ function newTerm(tokens, mdecls, scope, imports, typemap) { // array init rootTerm(tokens, mdecls, scope, imports, typemap); } - return new ResolvedIdent(match.source, [new NewArray(ctr_type, match)]); + return new ResolvedIdent(match.source, [new NewArray(new_token, ctr_type, match)]); case '(': tokens.inc(); if (!tokens.isValue(')')) { @@ -1592,7 +1596,7 @@ function newTerm(tokens, mdecls, scope, imports, typemap) { addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); break; } - return new ResolvedIdent(match.source, [new NewObject(ctr_type, ctr_args, type_body)]); + return new ResolvedIdent(match.source, [new NewObject(new_token, ctr_type, ctr_args, type_body)]); } /** @@ -1630,28 +1634,6 @@ function arrayElementOrConstructor(tokens, open_array, instance, index) { return new ResolvedIdent(ident, [new ArrayIndexExpression(instance, index)]); } -/** - * @param {CEIType} type - */ -function getTypeInheritanceList(type) { - const types = { - /** @type {JavaType[]} */ - list: [type], - /** @type {Set} */ - done: new Set(), - }; - for (let type; type = types.list.shift(); ) { - if (types.done.has(type)) { - continue; - } - types.done.add(type); - if (type instanceof CEIType) - types.list.push(...type.supers); - } - return Array.from(types.done); -} - - /** * @param {ResolvedIdent} matches * @param {TokenList} tokens @@ -1807,7 +1789,7 @@ function arrayTypeExpression(matches) { */ function resolveIdentifier(tokens, mdecls, scope, imports, typemap) { const ident = tokens.current.value; - const matches = findIdentifier(ident, mdecls, scope, imports, typemap); + const matches = findIdentifier(tokens.current, mdecls, scope, imports, typemap); checkIdentifierFound(tokens, ident, matches); return matches; } @@ -1827,20 +1809,22 @@ function checkIdentifierFound(tokens, ident, matches) { } /** - * @param {string} ident + * @param {Token} token * @param {MethodDeclarations} mdecls * @param {Scope} scope * @param {ResolvedImport[]} imports * @param {Map} typemap */ -function findIdentifier(ident, mdecls, scope, imports, typemap) { +function findIdentifier(token, mdecls, scope, imports, typemap) { + const ident = token.value; const matches = new ResolvedIdent(ident); + matches.tokens = [token]; // is it a local or parameter - note that locals must be ordered innermost-scope-first const local = mdecls.locals.find(local => local.name === ident); let param = scope && !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident); if (local || param) { - matches.variables = [local || param]; + matches.variables = [new Variable(token, local || param)]; } else if (scope) { // is it a field, method or enum value in the current type (or any of the outer types or superclasses) const scoped_type = scope instanceof SourceType ? scope : scope.owner; @@ -1857,12 +1841,12 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) { if (!matches.variables[0]) { const field = type.fields.find(f => f.name === ident); if (field) { - matches.variables = [field]; + matches.variables = [new Variable(token, field)]; return; } const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident); if (enumValue) { - matches.variables = [enumValue]; + matches.variables = [new Variable(token, enumValue)]; return; } } @@ -1883,7 +1867,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) { imp.members.forEach(member => { if (member.name === ident) { if (member instanceof Field) { - matches.variables.push(member); + matches.variables.push(new Variable(token, member)); } else if (member instanceof Method) { matches.methods.push(member); } diff --git a/langserver/java/body-types.js b/langserver/java/body-types.js index c76a708..c704195 100644 --- a/langserver/java/body-types.js +++ b/langserver/java/body-types.js @@ -7,7 +7,7 @@ const { Token } = require('./tokenizer'); class ResolvedIdent { /** * @param {string} ident - * @param {(Local|Parameter|Field|Expression)[]} variables + * @param {Expression[]} variables * @param {Method[]} methods * @param {JavaType[]} types * @param {string} package_name diff --git a/langserver/java/expression-resolver.js b/langserver/java/expression-resolver.js new file mode 100644 index 0000000..64e746e --- /dev/null +++ b/langserver/java/expression-resolver.js @@ -0,0 +1,175 @@ +/** + * @typedef {import('./tokenizer').Token} Token + */ +const ParseProblem = require('./parsetypes/parse-problem'); +const { TypeVariable, JavaType, PrimitiveType, NullType, ArrayType, CEIType, WildcardType, TypeVariableType, InferredTypeArgument } = require('java-mti'); +const { AnyType } = require('./anys'); +const { Local } = require('./body-types'); +const { Expression } = require('./expressiontypes/Expression'); +const { Variable } = require('./expressiontypes/Variable'); + +/** + * @param {import('./body-types').ResolvedIdent} e + * @param {JavaType} assign_type + * @param {Map} typemap + * @param {ParseProblem[]} problems + */ +function checkAssignment(e, assign_type, typemap, problems) { + const value = e.variables[0]; + if (value instanceof Variable) { + checkTypeAssignable(assign_type, value.type, () => value.name_token, problems); + return; + } + if (value instanceof Expression) { + const expression_result_type = null;//value.resolveType(typemap); + checkTypeAssignable(assign_type, expression_result_type, value.tokens, problems); + return; + } +} + +/** + * + * @param {JavaType} variable_type + * @param {JavaType} value_type + * @param {() => Token|Token[]} tokens + */ +function checkTypeAssignable(variable_type, value_type, tokens, problems) { + if (!isTypeAssignable(variable_type, value_type)) { + const t = tokens(); + problems.push(ParseProblem.Error(t, `Incompatible types: Expression of type '${value_type.fullyDottedTypeName}' cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`)); + } +} + +/** + * Set of regexes to map source primitives to their destination types. + * eg, long (J) is type-assignable to long, float and double (and their boxed counterparts) + * Note that void (V) is never type-assignable to anything + */ +const valid_primitive_types = { + // conversions from a primitive to a value + from: { + B: /^[BSIJFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Float|Double);$/, + S: /^[SIJFD]$|^Ljava\/lang\/(Short|Integer|Long|Float|Double);$/, + I: /^[IJFD]$|^Ljava\/lang\/(Integer|Long|Float|Double);$/, + J: /^[JFD]$|^Ljava\/lang\/(Long|Float|Double);$/, + F: /^[FD]$|^Ljava\/lang\/(Float|Double);$/, + D: /^D$|^Ljava\/lang\/(Double);$/, + C: /^[CIJFD]$|^Ljava\/lang\/(Character|Integer|Long|Float|Double);$/, + Z: /^Z$|^Ljava\/lang\/(Boolean);$/, + V: /$^/, // V.test() always returns false + }, + // conversions to a primitive from a value + to: { + B: /^[B]$|^Ljava\/lang\/(Byte);$/, + S: /^[BS]$|^Ljava\/lang\/(Byte|Short);$/, + I: /^[BSIC]$|^Ljava\/lang\/(Byte|Short|Integer|Character);$/, + J: /^[BSIJC]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character);$/, + F: /^[BSIJCF]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float);$/, + D: /^[BSIJCFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float|Double);$/, + C: /^C$|^Ljava\/lang\/(Character);$/, + Z: /^Z$|^Ljava\/lang\/(Boolean);$/, + V: /$^/, // V.test() always returns false + } +} + +/** + * Returns true if a value of value_type is assignable to a variable of dest_type + * @param {JavaType} dest_type + * @param {JavaType} value_type + */ +function isTypeAssignable(dest_type, value_type) { + let is_assignable = false; + if (dest_type.typeSignature === value_type.typeSignature) { + // exact signature match + is_assignable = true; + } else if (dest_type instanceof AnyType || value_type instanceof AnyType) { + // everything is assignable to or from AnyType + is_assignable = true; + } else if (dest_type.rawTypeSignature === 'Ljava/lang/Object;') { + // everything is assignable to Object + is_assignable = true; + } else if (value_type instanceof PrimitiveType) { + // primitive values can only be assigned to wider primitives or their class equivilents + is_assignable = valid_primitive_types.from[value_type.typeSignature].test(dest_type.typeSignature); + } else if (dest_type instanceof PrimitiveType) { + // primitive variables can only be assigned from narrower primitives or their class equivilents + is_assignable = valid_primitive_types.to[dest_type.typeSignature].test(value_type.typeSignature); + } else if (value_type instanceof NullType) { + // null is assignable to any non-primitive + is_assignable = !(dest_type instanceof PrimitiveType); + } else if (value_type instanceof ArrayType) { + // arrays are assignable to other arrays with the same dimensionality and type-assignable bases + is_assignable = dest_type instanceof ArrayType + && dest_type.arrdims === value_type.arrdims + && isTypeAssignable(dest_type.base, value_type.base); + } else if (value_type instanceof CEIType && dest_type instanceof CEIType) { + // class/interfaces types are assignable to any class/interface types in their inheritence tree + const valid_types = getTypeInheritanceList(value_type); + is_assignable = valid_types.includes(dest_type); + if (!is_assignable) { + // generic types are also assignable to their raw counterparts + const valid_raw_types = valid_types.map(t => t.getRawType()); + is_assignable = valid_raw_types.includes(dest_type); + if (!is_assignable) { + // generic types are also assignable to compatible wildcard type bounds + const raw_type = valid_raw_types.find(rt => rt.rawTypeSignature === dest_type.rawTypeSignature); + if (raw_type instanceof CEIType && raw_type.typeVariables.length === value_type.typeVariables.length) { + is_assignable = dest_type.typeVariables.every((dest_tv, idx) => isTypeArgumentCompatible(dest_tv, value_type.typeVariables[idx].type)); + } + } + } + } + return is_assignable; +} + +/** + * @param {TypeVariable} dest_typevar + * @param {JavaType} value_typevar_type + */ +function isTypeArgumentCompatible(dest_typevar, value_typevar_type) { + if (dest_typevar.type instanceof WildcardType) { + if (!dest_typevar.type.bound) { + // unbounded wildcard types are compatible with everything + return true; + } + if (dest_typevar.type.bound.type === value_typevar_type) { + return true; + } + switch (dest_typevar.type.bound.kind) { + case 'extends': + return isTypeAssignable(dest_typevar.type.bound.type, value_typevar_type); + case 'super':; + return isTypeAssignable(value_typevar_type, dest_typevar.type.bound.type); + } + return false; + } + if (value_typevar_type instanceof TypeVariableType) { + // inferred type arguments of the form `x = List<>` are compatible with every destination type variable + return value_typevar_type.typeVariable instanceof InferredTypeArgument; + } + return dest_typevar.type === value_typevar_type; +} + +/** + * @param {CEIType} type + */ +function getTypeInheritanceList(type) { + const types = { + /** @type {JavaType[]} */ + list: [type], + /** @type {Set} */ + done: new Set(), + }; + for (let type; type = types.list.shift(); ) { + if (types.done.has(type)) { + continue; + } + types.done.add(type); + if (type instanceof CEIType) + types.list.push(...type.supers); + } + return Array.from(types.done); +} + +exports.checkAssignment = checkAssignment; +exports.getTypeInheritanceList = getTypeInheritanceList; diff --git a/langserver/java/expressiontypes/ArrayIndexExpression.js b/langserver/java/expressiontypes/ArrayIndexExpression.js index 8d8a158..8002cd4 100644 --- a/langserver/java/expressiontypes/ArrayIndexExpression.js +++ b/langserver/java/expressiontypes/ArrayIndexExpression.js @@ -13,6 +13,10 @@ class ArrayIndexExpression extends Expression { this.instance = instance; this.index = index; } + + tokens() { + return [...this.instance.tokens, ...this.index.tokens]; + } } exports.ArrayIndexExpression = ArrayIndexExpression; diff --git a/langserver/java/expressiontypes/ArrayValueExpression.js b/langserver/java/expressiontypes/ArrayValueExpression.js index 9c403bd..af1f5de 100644 --- a/langserver/java/expressiontypes/ArrayValueExpression.js +++ b/langserver/java/expressiontypes/ArrayValueExpression.js @@ -1,15 +1,22 @@ /** * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent + * @typedef {import('../tokenizer').Token} Token */ const { Expression } = require("./Expression"); class ArrayValueExpression extends Expression { /** * @param {ResolvedIdent[]} elements + * @param {Token} open */ - constructor(elements) { + constructor(elements, open) { super(); this.elements = elements; + this.open = open; + } + + tokens() { + return this.open; } } diff --git a/langserver/java/expressiontypes/BinaryOpExpression.js b/langserver/java/expressiontypes/BinaryOpExpression.js index 1624821..d263f42 100644 --- a/langserver/java/expressiontypes/BinaryOpExpression.js +++ b/langserver/java/expressiontypes/BinaryOpExpression.js @@ -16,6 +16,10 @@ class BinaryOpExpression extends Expression { this.op = op; this.rhs = rhs; } + + tokens() { + return [...this.lhs.tokens, this.op, ...this.rhs.tokens]; + } } exports.BinaryOpExpression = BinaryOpExpression; diff --git a/langserver/java/expressiontypes/BracketedExpression.js b/langserver/java/expressiontypes/BracketedExpression.js index f6fd01d..e90caeb 100644 --- a/langserver/java/expressiontypes/BracketedExpression.js +++ b/langserver/java/expressiontypes/BracketedExpression.js @@ -11,6 +11,10 @@ class BracketedExpression extends Expression { super(); this.expression = expression; } + + tokens() { + return this.expression.tokens; + } } exports.BracketedExpression = BracketedExpression; diff --git a/langserver/java/expressiontypes/CastExpression.js b/langserver/java/expressiontypes/CastExpression.js index f0441f5..9431f0a 100644 --- a/langserver/java/expressiontypes/CastExpression.js +++ b/langserver/java/expressiontypes/CastExpression.js @@ -13,6 +13,10 @@ class CastExpression extends Expression { this.castType = castType; this.expression = expression; } + + tokens() { + return [...this.castType.tokens, ...this.expression.tokens]; + } } exports.CastExpression = CastExpression; diff --git a/langserver/java/expressiontypes/ClassMemberExpression.js b/langserver/java/expressiontypes/ClassMemberExpression.js index 15cefb2..5c0edb3 100644 --- a/langserver/java/expressiontypes/ClassMemberExpression.js +++ b/langserver/java/expressiontypes/ClassMemberExpression.js @@ -14,5 +14,9 @@ class ClassMemberExpression extends Expression { this.instance = instance; this.classToken = class_token; } + + tokens() { + return this.classToken; + } } exports.ClassMemberExpression = ClassMemberExpression; diff --git a/langserver/java/expressiontypes/Expression.js b/langserver/java/expressiontypes/Expression.js index 1bb8bb7..e19eb48 100644 --- a/langserver/java/expressiontypes/Expression.js +++ b/langserver/java/expressiontypes/Expression.js @@ -1,5 +1,12 @@ +/** + * @typedef {import('../tokenizer').Token} Token + */ class Expression { + /** @returns {Token|Token[]} */ + tokens() { + throw new Error('Expression.tokens'); + } } exports.Expression = Expression; diff --git a/langserver/java/expressiontypes/IncDecExpression.js b/langserver/java/expressiontypes/IncDecExpression.js index 67ea5cb..0965847 100644 --- a/langserver/java/expressiontypes/IncDecExpression.js +++ b/langserver/java/expressiontypes/IncDecExpression.js @@ -16,6 +16,10 @@ class IncDecExpression extends Expression { this.operator = operator; this.which = which; } + + tokens() { + return this.operator; + } } exports.IncDecExpression = IncDecExpression; diff --git a/langserver/java/expressiontypes/LambdaExpression.js b/langserver/java/expressiontypes/LambdaExpression.js index 07005c7..d60ceb8 100644 --- a/langserver/java/expressiontypes/LambdaExpression.js +++ b/langserver/java/expressiontypes/LambdaExpression.js @@ -1,19 +1,26 @@ /** * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent - * @typedef {import('../statementtypes/Block').Block} Block */ const { Expression } = require("./Expression"); +const { Block } = require('../statementtypes/Block'); class LambdaExpression extends Expression { /** * * @param {*[]} params - * @param {Expression|Block} body + * @param {ResolvedIdent|Block} body */ constructor(params, body) { super(); this.params = params; this.body = body; } + + tokens() { + if (this.body instanceof Block) { + return this.body.open; + } + return this.body.tokens; + } } exports.LambdaExpression = LambdaExpression; diff --git a/langserver/java/expressiontypes/MemberExpression.js b/langserver/java/expressiontypes/MemberExpression.js index ae85d2f..b1b2a2b 100644 --- a/langserver/java/expressiontypes/MemberExpression.js +++ b/langserver/java/expressiontypes/MemberExpression.js @@ -15,6 +15,10 @@ class MemberExpression extends Expression { // member will be null for incomplete expressions this.member = member; } + + tokens() { + return this.member; + } } exports.MemberExpression = MemberExpression; diff --git a/langserver/java/expressiontypes/MethodCallExpression.js b/langserver/java/expressiontypes/MethodCallExpression.js index ccc8256..24ee887 100644 --- a/langserver/java/expressiontypes/MethodCallExpression.js +++ b/langserver/java/expressiontypes/MethodCallExpression.js @@ -13,6 +13,10 @@ class MethodCallExpression extends Expression { this.instance = instance; this.args = args; } + + tokens() { + return this.instance.tokens; + } } exports.MethodCallExpression = MethodCallExpression; diff --git a/langserver/java/expressiontypes/NewExpression.js b/langserver/java/expressiontypes/NewExpression.js index 7b1b9e7..fc31cee 100644 --- a/langserver/java/expressiontypes/NewExpression.js +++ b/langserver/java/expressiontypes/NewExpression.js @@ -1,34 +1,47 @@ /** * @typedef {import('../tokenizer').Token} Token * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent + * @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent * @typedef {import('java-mti').JavaType} JavaType */ const { Expression } = require("./Expression"); class NewArray extends Expression { /** - * @param {JavaType} element_type + * @param {Token} new_token + * @param {SourceTypeIdent} element_type * @param {ResolvedIdent} dimensions */ - constructor(element_type, dimensions) { + constructor(new_token, element_type, dimensions) { super(); + this.new_token = new_token; this.element_type = element_type; this.dimensions = dimensions; } + + tokens() { + return [this.new_token, ...this.element_type.tokens, ...this.dimensions.tokens]; + } } class NewObject extends Expression { /** - * @param {JavaType} object_type + * @param {Token} new_token + * @param {SourceTypeIdent} object_type * @param {ResolvedIdent[]} ctr_args * @param {Token[]} type_body */ - constructor(object_type, ctr_args, type_body) { + constructor(new_token, object_type, ctr_args, type_body) { super(); - this.element_type = object_type; + this.new_token = new_token; + this.object_type = object_type; this.ctr_args = ctr_args; this.type_body = type_body; } + + tokens() { + return [this.new_token, ...this.object_type.tokens]; + } } exports.NewArray = NewArray; diff --git a/langserver/java/expressiontypes/TernaryOpExpression.js b/langserver/java/expressiontypes/TernaryOpExpression.js index 6d12a75..69795c1 100644 --- a/langserver/java/expressiontypes/TernaryOpExpression.js +++ b/langserver/java/expressiontypes/TernaryOpExpression.js @@ -15,6 +15,10 @@ class TernaryOpExpression extends Expression { this.truthExpression = truthExpression; this.falseExpression = falseExpression; } + + tokens() { + return [...this.test.tokens, ...this.truthExpression.tokens, ...this.falseExpression.tokens]; + } } exports.TernaryOpExpression = TernaryOpExpression; diff --git a/langserver/java/expressiontypes/ThisMemberExpression.js b/langserver/java/expressiontypes/ThisMemberExpression.js index 3972991..36cf204 100644 --- a/langserver/java/expressiontypes/ThisMemberExpression.js +++ b/langserver/java/expressiontypes/ThisMemberExpression.js @@ -14,6 +14,10 @@ class ThisMemberExpression extends Expression { this.instance = instance; this.thisToken = this_token; } + + tokens() { + return this.thisToken; + } } exports.ThisMemberExpression = ThisMemberExpression; diff --git a/langserver/java/expressiontypes/Variable.js b/langserver/java/expressiontypes/Variable.js new file mode 100644 index 0000000..2a9a1f8 --- /dev/null +++ b/langserver/java/expressiontypes/Variable.js @@ -0,0 +1,28 @@ +/** + * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent + * @typedef {import('../body-types').Local} Local + * @typedef {import('../tokenizer').Token} Token + * @typedef {import('java-mti').Field} Field + * @typedef {import('java-mti').Parameter} Parameter + * @typedef {import('../source-types').SourceEnumValue} SourceEnumValue + */ +const { Expression } = require("./Expression"); + +class Variable extends Expression { + /** + * @param {Token} name_token + * @param {Local|Parameter|Field|SourceEnumValue} variable + */ + constructor(name_token, variable) { + super(); + this.name_token = name_token; + this.variable = variable; + this.type = this.variable.type; + } + + tokens() { + return this.name_token; + } +} + +exports.Variable = Variable; diff --git a/langserver/java/expressiontypes/literals/LiteralValue.js b/langserver/java/expressiontypes/literals/LiteralValue.js index b754f2f..c94acd8 100644 --- a/langserver/java/expressiontypes/literals/LiteralValue.js +++ b/langserver/java/expressiontypes/literals/LiteralValue.js @@ -11,6 +11,10 @@ class LiteralValue extends Expression { super(); this.token = token; } + + tokens() { + return this.token; + } } exports.LiteralValue = LiteralValue; diff --git a/langserver/java/source-types.js b/langserver/java/source-types.js index 2aaef56..f19caf9 100644 --- a/langserver/java/source-types.js +++ b/langserver/java/source-types.js @@ -498,3 +498,4 @@ exports.SourceAnnotation = SourceAnnotation; exports.SourceUnit = SourceUnit; exports.SourcePackage = SourcePackage; exports.SourceImport = SourceImport; +exports.SourceEnumValue = SourceEnumValue; diff --git a/langserver/java/statementtypes/Block.js b/langserver/java/statementtypes/Block.js index c2df786..fd3cfe8 100644 --- a/langserver/java/statementtypes/Block.js +++ b/langserver/java/statementtypes/Block.js @@ -1,8 +1,19 @@ +/** + * @typedef {import('../tokenizer').Token} Token + */ const { Statement } = require("./Statement"); class Block extends Statement { /** @type {Statement[]} */ statements = []; + + /** + * @param {Token} open + */ + constructor(open) { + super(); + this.open = open; + } } exports.Block = Block;