mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-24 10:30:54 +00:00
add Any* classes to reduce cascading errors
This commit is contained in:
@@ -941,6 +941,9 @@ function resolveAssignment(tokens, ident, lhs, op, rhs) {
|
||||
* @param {Local|Parameter|Field|ArrayElement|Value} value
|
||||
*/
|
||||
function checkAssignmentExpression(tokens, variable, op, value) {
|
||||
if (variable instanceof AnyValue || value instanceof AnyValue) {
|
||||
return true;
|
||||
}
|
||||
if (variable instanceof Value) {
|
||||
addproblem(tokens, ParseProblem.Error(op, `Invalid assignment: left-hand side is not a variable`));
|
||||
return;
|
||||
@@ -1048,8 +1051,12 @@ function isTypeCastable(source_type, cast_type) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (source_type instanceof AnyType || cast_type instanceof AnyType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1061,8 +1068,9 @@ function isTypeAssignable(dest_type, value_type) {
|
||||
let is_assignable = false;
|
||||
if (dest_type.typeSignature === value_type.typeSignature) {
|
||||
is_assignable = true;
|
||||
}
|
||||
else if (value_type instanceof PrimitiveType) {
|
||||
} else if (dest_type instanceof AnyType || value_type instanceof AnyType) {
|
||||
return true;
|
||||
} else if (value_type instanceof PrimitiveType) {
|
||||
const valid_dest_types = {
|
||||
I: /^[IJFD]$/,
|
||||
J: /^[JFD]$/,
|
||||
@@ -1119,8 +1127,9 @@ function checkEqualityComparison(tokens, lhs, op, rhs) {
|
||||
let is_comparable;
|
||||
if (lhs.type.typeSignature === rhs.type.typeSignature) {
|
||||
is_comparable = true;
|
||||
}
|
||||
else if (lhs.type instanceof PrimitiveType) {
|
||||
} else if (lhs.type instanceof AnyType || rhs.type instanceof AnyType) {
|
||||
is_comparable = true;
|
||||
} else if (lhs.type instanceof PrimitiveType) {
|
||||
const valid_rhs_type = {
|
||||
Z: /^Z$/,
|
||||
V: /^$/,
|
||||
@@ -1170,6 +1179,9 @@ function resolveComparison(tokens, ident, lhs, op, rhs) {
|
||||
* @param {Local|Parameter|Field|ArrayElement|Value} rhs
|
||||
*/
|
||||
function checkOperator(tokens, lhs, op, rhs, re) {
|
||||
if (lhs.type instanceof AnyType || rhs.type instanceof AnyType) {
|
||||
return;
|
||||
}
|
||||
let is_comparable = re.test(`${lhs.type.typeSignature}${rhs.type.typeSignature}`);
|
||||
if (!is_comparable) {
|
||||
addproblem(tokens, ParseProblem.Error(op, `Operator ${op.value} cannot be applied to types '${lhs.type.fullyDottedTypeName}' and '${rhs.type.fullyDottedTypeName}'`));
|
||||
@@ -1534,14 +1546,7 @@ function arrayElementOrConstructor(tokens, open_array, matches, index) {
|
||||
*/
|
||||
function methodCallExpression(tokens, instance, call_arguments, typemap) {
|
||||
const ident = `${instance.source}(${call_arguments.map(arg => arg.source).join(',')})`;
|
||||
// to keep this simple for now, only resolve if there is exactly one variable for each argument
|
||||
for (let arg of call_arguments) {
|
||||
switch(arg.variables.length) {
|
||||
case 0:
|
||||
return new ResolvedIdent(ident);
|
||||
default: continue;
|
||||
}
|
||||
}
|
||||
|
||||
// method call resolving is painful in Java - we need to match arguments against
|
||||
// possible types in the call, but this must include matching against inherited types and choosing the
|
||||
// most-specific match
|
||||
@@ -1581,6 +1586,9 @@ function methodCallExpression(tokens, instance, call_arguments, typemap) {
|
||||
* @param {string[][]} arg_type_signatures
|
||||
*/
|
||||
function isCallCompatible(m, arg_type_signatures) {
|
||||
if (m instanceof AnyMethod) {
|
||||
return true;
|
||||
}
|
||||
if (m.parameterCount !== arg_type_signatures.length) {
|
||||
return;
|
||||
}
|
||||
@@ -1744,7 +1752,10 @@ function arrayTypeExpression(matches) {
|
||||
* @param {Map<string,JavaType>} typemap
|
||||
*/
|
||||
function parseDottedIdent(matches, tokens, typemap) {
|
||||
let variables = [], methods = [], types = [], package_name = '';
|
||||
let variables = [],
|
||||
methods = [],
|
||||
types = [],
|
||||
package_name = '';
|
||||
const qualified_ident = `${matches.source}.${tokens.current.value}`;
|
||||
|
||||
switch (tokens.current.value) {
|
||||
@@ -1752,7 +1763,10 @@ function parseDottedIdent(matches, tokens, typemap) {
|
||||
// e.g int.class
|
||||
// convert the types to Class instances
|
||||
tokens.inc();
|
||||
variables = matches.types.map(t => new Value(qualified_ident, signatureToType(`Ljava/lang/Class<${t.typeSignature}>;`, typemap)));
|
||||
variables = matches.types.map(t => {
|
||||
const type_signature = t instanceof AnyType ? '' : `<${t.typeSignature}>`
|
||||
return new Value(qualified_ident, signatureToType(`Ljava/lang/Class${type_signature};`, typemap));
|
||||
});
|
||||
return new ResolvedIdent(qualified_ident, variables);
|
||||
case 'this':
|
||||
// e.g Type.this - it must be an enclosing type
|
||||
@@ -1776,6 +1790,15 @@ function parseDottedIdent(matches, tokens, typemap) {
|
||||
});
|
||||
/** @type {JavaType[]} */
|
||||
matches.types.forEach(t => {
|
||||
// if there is an AnyType, then add a type, variable and method
|
||||
// - this prevents multiple errors in dotted values/
|
||||
// e.g R.layout.name wiil only error once (on R), not on all 3 idents
|
||||
if (t instanceof AnyType) {
|
||||
types.push(new AnyType(qualified_ident));
|
||||
variables.push(new AnyValue(qualified_ident));
|
||||
methods.push(new AnyMethod(tokens.current.value));
|
||||
return;
|
||||
}
|
||||
if (t instanceof CEIType) {
|
||||
const enclosed_type_signature = `${t.shortSignature}$${tokens.current.value}`;
|
||||
const enc_type = typemap.get(enclosed_type_signature);
|
||||
@@ -1803,8 +1826,10 @@ function parseDottedIdent(matches, tokens, typemap) {
|
||||
}
|
||||
}
|
||||
|
||||
const match = new ResolvedIdent(qualified_ident, variables, methods, types, package_name);
|
||||
checkIdentifierFound(tokens, tokens.current.value, match);
|
||||
tokens.inc();
|
||||
return new ResolvedIdent(qualified_ident, variables, methods, types, package_name);
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1837,13 +1862,26 @@ function parseDottedIdent(matches, tokens, typemap) {
|
||||
* @param {Map<string,JavaType>} typemap
|
||||
*/
|
||||
function resolveIdentifier(tokens, locals, method, imports, typemap) {
|
||||
const matches = findIdentifier(tokens.current.value, locals, method, imports, typemap);
|
||||
if (!matches.variables[0] && !matches.methods[0] && !matches.types[0] && !matches.package_name) {
|
||||
addproblem(tokens, ParseProblem.Error(tokens.current, `Undeclared identifier: ${tokens.current.value}`))
|
||||
}
|
||||
const ident = tokens.current.value;
|
||||
const matches = findIdentifier(ident, locals, method, imports, typemap);
|
||||
checkIdentifierFound(tokens, ident, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TokenList} tokens
|
||||
* @param {ResolvedIdent} matches
|
||||
*/
|
||||
function checkIdentifierFound(tokens, ident, matches) {
|
||||
if (!matches.variables[0] && !matches.methods[0] && !matches.types[0] && !matches.package_name) {
|
||||
addproblem(tokens, ParseProblem.Error(tokens.current, `Unresolved identifier: ${matches.source}`));
|
||||
// pretend it matches everything
|
||||
matches.variables = [new AnyValue(matches.source)];
|
||||
matches.methods = [new AnyMethod(ident)];
|
||||
matches.types = [new AnyType(matches.source)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} ident
|
||||
* @param {Local[]} locals
|
||||
@@ -1945,6 +1983,45 @@ function resolveTypeOrPackage(ident, scoped_type, imports, typemap) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AnyType is a special type that's used to fill in types that are missing.
|
||||
* To prevent cascading errors, AnyType should be fully assign/case/type-compatible
|
||||
* with any other type
|
||||
*/
|
||||
class AnyType extends JavaType {
|
||||
/**
|
||||
*
|
||||
* @param {String} label
|
||||
*/
|
||||
constructor(label) {
|
||||
super("class", [], '');
|
||||
super.simpleTypeName = label;
|
||||
}
|
||||
|
||||
static Instance = new AnyType('');
|
||||
|
||||
get rawTypeSignature() {
|
||||
return 'U';
|
||||
}
|
||||
|
||||
get typeSignature() {
|
||||
return 'U';
|
||||
}
|
||||
}
|
||||
|
||||
class AnyMethod extends Method {
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
constructor(name) {
|
||||
super(name, [], '');
|
||||
}
|
||||
|
||||
get returnType() {
|
||||
return AnyType.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
class Local {
|
||||
/**
|
||||
* @param {Token[]} modifiers
|
||||
@@ -2003,6 +2080,12 @@ class Value {
|
||||
}
|
||||
}
|
||||
|
||||
class AnyValue extends Value {
|
||||
constructor(name) {
|
||||
super(name, AnyType.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
class LiteralValue extends Value { }
|
||||
|
||||
class MethodCall extends Value {
|
||||
|
||||
Reference in New Issue
Block a user