org.sonar.java.resolve.Resolve Maven / Gradle / Ivy
The newest version!
/*
* SonarQube Java
* Copyright (C) 2012 SonarSource
* [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.java.resolve;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/**
* Routines for name resolution.
*
* Lookup by name and then filter by type is performant, because amount of symbols with same name are relatively small.
*
* Naming conventions:
* env - is the environment where the symbol was mentioned
* site - is the type of which symbol is a member
* name - is the symbol's name
*
* TODO site should be represented by class Type
*/
public class Resolve {
private final JavaSymbolNotFound symbolNotFound = new JavaSymbolNotFound();
private final BytecodeCompleter bytecodeCompleter;
private final ParametrizedTypeCache parametrizedTypeCache;
private final Types types = new Types();
private final Symbols symbols;
public Resolve(Symbols symbols, BytecodeCompleter bytecodeCompleter, ParametrizedTypeCache parametrizedTypeCache) {
this.symbols = symbols;
this.bytecodeCompleter = bytecodeCompleter;
this.parametrizedTypeCache = parametrizedTypeCache;
}
@Nullable
private static JavaSymbol.TypeJavaSymbol superclassSymbol(JavaSymbol.TypeJavaSymbol c) {
JavaType supertype = c.getSuperclass();
return supertype == null ? null : supertype.symbol;
}
public JavaSymbol.TypeJavaSymbol registerClass(JavaSymbol.TypeJavaSymbol classSymbol) {
return bytecodeCompleter.registerClass(classSymbol);
}
public Scope createStarImportScope(JavaSymbol owner) {
return new Scope.StarImportScope(owner, bytecodeCompleter);
}
public Scope createStaticStarImportScope(JavaSymbol owner) {
return new Scope.StaticStarImportScope(owner, bytecodeCompleter);
}
public JavaType resolveTypeSubstitution(JavaType type, JavaType definition) {
if(definition instanceof JavaType.ParametrizedTypeJavaType) {
return substituteTypeParameter(type, ((JavaType.ParametrizedTypeJavaType) definition).typeSubstitution);
}
return type;
}
private JavaType substituteTypeParameter(JavaType type, TypeSubstitution substitution) {
JavaType substitutedType = substitution.substitutedType(type);
if (substitutedType != null) {
return substitutedType;
}
if(type instanceof JavaType.ParametrizedTypeJavaType) {
JavaType.ParametrizedTypeJavaType ptt = (JavaType.ParametrizedTypeJavaType) type;
TypeSubstitution newSubstitution = new TypeSubstitution();
for (Map.Entry entry : ptt.typeSubstitution.substitutionEntries()) {
newSubstitution.add(entry.getKey(), substituteTypeParameter(entry.getValue(), substitution));
}
return parametrizedTypeCache.getParametrizedTypeType(ptt.rawType.getSymbol(), newSubstitution);
}
return type;
}
/**
* Finds field with given name.
*/
private Resolution findField(Env env, JavaSymbol.TypeJavaSymbol site, String name, JavaSymbol.TypeJavaSymbol c) {
Resolution bestSoFar = unresolved();
Resolution resolution = new Resolution();
for (JavaSymbol symbol : c.members().lookup(name)) {
if (symbol.kind == JavaSymbol.VAR) {
if(isAccessible(env, site, symbol)) {
resolution.symbol = symbol;
resolution.type = resolveTypeSubstitution(symbol.type, c.type);
return resolution;
} else {
return Resolution.resolution(new AccessErrorJavaSymbol(symbol, symbols.unknownType));
}
}
}
if (c.getSuperclass() != null) {
resolution = findField(env, site, name, c.getSuperclass().symbol);
if (resolution.symbol.kind < bestSoFar.symbol.kind) {
resolution.type = resolveTypeSubstitution(resolution.symbol.type, c.getSuperclass());
bestSoFar = resolution;
}
}
for (JavaType interfaceType : c.getInterfaces()) {
resolution = findField(env, site, name, interfaceType.symbol);
if (resolution.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = resolution;
}
}
return bestSoFar;
}
/**
* Finds variable or field with given name.
*/
private Resolution findVar(Env env, String name) {
Resolution bestSoFar = unresolved();
Env env1 = env;
while (env1.outer() != null) {
Resolution sym = new Resolution();
for (JavaSymbol symbol : env1.scope().lookup(name)) {
if (symbol.kind == JavaSymbol.VAR) {
sym.symbol = symbol;
}
}
if (sym.symbol == null) {
sym = findField(env1, env1.enclosingClass(), name, env1.enclosingClass());
}
if (sym.symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return sym;
} else if (sym.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = sym;
}
env1 = env1.outer();
}
JavaSymbol symbol = findInStaticImport(env, name, JavaSymbol.VAR);
if (symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return Resolution.resolution(symbol);
} else if (symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = Resolution.resolution(symbol);
}
return bestSoFar;
}
/**
* @param kind subset of {@link JavaSymbol#VAR}, {@link JavaSymbol#MTH}
*/
private JavaSymbol findInStaticImport(Env env, String name, int kind) {
JavaSymbol bestSoFar = symbolNotFound;
//imports
//Ok because clash of name between type and var/method result in compile error: JLS8 7.5.3
for (JavaSymbol symbol : env.namedImports().lookup(name)) {
if ((kind & symbol.kind) != 0) {
return symbol;
}
}
for (JavaSymbol symbol : env.staticStarImports().lookup(name)) {
if ((kind & symbol.kind) != 0) {
return symbol;
}
}
return bestSoFar;
}
private JavaSymbol findMemberType(Env env, JavaSymbol.TypeJavaSymbol site, String name, JavaSymbol.TypeJavaSymbol c) {
JavaSymbol bestSoFar = symbolNotFound;
for (JavaSymbol symbol : c.members().lookup(name)) {
if (symbol.kind == JavaSymbol.TYP) {
return isAccessible(env, site, symbol)
? symbol
: new AccessErrorJavaSymbol(symbol, symbols.unknownType);
}
}
if (c.getSuperclass() != null) {
JavaSymbol symbol = findMemberType(env, site, name, c.getSuperclass().symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
for (JavaType interfaceType : c.getInterfaces()) {
JavaSymbol symbol = findMemberType(env, site, name, interfaceType.symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* Finds type with given name.
*/
private JavaSymbol findType(Env env, String name) {
JavaSymbol bestSoFar = symbolNotFound;
for (Env env1 = env; env1 != null; env1 = env1.outer()) {
for (JavaSymbol symbol : env1.scope().lookup(name)) {
if (symbol.kind == JavaSymbol.TYP) {
return symbol;
}
}
if (env1.outer != null) {
JavaSymbol symbol = findMemberType(env1, env1.enclosingClass(), name, env1.enclosingClass());
if (symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
}
//checks predefined types
JavaSymbol predefinedSymbol = findMemberType(env, symbols.predefClass, name, symbols.predefClass);
if (predefinedSymbol.kind < bestSoFar.kind) {
return predefinedSymbol;
}
//JLS8 6.4.1 Shadowing rules
//named imports
for (JavaSymbol symbol : env.namedImports().lookup(name)) {
if (symbol.kind == JavaSymbol.TYP) {
return symbol;
}
}
//package types
JavaSymbol sym = findIdentInPackage(env.packge(), name, JavaSymbol.TYP);
if (sym.kind < bestSoFar.kind) {
return sym;
}
//on demand imports
for (JavaSymbol symbol : env.starImports().lookup(name)) {
if (symbol.kind == JavaSymbol.TYP) {
return symbol;
}
}
//java.lang
JavaSymbol.PackageJavaSymbol javaLang = bytecodeCompleter.enterPackage("java.lang");
for (JavaSymbol symbol : javaLang.members().lookup(name)) {
if (symbol.kind == JavaSymbol.TYP) {
return symbol;
}
}
return bestSoFar;
}
/**
* @param kind subset of {@link JavaSymbol#VAR}, {@link JavaSymbol#TYP}, {@link JavaSymbol#PCK}
*/
public Resolution findIdent(Env env, String name, int kind) {
Resolution bestSoFar = unresolved();
if ((kind & JavaSymbol.VAR) != 0) {
Resolution res = findVar(env, name);
if (res.symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return res;
} else if (res.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = res;
}
}
if ((kind & JavaSymbol.TYP) != 0) {
Resolution res = new Resolution();
res.symbol = findType(env, name);
if (res.symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return res;
} else if (res.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = res;
}
}
if ((kind & JavaSymbol.PCK) != 0) {
Resolution res = new Resolution();
res.symbol = findIdentInPackage(symbols.defaultPackage, name, JavaSymbol.PCK);
if (res.symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return res;
} else if (res.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = res;
}
}
return bestSoFar;
}
/**
* @param kind subset of {@link JavaSymbol#TYP}, {@link JavaSymbol#PCK}
*/
public JavaSymbol findIdentInPackage(JavaSymbol site, String name, int kind) {
String fullname = bytecodeCompleter.formFullName(name, site);
JavaSymbol bestSoFar = symbolNotFound;
//Try to find a type matching the name.
if ((kind & JavaSymbol.TYP) != 0) {
JavaSymbol sym = bytecodeCompleter.loadClass(fullname);
if (sym.kind < bestSoFar.kind) {
bestSoFar = sym;
}
}
//We did not find the class so identifier must be a package.
if ((kind & JavaSymbol.PCK) != 0 && bestSoFar.kind >= symbolNotFound.kind) {
bestSoFar = bytecodeCompleter.enterPackage(fullname);
}
return bestSoFar;
}
/**
* @param kind subset of {@link JavaSymbol#VAR}, {@link JavaSymbol#TYP}
*/
public JavaSymbol findIdentInType(Env env, JavaSymbol.TypeJavaSymbol site, String name, int kind) {
JavaSymbol bestSoFar = symbolNotFound;
JavaSymbol symbol;
if ((kind & JavaSymbol.VAR) != 0) {
symbol = findField(env, site, name, site).symbol;
if (symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
if ((kind & JavaSymbol.TYP) != 0) {
symbol = findMemberType(env, site, name, site);
if (symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* Finds method matching given name and types of arguments.
*/
public Resolution findMethod(Env env, String name, List argTypes, List typeParamTypes) {
Resolution bestSoFar = unresolved();
Env env1 = env;
while (env1.outer() != null) {
Resolution res = findMethod(env1, env1.enclosingClass().getType(), name, argTypes, typeParamTypes);
if (res.symbol.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return res;
} else if (res.symbol.kind < bestSoFar.symbol.kind) {
bestSoFar = res;
}
env1 = env1.outer;
}
JavaSymbol sym = findInStaticImport(env, name, JavaSymbol.MTH);
if (sym.kind < JavaSymbol.ERRONEOUS) {
// symbol exists
return Resolution.resolution(sym);
} else if (sym.kind < bestSoFar.symbol.kind) {
bestSoFar = Resolution.resolution(sym);
}
return bestSoFar;
}
public Resolution findMethod(Env env, JavaType site, String name, List argTypes) {
return findMethod(env, site, name, argTypes, ImmutableList.of(), false);
}
public Resolution findMethod(Env env, JavaType site, String name, List argTypes, List typeParams) {
return findMethod(env, site, name, argTypes, typeParams, false);
}
private Resolution findMethod(Env env, JavaType site, String name, List argTypes, List typeParams, boolean autoboxing) {
Resolution bestSoFar = unresolved();
for (JavaSymbol symbol : site.getSymbol().members().lookup(name)) {
if (symbol.kind == JavaSymbol.MTH) {
JavaSymbol best = selectBest(env, site.getSymbol(), argTypes, symbol, bestSoFar.symbol, autoboxing);
if(best == symbol) {
bestSoFar = Resolution.resolution(best);
bestSoFar.type = resolveTypeSubstitution(((JavaType.MethodJavaType) best.type).resultType, site);
JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) best;
bestSoFar.type = handleTypeArguments(typeParams, bestSoFar.type, methodSymbol);
}
}
}
//look in supertypes for more specialized method (overloading).
if (site.getSymbol().getSuperclass() != null) {
Resolution method = findMethod(env, site.getSymbol().getSuperclass(), name, argTypes, typeParams);
JavaSymbol best = selectBest(env, site.getSymbol(), argTypes, method.symbol, bestSoFar.symbol, autoboxing);
if(best == method.symbol) {
bestSoFar = method;
}
}
for (JavaType interfaceType : site.getSymbol().getInterfaces()) {
Resolution method = findMethod(env, interfaceType, name, argTypes, typeParams);
JavaSymbol best = selectBest(env, site.getSymbol(), argTypes, method.symbol, bestSoFar.symbol, autoboxing);
if(best == method.symbol) {
bestSoFar = method;
}
}
if(bestSoFar.symbol.kind >= JavaSymbol.ERRONEOUS && !autoboxing) {
bestSoFar = findMethod(env, site, name, argTypes, typeParams, true);
}
return bestSoFar;
}
private JavaType handleTypeArguments(List typeParams, JavaType type, JavaSymbol.MethodJavaSymbol methodSymbol) {
if (!typeParams.isEmpty() && methodSymbol.typeVariableTypes.size() == typeParams.size()) {
TypeSubstitution typeSubstitution = new TypeSubstitution();
int i = 0;
for (JavaType.TypeVariableJavaType typeVariableType : methodSymbol.typeVariableTypes) {
typeSubstitution.add(typeVariableType, typeParams.get(i));
i++;
}
return substituteTypeParameter(type, typeSubstitution);
}
return type;
}
/**
* @param symbol candidate
* @param bestSoFar previously found best match
*/
private JavaSymbol selectBest(Env env, JavaSymbol.TypeJavaSymbol site, List argTypes, JavaSymbol symbol, JavaSymbol bestSoFar, boolean autoboxing) {
// TODO get rid of null check
if (symbol.kind >= JavaSymbol.ERRONEOUS || !isInheritedIn(symbol, site) || symbol.type == null) {
return bestSoFar;
}
boolean isVarArgs = ((JavaSymbol.MethodJavaSymbol) symbol).isVarArgs();
if (!isArgumentsAcceptable(argTypes, ((JavaType.MethodJavaType) symbol.type).argTypes, isVarArgs, autoboxing)) {
return bestSoFar;
}
// TODO ambiguity, errors, ...
if (!isAccessible(env, site, symbol)) {
return new AccessErrorJavaSymbol(symbol, symbols.unknownType);
}
JavaSymbol mostSpecific = selectMostSpecific(symbol, bestSoFar, argTypes);
if (mostSpecific.isKind(JavaSymbol.AMBIGUOUS)) {
//same signature, we keep the first symbol found (overrides the other one).
mostSpecific = bestSoFar;
}
return mostSpecific;
}
/**
* @param argTypes types of arguments
* @param formals types of formal parameters of method
*/
private boolean isArgumentsAcceptable(List argTypes, List formals, boolean isVarArgs, boolean autoboxing) {
int argsSize = argTypes.size();
int formalsSize = formals.size();
int nbArgToCheck = argsSize - formalsSize;
if (isVarArgs) {
//Check at least last parameter for varags compatibility
nbArgToCheck += 1;
if (nbArgToCheck < 0) {
//arity is not correct, it can only differ negatively by one for varargs.
return false;
}
} else if (nbArgToCheck != 0) {
//Not a vararg, we should have same number of arguments
return false;
}
for (int i = 1; i <= nbArgToCheck; i++) {
JavaType.ArrayJavaType lastFormal = (JavaType.ArrayJavaType) formals.get(formalsSize - 1);
JavaType argType = argTypes.get(argsSize - i);
//Check type of element of array or if we invoke with an array that it is a compatible array type
if (!isAcceptableType(argType, lastFormal.elementType, autoboxing) && (nbArgToCheck != 1 || !types.isSubtype(argType, lastFormal))) {
return false;
}
}
for (int i = 0; i < argsSize - nbArgToCheck; i++) {
if (!isAcceptableType(argTypes.get(i), formals.get(i), autoboxing)) {
return false;
}
}
return true;
}
private boolean isAcceptableType(JavaType arg, JavaType formal, boolean autoboxing) {
return types.isSubtype(arg.erasure(), formal.erasure()) || (autoboxing && isAcceptableByAutoboxing(arg, formal.erasure()));
}
private boolean isAcceptableByAutoboxing(JavaType expressionType, JavaType formalType) {
if (expressionType.isPrimitive()) {
return types.isSubtype(symbols.boxedTypes.get(expressionType), formalType);
} else {
JavaType unboxedType = symbols.boxedTypes.inverse().get(expressionType);
if (unboxedType != null) {
return types.isSubtype(unboxedType, formalType);
}
}
return false;
}
/**
* JLS7 15.12.2.5. Choosing the Most Specific Method
*/
private JavaSymbol selectMostSpecific(JavaSymbol m1, JavaSymbol m2, List argTypes) {
// FIXME get rig of null check
if (m2.type == null || !m2.isKind(JavaSymbol.MTH)) {
return m1;
}
boolean m1SignatureMoreSpecific = isSignatureMoreSpecific(m1, m2);
boolean m2SignatureMoreSpecific = isSignatureMoreSpecific(m2, m1);
if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) {
return new AmbiguityErrorJavaSymbol();
} else if (m1SignatureMoreSpecific) {
return m1;
} else if (m2SignatureMoreSpecific) {
return m2;
} else {
return new AmbiguityErrorJavaSymbol();
}
}
/**
* @return true, if signature of m1 is more specific than signature of m2
*/
private boolean isSignatureMoreSpecific(JavaSymbol m1, JavaSymbol m2) {
//TODO handle specific signature with varargs
// ((MethodSymbol) m2).isVarArgs()
return isArgumentsAcceptable(((JavaType.MethodJavaType) m1.type).argTypes, ((JavaType.MethodJavaType) m2.type).argTypes, false, false);
}
/**
* Is class accessible in given environment?
*/
@VisibleForTesting
boolean isAccessible(Env env, JavaSymbol.TypeJavaSymbol c) {
final boolean result;
switch (c.flags() & Flags.ACCESS_FLAGS) {
case Flags.PRIVATE:
result = (env.enclosingClass().outermostClass() == c.owner().outermostClass());
break;
case 0:
result = (env.packge() == c.packge());
break;
case Flags.PUBLIC:
result = true;
break;
case Flags.PROTECTED:
result = (env.packge() == c.packge()) || isInnerSubClass(env.enclosingClass(), c.owner());
break;
default:
throw new IllegalStateException();
}
// TODO check accessibility of enclosing type: isAccessible(env, c.type.getEnclosingType())
return result;
}
/**
* Is given class a subclass of given base class, or an inner class of a subclass?
*/
private boolean isInnerSubClass(JavaSymbol.TypeJavaSymbol c, JavaSymbol base) {
while (c != null && isSubClass(c, base)) {
c = c.owner().enclosingClass();
}
return c != null;
}
/**
* Is given class a subclass of given base class?
*/
@VisibleForTesting
boolean isSubClass(JavaSymbol.TypeJavaSymbol c, JavaSymbol base) {
// TODO get rid of null check
if (c == null) {
return false;
}
// TODO see Javac
if (c == base) {
// same class
return true;
} else if ((base.flags() & Flags.INTERFACE) != 0) {
// check if class implements base
for (JavaType interfaceType : c.getInterfaces()) {
if (isSubClass(interfaceType.symbol, base)) {
return true;
}
}
// check if superclass implements base
return isSubClass(superclassSymbol(c), base);
} else {
// check if class extends base or its superclass extends base
return isSubClass(superclassSymbol(c), base);
}
}
/**
* Is symbol accessible as a member of given class in given environment?
*
* Symbol is accessible only if not overridden by another symbol. If overridden, then strictly speaking it is not a member.
*/
private boolean isAccessible(Env env, JavaSymbol.TypeJavaSymbol site, JavaSymbol symbol) {
switch (symbol.flags() & Flags.ACCESS_FLAGS) {
case Flags.PRIVATE:
//if enclosing class is null, we are checking accessibility for imports so we return false.
// no check of overriding, because private members cannot be overridden
return env.enclosingClass != null && (env.enclosingClass().outermostClass() == symbol.owner().outermostClass())
&& isInheritedIn(symbol, site);
case 0:
return (env.packge() == symbol.packge())
&& isAccessible(env, site)
&& isInheritedIn(symbol, site)
&& notOverriddenIn(site, symbol);
case Flags.PUBLIC:
return isAccessible(env, site)
&& notOverriddenIn(site, symbol);
case Flags.PROTECTED:
return ((env.packge() == symbol.packge()) || isProtectedAccessible(symbol, env.enclosingClass, site))
&& isAccessible(env, site)
&& notOverriddenIn(site, symbol);
default:
throw new IllegalStateException();
}
}
private boolean notOverriddenIn(JavaSymbol.TypeJavaSymbol site, JavaSymbol symbol) {
// TODO see Javac
return true;
}
/**
* Is symbol inherited in given class?
*/
@VisibleForTesting
boolean isInheritedIn(JavaSymbol symbol, JavaSymbol.TypeJavaSymbol clazz) {
switch (symbol.flags() & Flags.ACCESS_FLAGS) {
case Flags.PUBLIC:
return true;
case Flags.PRIVATE:
return symbol.owner() == clazz;
case Flags.PROTECTED:
// TODO see Javac
return true;
case 0:
// TODO see Javac
JavaSymbol.PackageJavaSymbol thisPackage = symbol.packge();
for (JavaSymbol.TypeJavaSymbol sup = clazz; sup != null && sup != symbol.owner(); sup = superclassSymbol(sup)) {
if (sup.packge() != thisPackage) {
return false;
}
}
return true;
default:
throw new IllegalStateException();
}
}
private boolean isProtectedAccessible(JavaSymbol symbol, JavaSymbol.TypeJavaSymbol c, JavaSymbol.TypeJavaSymbol site) {
// TODO see Javac
return true;
}
private Resolution unresolved() {
Resolution resolution = new Resolution(symbolNotFound);
resolution.type = symbols.unknownType;
return resolution;
}
/**
* Resolution holds the symbol resolved and its type in this context.
* This is required to handle type substitution for generics.
*/
static class Resolution {
private JavaSymbol symbol;
private JavaType type;
private Resolution(JavaSymbol symbol) {
this.symbol = symbol;
}
Resolution() {
}
static Resolution resolution(JavaSymbol symbol) {
return new Resolution(symbol);
}
JavaSymbol symbol() {
return symbol;
}
public JavaType type() {
if (type == null) {
if(symbol.isKind(JavaSymbol.MTH)) {
return ((JavaType.MethodJavaType)symbol.type).resultType;
}
return symbol.type;
}
return type;
}
}
static class Env {
/**
* The next enclosing environment.
*/
Env next;
/**
* The environment enclosing the current class.
*/
Env outer;
JavaSymbol.PackageJavaSymbol packge;
JavaSymbol.TypeJavaSymbol enclosingClass;
Scope scope;
Scope namedImports;
Scope starImports;
Scope staticStarImports;
@Nullable
Env outer() {
return outer;
}
JavaSymbol.TypeJavaSymbol enclosingClass() {
return enclosingClass;
}
public JavaSymbol.PackageJavaSymbol packge() {
return packge;
}
Scope namedImports() {
return namedImports;
}
Scope starImports() {
return starImports;
}
public Scope staticStarImports() {
return staticStarImports;
}
Scope scope() {
return scope;
}
public Env dup() {
Env env = new Env();
env.next = this;
env.outer = this.outer;
env.packge = this.packge;
env.enclosingClass = this.enclosingClass;
env.scope = this.scope;
env.namedImports = this.namedImports;
env.starImports = this.starImports;
env.staticStarImports = this.staticStarImports;
return env;
}
}
public static class JavaSymbolNotFound extends JavaSymbol {
public JavaSymbolNotFound() {
super(JavaSymbol.ABSENT, 0, null, null);
}
}
public static class AmbiguityErrorJavaSymbol extends JavaSymbol {
public AmbiguityErrorJavaSymbol() {
super(JavaSymbol.AMBIGUOUS, 0, null, null);
}
}
public static class AccessErrorJavaSymbol extends JavaSymbol {
/**
* The invalid symbol found during resolution.
*/
JavaSymbol symbol;
public AccessErrorJavaSymbol(JavaSymbol symbol, JavaType type) {
super(JavaSymbol.ERRONEOUS, 0, null, null);
this.symbol = symbol;
this.type = type;
}
}
}