use of org.codehaus.groovy.ast.Variable in project groovy by apache.
the class StaticTypeCheckingVisitor method visitMethodCallExpression.
@Override
public void visitMethodCallExpression(MethodCallExpression call) {
final String name = call.getMethodAsString();
if (name == null) {
addStaticTypeError("cannot resolve dynamic method name at compile time.", call.getMethod());
return;
}
if (extension.beforeMethodCall(call)) {
extension.afterMethodCall(call);
return;
}
typeCheckingContext.pushEnclosingMethodCall(call);
final Expression objectExpression = call.getObjectExpression();
objectExpression.visit(this);
call.getMethod().visit(this);
// the call is made on a collection type
if (call.isSpreadSafe()) {
//TODO check if this should not be change to iterator based call logic
ClassNode expressionType = getType(objectExpression);
if (!implementsInterfaceOrIsSubclassOf(expressionType, Collection_TYPE) && !expressionType.isArray()) {
addStaticTypeError("Spread operator can only be used on collection types", objectExpression);
return;
} else {
// type check call as if it was made on component type
ClassNode componentType = inferComponentType(expressionType, int_TYPE);
MethodCallExpression subcall = callX(castX(componentType, EmptyExpression.INSTANCE), name, call.getArguments());
subcall.setLineNumber(call.getLineNumber());
subcall.setColumnNumber(call.getColumnNumber());
subcall.setImplicitThis(call.isImplicitThis());
visitMethodCallExpression(subcall);
// the inferred type here should be a list of what the subcall returns
ClassNode subcallReturnType = getType(subcall);
ClassNode listNode = LIST_TYPE.getPlainNodeReference();
listNode.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(subcallReturnType)) });
storeType(call, listNode);
// store target method
storeTargetMethod(call, (MethodNode) subcall.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
typeCheckingContext.popEnclosingMethodCall();
return;
}
}
Expression callArguments = call.getArguments();
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments);
checkForbiddenSpreadArgument(argumentList);
// for arguments, we need to visit closures *after* the method has been chosen
final ClassNode receiver = getType(objectExpression);
visitMethodCallArguments(receiver, argumentList, false, null);
ClassNode[] args = getArgumentTypes(argumentList);
final boolean isCallOnClosure = isClosureCall(name, objectExpression, callArguments);
try {
boolean callArgsVisited = false;
if (isCallOnClosure) {
// this is a closure.call() call
if (objectExpression == VariableExpression.THIS_EXPRESSION) {
// isClosureCall() check verified earlier that a field exists
FieldNode field = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
GenericsType[] genericsTypes = field.getType().getGenericsTypes();
if (genericsTypes != null) {
ClassNode closureReturnType = genericsTypes[0].getType();
Object data = field.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
if (data != null) {
Parameter[] parameters = (Parameter[]) data;
typeCheckClosureCall(callArguments, args, parameters);
}
storeType(call, closureReturnType);
}
} else if (objectExpression instanceof VariableExpression) {
Variable variable = findTargetVariable((VariableExpression) objectExpression);
if (variable instanceof ASTNode) {
Object data = ((ASTNode) variable).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
if (data != null) {
Parameter[] parameters = (Parameter[]) data;
typeCheckClosureCall(callArguments, args, parameters);
}
ClassNode type = getType(((ASTNode) variable));
if (type != null && type.equals(CLOSURE_TYPE)) {
GenericsType[] genericsTypes = type.getGenericsTypes();
type = OBJECT_TYPE;
if (genericsTypes != null) {
if (!genericsTypes[0].isPlaceholder()) {
type = genericsTypes[0].getType();
}
}
}
if (type != null) {
storeType(call, type);
}
}
} else if (objectExpression instanceof ClosureExpression) {
// we can get actual parameters directly
Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters();
typeCheckClosureCall(callArguments, args, parameters);
ClassNode data = getInferredReturnType(objectExpression);
if (data != null) {
storeType(call, data);
}
}
int nbOfArgs;
if (callArguments instanceof ArgumentListExpression) {
ArgumentListExpression list = (ArgumentListExpression) callArguments;
nbOfArgs = list.getExpressions().size();
} else {
// todo : other cases
nbOfArgs = 0;
}
storeTargetMethod(call, nbOfArgs == 0 ? CLOSURE_CALL_NO_ARG : nbOfArgs == 1 ? CLOSURE_CALL_ONE_ARG : CLOSURE_CALL_VARGS);
} else {
// method call receivers are :
// - possible "with" receivers
// - the actual receiver as found in the method call expression
// - any of the potential receivers found in the instanceof temporary table
// in that order
List<Receiver<String>> receivers = new LinkedList<Receiver<String>>();
List<Receiver<String>> owners = makeOwnerList(objectExpression);
addReceivers(receivers, owners, call.isImplicitThis());
List<MethodNode> mn = null;
Receiver<String> chosenReceiver = null;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = currentReceiver.getType();
mn = findMethod(receiverType, name, args);
// ensure that all methods are either static or declared by the current receiver or a superclass
if (!mn.isEmpty() && (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0) && (call.isImplicitThis() || (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()))) {
// we create separate method lists just to be able to print out
// a nice error message to the user
// a method is accessible if it is static, or if we are not in a static context and it is
// declared by the current receiver or a superclass
List<MethodNode> accessibleMethods = new LinkedList<MethodNode>();
List<MethodNode> inaccessibleMethods = new LinkedList<MethodNode>();
for (final MethodNode node : mn) {
if (node.isStatic() || (!typeCheckingContext.isInStaticContext && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) {
accessibleMethods.add(node);
} else {
inaccessibleMethods.add(node);
}
}
mn = accessibleMethods;
if (accessibleMethods.isEmpty()) {
// choose an arbitrary method to display an error message
MethodNode node = inaccessibleMethods.get(0);
ClassNode owner = node.getDeclaringClass();
addStaticTypeError("Non static method " + owner.getName() + "#" + node.getName() + " cannot be called from static context", call);
}
}
if (!mn.isEmpty()) {
chosenReceiver = currentReceiver;
break;
}
}
if (mn.isEmpty() && typeCheckingContext.getEnclosingClosure() != null && args.length == 0) {
// add special handling of getDelegate() and getOwner()
if ("getDelegate".equals(name)) {
mn = Collections.singletonList(GET_DELEGATE);
} else if ("getOwner".equals(name)) {
mn = Collections.singletonList(GET_OWNER);
} else if ("getThisObject".equals(name)) {
mn = Collections.singletonList(GET_THISOBJECT);
}
}
if (mn.isEmpty()) {
mn = extension.handleMissingMethod(receiver, name, argumentList, args, call);
}
if (mn.isEmpty()) {
addNoMatchingMethodError(receiver, name, args, call);
} else {
if (areCategoryMethodCalls(mn, name, args)) {
addCategoryMethodCallError(call);
}
mn = disambiguateMethods(mn, chosenReceiver != null ? chosenReceiver.getType() : null, args, call);
if (mn.size() == 1) {
MethodNode directMethodCallCandidate = mn.get(0);
if (call.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) == null && !directMethodCallCandidate.isStatic() && objectExpression instanceof ClassExpression && !"java.lang.Class".equals(directMethodCallCandidate.getDeclaringClass().getName())) {
ClassNode owner = directMethodCallCandidate.getDeclaringClass();
addStaticTypeError("Non static method " + owner.getName() + "#" + directMethodCallCandidate.getName() + " cannot be called from static context", call);
}
if (chosenReceiver == null) {
chosenReceiver = Receiver.make(directMethodCallCandidate.getDeclaringClass());
}
ClassNode returnType = getType(directMethodCallCandidate);
if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) {
visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, directMethodCallCandidate);
ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), directMethodCallCandidate, callArguments, call.getGenericsTypes());
returnType = irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType) ? irtg : returnType;
callArgsVisited = true;
}
if (directMethodCallCandidate == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) {
DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression());
returnType = typeCheckingContext.getEnclosingClassNode();
if (md != null) {
returnType = md.getType();
}
}
if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, mn.get(0), call)) {
returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
storeType(call, returnType);
storeTargetMethod(call, directMethodCallCandidate);
String data = chosenReceiver.getData();
if (data != null) {
// the method which has been chosen is supposed to be a call on delegate or owner
// so we store the information so that the static compiler may reuse it
call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data);
}
// if the object expression is a closure shared variable, we will have to perform a second pass
if (objectExpression instanceof VariableExpression) {
VariableExpression var = (VariableExpression) objectExpression;
if (var.isClosureSharedVariable()) {
SecondPassExpression<ClassNode[]> wrapper = new SecondPassExpression<ClassNode[]>(call, args);
typeCheckingContext.secondPassExpressions.add(wrapper);
}
}
}
} else {
addAmbiguousErrorMessage(mn, name, args, call);
}
}
}
// now that a method has been chosen, we are allowed to visit the closures
if (!callArgsVisited) {
MethodNode mn = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
visitMethodCallArguments(receiver, argumentList, true, mn);
// GROOVY-6219
if (mn != null) {
List<Expression> argExpressions = argumentList.getExpressions();
Parameter[] parameters = mn.getParameters();
for (int i = 0; i < argExpressions.size() && i < parameters.length; i++) {
Expression arg = argExpressions.get(i);
ClassNode pType = parameters[i].getType();
ClassNode aType = getType(arg);
if (CLOSURE_TYPE.equals(pType) && CLOSURE_TYPE.equals(aType)) {
if (!isAssignableTo(aType, pType)) {
addNoMatchingMethodError(receiver, name, getArgumentTypes(argumentList), call);
call.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
}
}
}
}
}
} finally {
typeCheckingContext.popEnclosingMethodCall();
extension.afterMethodCall(call);
}
}
use of org.codehaus.groovy.ast.Variable in project groovy by apache.
the class StaticTypeCheckingVisitor method allowStaticAccessToMember.
/**
* This method is used to filter search results in which null means "no match",
* to filter out illegal access to instance members from a static context.
*
* Return null if the given member is not static, but we want to access in
* a static way (staticOnly=true). If we want to access in a non-static way
* we always return the member, since then access to static members and
* non-static members is allowed.
*/
@SuppressWarnings("unchecked")
private <T> T allowStaticAccessToMember(T member, boolean staticOnly) {
if (member == null)
return null;
if (!staticOnly)
return member;
boolean isStatic;
if (member instanceof Variable) {
Variable v = (Variable) member;
isStatic = Modifier.isStatic(v.getModifiers());
} else if (member instanceof List) {
List<MethodNode> list = (List<MethodNode>) member;
if (list.size() == 1) {
return (T) Collections.singletonList(allowStaticAccessToMember(list.get(0), staticOnly));
}
return (T) Collections.emptyList();
} else {
MethodNode mn = (MethodNode) member;
isStatic = mn.isStatic();
}
if (staticOnly && !isStatic)
return null;
return member;
}
use of org.codehaus.groovy.ast.Variable in project groovy by apache.
the class StaticTypeCheckingVisitor method visitBinaryExpression.
@Override
public void visitBinaryExpression(BinaryExpression expression) {
int op = expression.getOperation().getType();
if (op == COMPARE_IDENTICAL || op == COMPARE_NOT_IDENTICAL) {
// we'll report those as errors later
return;
}
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
typeCheckingContext.pushEnclosingBinaryExpression(expression);
try {
final Expression leftExpression = expression.getLeftExpression();
final Expression rightExpression = expression.getRightExpression();
leftExpression.visit(this);
SetterInfo setterInfo = removeSetterInfo(leftExpression);
if (setterInfo != null) {
if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
return;
}
} else {
rightExpression.visit(this);
}
ClassNode lType = getType(leftExpression);
ClassNode rType = getType(rightExpression);
if (isNullConstant(rightExpression)) {
if (!isPrimitiveType(lType))
// primitive types should be ignored as they will result in another failure
rType = UNKNOWN_PARAMETER_TYPE;
}
BinaryExpression reversedBinaryExpression = binX(rightExpression, expression.getOperation(), leftExpression);
ClassNode resultType = op == KEYWORD_IN ? getResultType(rType, op, lType, reversedBinaryExpression) : getResultType(lType, op, rType, expression);
if (op == KEYWORD_IN) {
// in case of the "in" operator, the receiver and the arguments are reversed
// so we use the reversedExpression and get the target method from it
storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
} else if (op == LEFT_SQUARE_BRACKET && leftExpression instanceof VariableExpression && leftExpression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == null) {
storeType(leftExpression, lType);
}
if (resultType == null) {
resultType = lType;
}
// if left expression is a closure shared variable, a second pass should be done
if (leftExpression instanceof VariableExpression) {
VariableExpression leftVar = (VariableExpression) leftExpression;
if (leftVar.isClosureSharedVariable()) {
// if left expression is a closure shared variable, we should check it twice
// see GROOVY-5874
typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<Void>(expression));
}
}
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
// unchecked assignment
// examples:
// List<A> list = new LinkedList()
// List<A> list = []
// Iterable<A> list = new LinkedList()
// in that case, the inferred type of the binary expression is the type of the RHS
// "completed" with generics type information available in the LHS
ClassNode completedType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
resultType = completedType;
}
if (isArrayOp(op) && enclosingBinaryExpression != null && enclosingBinaryExpression.getLeftExpression() == expression && isAssignment(enclosingBinaryExpression.getOperation().getType()) && !lType.isArray()) {
// left hand side of an assignment : map['foo'] = ...
Expression enclosingBE_rightExpr = enclosingBinaryExpression.getRightExpression();
if (!(enclosingBE_rightExpr instanceof ClosureExpression)) {
enclosingBE_rightExpr.visit(this);
}
ClassNode[] arguments = { rType, getType(enclosingBE_rightExpr) };
List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
if (nodes.size() == 1) {
typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingBE_rightExpr);
} else if (nodes.isEmpty()) {
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
}
}
boolean isEmptyDeclaration = expression instanceof DeclarationExpression && rightExpression instanceof EmptyExpression;
if (!isEmptyDeclaration && isAssignment(op)) {
if (rightExpression instanceof ConstructorCallExpression) {
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
}
ClassNode originType = getOriginalDeclarationType(leftExpression);
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
// and we must update the result type
if (!implementsInterfaceOrIsSubclassOf(getWrapper(resultType), getWrapper(originType))) {
resultType = originType;
} else if (lType.isUsingGenerics() && !lType.isEnum() && hasRHSIncompleteGenericTypeInfo(resultType)) {
// for example, LHS is List<ConcreteClass> and RHS is List<T> where T is a placeholder
resultType = lType;
}
// make sure we keep primitive types
if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) {
resultType = originType;
}
// if we are in an if/else branch, keep track of assignment
if (typeCheckingContext.ifElseForWhileAssignmentTracker != null && leftExpression instanceof VariableExpression && !isNullConstant(rightExpression)) {
Variable accessedVariable = ((VariableExpression) leftExpression).getAccessedVariable();
if (accessedVariable instanceof VariableExpression) {
VariableExpression var = (VariableExpression) accessedVariable;
List<ClassNode> types = typeCheckingContext.ifElseForWhileAssignmentTracker.get(var);
if (types == null) {
types = new LinkedList<ClassNode>();
ClassNode type = var.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
types.add(type);
typeCheckingContext.ifElseForWhileAssignmentTracker.put(var, types);
}
types.add(resultType);
}
}
storeType(leftExpression, resultType);
// if right expression is a ClosureExpression, store parameter type information
if (leftExpression instanceof VariableExpression) {
if (rightExpression instanceof ClosureExpression) {
Parameter[] parameters = ((ClosureExpression) rightExpression).getParameters();
leftExpression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, parameters);
} else if (rightExpression instanceof VariableExpression && ((VariableExpression) rightExpression).getAccessedVariable() instanceof Expression && ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) != null) {
Variable targetVariable = findTargetVariable((VariableExpression) leftExpression);
if (targetVariable instanceof ASTNode) {
((ASTNode) targetVariable).putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, ((Expression) ((VariableExpression) rightExpression).getAccessedVariable()).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS));
}
}
}
} else if (op == KEYWORD_INSTANCEOF) {
pushInstanceOfTypeInfo(leftExpression, rightExpression);
}
if (!isEmptyDeclaration) {
storeType(expression, resultType);
}
} finally {
typeCheckingContext.popEnclosingBinaryExpression();
}
}
use of org.codehaus.groovy.ast.Variable in project groovy by apache.
the class StaticTypeCheckingVisitor method getType.
protected ClassNode getType(ASTNode exp) {
ClassNode cn = exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
if (cn != null)
return cn;
if (exp instanceof ClassExpression) {
ClassNode node = CLASS_Type.getPlainNodeReference();
node.setGenericsTypes(new GenericsType[] { new GenericsType(((ClassExpression) exp).getType()) });
return node;
} else if (exp instanceof VariableExpression) {
VariableExpression vexp = (VariableExpression) exp;
ClassNode selfTrait = isTraitSelf(vexp);
if (selfTrait != null)
return makeSelf(selfTrait);
if (vexp == VariableExpression.THIS_EXPRESSION)
return makeThis();
if (vexp == VariableExpression.SUPER_EXPRESSION)
return makeSuper();
final Variable variable = vexp.getAccessedVariable();
if (variable instanceof FieldNode) {
checkOrMarkPrivateAccess(vexp, (FieldNode) variable, isLHSOfEnclosingAssignment(vexp));
return getType((FieldNode) variable);
}
if (variable != null && variable != vexp && variable instanceof VariableExpression) {
return getType((Expression) variable);
}
if (variable instanceof Parameter) {
Parameter parameter = (Parameter) variable;
ClassNode type = typeCheckingContext.controlStructureVariables.get(parameter);
TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
ClassNode[] closureParamTypes = (ClassNode[]) (enclosingClosure != null ? enclosingClosure.getClosureExpression().getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS) : null);
if (type == null && enclosingClosure != null && "it".equals(variable.getName()) && closureParamTypes != null) {
final Parameter[] parameters = enclosingClosure.getClosureExpression().getParameters();
if (parameters.length == 0 && getTemporaryTypesForExpression(vexp) == null) {
type = closureParamTypes[0];
}
}
if (type != null) {
storeType((VariableExpression) exp, type);
return type;
}
}
}
if (exp instanceof ListExpression) {
return inferListExpressionType((ListExpression) exp);
} else if (exp instanceof MapExpression) {
return inferMapExpressionType((MapExpression) exp);
}
if (exp instanceof ConstructorCallExpression) {
return ((ConstructorCallExpression) exp).getType();
}
if (exp instanceof MethodNode) {
if ((exp == GET_DELEGATE || exp == GET_OWNER || exp == GET_THISOBJECT) && typeCheckingContext.getEnclosingClosure() != null) {
return typeCheckingContext.getEnclosingClassNode();
}
ClassNode ret = getInferredReturnType(exp);
return ret != null ? ret : ((MethodNode) exp).getReturnType();
}
if (exp instanceof ClosureExpression) {
ClassNode irt = getInferredReturnType(exp);
if (irt != null) {
irt = wrapTypeIfNecessary(irt);
ClassNode result = CLOSURE_TYPE.getPlainNodeReference();
result.setGenericsTypes(new GenericsType[] { new GenericsType(irt) });
return result;
}
}
if (exp instanceof RangeExpression) {
ClassNode plain = ClassHelper.RANGE_TYPE.getPlainNodeReference();
RangeExpression re = (RangeExpression) exp;
ClassNode fromType = getType(re.getFrom());
ClassNode toType = getType(re.getTo());
if (fromType.equals(toType)) {
plain.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(fromType)) });
} else {
plain.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(lowestUpperBound(fromType, toType))) });
}
return plain;
}
if (exp instanceof UnaryPlusExpression) {
return getType(((UnaryPlusExpression) exp).getExpression());
}
if (exp instanceof UnaryMinusExpression) {
return getType(((UnaryMinusExpression) exp).getExpression());
}
if (exp instanceof BitwiseNegationExpression) {
return getType(((BitwiseNegationExpression) exp).getExpression());
}
if (exp instanceof MethodCall) {
MethodNode target = (MethodNode) exp.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
if (target != null) {
return getType(target);
}
}
if (exp instanceof Parameter) {
return ((Parameter) exp).getOriginType();
}
if (exp instanceof FieldNode) {
FieldNode fn = (FieldNode) exp;
return getGenericsResolvedTypeOfFieldOrProperty(fn, fn.getOriginType());
}
if (exp instanceof PropertyNode) {
PropertyNode pn = (PropertyNode) exp;
return getGenericsResolvedTypeOfFieldOrProperty(pn, pn.getOriginType());
}
return exp instanceof VariableExpression ? ((VariableExpression) exp).getOriginType() : ((Expression) exp).getType();
}
use of org.codehaus.groovy.ast.Variable in project groovy by apache.
the class StaticTypeCheckingSupport method isTraitSelf.
public static ClassNode isTraitSelf(VariableExpression vexp) {
if (Traits.THIS_OBJECT.equals(vexp.getName())) {
Variable accessedVariable = vexp.getAccessedVariable();
ClassNode type = accessedVariable != null ? accessedVariable.getType() : null;
if (accessedVariable instanceof Parameter && Traits.isTrait(type)) {
return type;
}
}
return null;
}
Aggregations