| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.netbeans.modules.refactoring.java; |
| |
| import com.sun.source.tree.ImportTree; |
| import com.sun.source.tree.ArrayAccessTree; |
| import com.sun.source.tree.AssertTree; |
| import com.sun.source.tree.AssignmentTree; |
| import com.sun.source.tree.BinaryTree; |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.ConditionalExpressionTree; |
| import com.sun.source.tree.DoWhileLoopTree; |
| import com.sun.source.tree.EnhancedForLoopTree; |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.ForLoopTree; |
| import com.sun.source.tree.IdentifierTree; |
| import com.sun.source.tree.IfTree; |
| import com.sun.source.tree.InstanceOfTree; |
| import com.sun.source.tree.MemberSelectTree; |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.NewArrayTree; |
| import com.sun.source.tree.NewClassTree; |
| import com.sun.source.tree.ParameterizedTypeTree; |
| import com.sun.source.tree.ParenthesizedTree; |
| import com.sun.source.tree.ReturnTree; |
| import com.sun.source.tree.Scope; |
| import com.sun.source.tree.SwitchTree; |
| import com.sun.source.tree.SynchronizedTree; |
| import com.sun.source.tree.ThrowTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.tree.Tree.Kind; |
| import com.sun.source.tree.TypeParameterTree; |
| import com.sun.source.tree.UnaryTree; |
| import com.sun.source.tree.VariableTree; |
| import com.sun.source.tree.WhileLoopTree; |
| import com.sun.source.util.SourcePositions; |
| import com.sun.source.util.TreePath; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.ArrayType; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.ExecutableType; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.type.TypeVariable; |
| import javax.lang.model.util.ElementFilter; |
| import javax.swing.text.BadLocationException; |
| import javax.swing.text.Document; |
| import org.netbeans.api.annotations.common.NonNull; |
| import org.netbeans.api.java.source.CompilationInfo; |
| import org.netbeans.api.java.source.TreeUtilities; |
| import org.netbeans.modules.refactoring.java.plugins.JavaPluginUtils; |
| |
| /** |
| * Copy of org.netbeans.modules.java.hints.errors.CreateElementUtilities |
| * @author Jan Lahoda |
| */ |
| public final class CreateElementUtilities { |
| |
| private CreateElementUtilities() {} |
| |
| public static List<? extends TypeMirror> resolveType(Set<ElementKind> types, CompilationInfo info, TreePath currentPath, Tree unresolved, int offset, TypeMirror[] typeParameterBound, int[] numTypeParameters) { |
| switch (currentPath.getLeaf().getKind()) { |
| case METHOD: |
| return computeMethod(types, info, currentPath, typeParameterBound ,unresolved, offset); |
| case MEMBER_SELECT: |
| return computeMemberSelect(types, info, currentPath, unresolved, offset); |
| case ASSIGNMENT: |
| return computeAssignment(types, info, currentPath, unresolved, offset); |
| case ENHANCED_FOR_LOOP: |
| return computeEnhancedForLoop(types, info, currentPath, unresolved, offset); |
| case ARRAY_ACCESS: |
| return computeArrayAccess(types, info, currentPath, unresolved, offset); |
| case VARIABLE: |
| return computeVariableDeclaration(types, info, currentPath, unresolved, offset); |
| case ASSERT: |
| return computeAssert(types, info, currentPath, unresolved, offset); |
| case PARENTHESIZED: |
| return computeParenthesis(types, info, currentPath, unresolved, offset); |
| case DO_WHILE_LOOP: |
| return computePrimitiveType(types, info, ((DoWhileLoopTree) currentPath.getLeaf()).getCondition(), unresolved, TypeKind.BOOLEAN); |
| case FOR_LOOP: |
| return computePrimitiveType(types, info, ((ForLoopTree) currentPath.getLeaf()).getCondition(), unresolved, TypeKind.BOOLEAN); |
| case IF: |
| return computePrimitiveType(types, info, ((IfTree) currentPath.getLeaf()).getCondition(), unresolved, TypeKind.BOOLEAN); |
| case WHILE_LOOP: |
| return computePrimitiveType(types, info, ((WhileLoopTree) currentPath.getLeaf()).getCondition(), unresolved, TypeKind.BOOLEAN); |
| case SYNCHRONIZED: |
| return computeReferenceType(types, info, ((SynchronizedTree) currentPath.getLeaf()).getExpression(), unresolved, "java.lang.Object"); |
| case THROW: |
| return computeReferenceType(types, info, ((ThrowTree) currentPath.getLeaf()).getExpression(), unresolved, "java.lang.Exception"); |
| case INSTANCE_OF: |
| return computeReferenceType(types, info, ((InstanceOfTree) currentPath.getLeaf()).getExpression(), unresolved, "java.lang.Object"); |
| case SWITCH: |
| //TODO: should consider also values in the cases?: |
| return computePrimitiveType(types, info, ((SwitchTree) currentPath.getLeaf()).getExpression(), unresolved, TypeKind.INT); |
| case EXPRESSION_STATEMENT: |
| return Collections.singletonList(info.getTypes().getNoType(TypeKind.VOID)); |
| |
| case RETURN: |
| return computeReturn(types, info, currentPath, unresolved, offset); |
| case TYPE_PARAMETER: |
| return computeTypeParameter(types, info, currentPath, unresolved, offset); |
| case PARAMETERIZED_TYPE: |
| return computeParametrizedType(types, info, currentPath, unresolved, offset, typeParameterBound, numTypeParameters); |
| case ANNOTATION_TYPE: |
| case CLASS: |
| case ENUM: |
| case INTERFACE: |
| return computeClass(types, info, currentPath, unresolved, offset); |
| |
| case CONDITIONAL_EXPRESSION: |
| return computeConditionalExpression(types, info, currentPath, unresolved, offset); |
| |
| case NEW_ARRAY: |
| return computeNewArray(types, info, currentPath, unresolved, offset); |
| |
| case METHOD_INVOCATION: |
| return computeMethodInvocation(types, info, currentPath, unresolved, offset); |
| |
| case NEW_CLASS: |
| return computeNewClass(types, info, currentPath, unresolved, offset); |
| |
| case POSTFIX_INCREMENT: |
| case POSTFIX_DECREMENT: |
| case PREFIX_INCREMENT: |
| case PREFIX_DECREMENT: |
| case UNARY_PLUS: |
| case UNARY_MINUS: |
| case BITWISE_COMPLEMENT: |
| case LOGICAL_COMPLEMENT: |
| return computeUnary(types, info, currentPath, unresolved, offset); |
| |
| case MULTIPLY: |
| case DIVIDE: |
| case REMAINDER: |
| case PLUS: |
| case MINUS: |
| case LEFT_SHIFT: |
| case RIGHT_SHIFT: |
| case UNSIGNED_RIGHT_SHIFT: |
| case LESS_THAN: |
| case GREATER_THAN: |
| case LESS_THAN_EQUAL: |
| case GREATER_THAN_EQUAL: |
| case EQUAL_TO: |
| case NOT_EQUAL_TO: |
| case AND: |
| case XOR: |
| case OR: |
| case CONDITIONAL_AND: |
| case CONDITIONAL_OR: |
| return computeBinaryOperator(types, info, currentPath, unresolved, offset); |
| |
| case MULTIPLY_ASSIGNMENT: |
| case DIVIDE_ASSIGNMENT: |
| case REMAINDER_ASSIGNMENT: |
| case PLUS_ASSIGNMENT: |
| case MINUS_ASSIGNMENT: |
| case LEFT_SHIFT_ASSIGNMENT: |
| case RIGHT_SHIFT_ASSIGNMENT: |
| case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: |
| case AND_ASSIGNMENT: |
| case XOR_ASSIGNMENT: |
| case OR_ASSIGNMENT: |
| //XXX: return computeCompoundAssignment(types, info, currentPath, unresolved, offset); |
| return null; |
| |
| case ARRAY_TYPE: |
| return computeArrayType(types, info, currentPath, unresolved, offset); |
| |
| case IMPORT: |
| return computeImport(types, info, currentPath, unresolved, offset); |
| |
| case BLOCK: |
| case BREAK: |
| case CATCH: |
| case COMPILATION_UNIT: |
| case CONTINUE: |
| case IDENTIFIER: |
| case TYPE_CAST: |
| case TRY: |
| case EMPTY_STATEMENT: |
| case PRIMITIVE_TYPE: |
| case LABELED_STATEMENT: |
| case MODIFIERS: |
| case ERRONEOUS: |
| case OTHER: |
| case INT_LITERAL: |
| case LONG_LITERAL: |
| case FLOAT_LITERAL: |
| case DOUBLE_LITERAL: |
| case BOOLEAN_LITERAL: |
| case CHAR_LITERAL: |
| case STRING_LITERAL: |
| case NULL_LITERAL: |
| //ignored: |
| return null; |
| |
| case CASE: |
| case ANNOTATION: |
| case UNBOUNDED_WILDCARD: |
| case EXTENDS_WILDCARD: |
| case SUPER_WILDCARD: |
| //XXX: currently unhandled |
| return null; |
| |
| default: |
| //should not happen unless set of Tree.Kind changes: |
| return null; |
| } |
| } |
| |
| private static List<? extends TypeMirror> computeBinaryOperator(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| BinaryTree bt = (BinaryTree) parent.getLeaf(); |
| TreePath typeToResolve = null; |
| |
| if (bt.getLeftOperand() == error) { |
| typeToResolve = new TreePath(parent, bt.getRightOperand()); |
| } |
| |
| if (bt.getRightOperand() == error) { |
| typeToResolve = new TreePath(parent, bt.getLeftOperand()); |
| } |
| |
| if (typeToResolve != null) { |
| TypeMirror resolvedType = info.getTrees().getTypeMirror(typeToResolve); |
| |
| if (resolvedType != null) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| if (resolvedType.getKind() == TypeKind.ERROR || resolvedType.getKind() == TypeKind.OTHER) { |
| return resolveType(types, info, parent.getParentPath(), bt, offset, null, null); |
| } |
| |
| return Collections.singletonList(resolvedType); |
| } |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeMethod(Set<ElementKind> types, CompilationInfo info, TreePath parent, TypeMirror[] typeParameterBound, Tree error, int offset) { |
| //class or field: |
| //check the error is in the body: |
| //#92419: check for abstract method/method without body: |
| MethodTree mt = (MethodTree) parent.getLeaf(); |
| |
| if (mt.getReturnType() == error) { |
| types.add(ElementKind.CLASS); |
| types.add(ElementKind.INTERFACE); |
| types.add(ElementKind.ENUM); |
| } |
| |
| List<? extends ExpressionTree> throwList = mt.getThrows(); |
| if (throwList != null && !throwList.isEmpty()) { |
| for (ExpressionTree t : throwList) { |
| if (t == error) { |
| types.add(ElementKind.CLASS); |
| typeParameterBound[0] = info.getElements().getTypeElement("java.lang.Exception").asType(); |
| break; |
| } |
| } |
| } |
| |
| if (mt.getBody() == null) { |
| return null; |
| } |
| |
| try { |
| Document doc = info.getDocument(); |
| |
| if (doc != null) {//XXX |
| int bodyStart = findBodyStart(parent.getLeaf(), info.getCompilationUnit(), info.getTrees().getSourcePositions(), doc); |
| int bodyEnd = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), parent.getLeaf()); |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| if (bodyStart <= offset && offset <= bodyEnd) |
| return Collections.singletonList(info.getElements().getTypeElement("java.lang.Object").asType()); |
| } |
| } catch (IOException ex) { |
| Logger.getLogger("global").log(Level.INFO, ex.getMessage(), ex); |
| } |
| |
| return null; |
| } |
| |
| private static int findBodyStartImpl(Tree cltree, CompilationUnitTree cu, SourcePositions positions, Document doc) { |
| int start = (int)positions.getStartPosition(cu, cltree); |
| int end = (int)positions.getEndPosition(cu, cltree); |
| |
| if (start == (-1) || end == (-1)) { |
| return -1; |
| } |
| |
| if (start > doc.getLength() || end > doc.getLength()) { |
| return (-1); |
| } |
| |
| try { |
| String text = doc.getText(start, end - start); |
| |
| int index = text.indexOf('{'); |
| |
| if (index == (-1)) { |
| return -1; |
| // throw new IllegalStateException("Should NEVER happen."); |
| } |
| |
| return start + index; |
| } catch (BadLocationException e) { |
| LOG.log(Level.INFO, null, e); |
| } |
| |
| return (-1); |
| } |
| private static final Logger LOG = Logger.getLogger(CreateElementUtilities.class.getName()); |
| |
| public static int findBodyStart(final Tree cltree, final CompilationUnitTree cu, final SourcePositions positions, final Document doc) { |
| Kind kind = cltree.getKind(); |
| if (!TreeUtilities.CLASS_TREE_KINDS.contains(kind) && kind != Kind.METHOD) |
| throw new IllegalArgumentException("Unsupported kind: "+ kind); |
| final int[] result = new int[1]; |
| |
| doc.render(new Runnable() { |
| public void run() { |
| result[0] = findBodyStartImpl(cltree, cu, positions, doc); |
| } |
| }); |
| |
| return result[0]; |
| } |
| |
| private static List<? extends TypeMirror> computeMemberSelect(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| //class or field: |
| MemberSelectTree ms = (MemberSelectTree) parent.getLeaf(); |
| final TypeElement jlObject = info.getElements().getTypeElement("java.lang.Object"); |
| |
| if ( jlObject != null //may happen if the platform is broken |
| && !"class".equals(ms.getIdentifier().toString())) {//we obviously should not propose "Create Field" for unknown.class: |
| types.add(ElementKind.FIELD); |
| types.add(ElementKind.CLASS); |
| return Collections.singletonList(jlObject.asType()); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * |
| * @param info context {@link CompilationInfo} |
| * @param iterable tested {@link TreePath} |
| * @return generic type of an {@link Iterable} or {@link ArrayType} at a TreePath |
| */ |
| public static TypeMirror getIterableGenericType(CompilationInfo info, TreePath iterable) { |
| TypeElement iterableElement = info.getElements().getTypeElement("java.lang.Iterable"); //NOI18N |
| if (iterableElement == null) { |
| return null; |
| } |
| TypeMirror iterableType = info.getTrees().getTypeMirror(iterable); |
| if (iterableType == null) { |
| return null; |
| } |
| TypeMirror designedType = null; |
| if (iterableType.getKind() == TypeKind.DECLARED) { |
| DeclaredType declaredType = (DeclaredType) iterableType; |
| if (!info.getTypes().isSubtype(info.getTypes().erasure(declaredType), info.getTypes().erasure(iterableElement.asType()))) { |
| return null; |
| } |
| ExecutableElement iteratorMethod = (ExecutableElement) iterableElement.getEnclosedElements().get(0); |
| ExecutableType iteratorMethodType = (ExecutableType) info.getTypes().asMemberOf(declaredType, iteratorMethod); |
| List<? extends TypeMirror> typeArguments = ((DeclaredType) iteratorMethodType.getReturnType()).getTypeArguments(); |
| if (!typeArguments.isEmpty()) { |
| designedType = typeArguments.get(0); |
| } else { |
| TypeElement jlObject = info.getElements().getTypeElement("java.lang.Object"); |
| |
| if (jlObject != null) { |
| designedType = jlObject.asType(); |
| } |
| } |
| } else if (iterableType.getKind() == TypeKind.ARRAY) { |
| designedType = ((ArrayType) iterableType).getComponentType(); |
| } |
| if (designedType == null) { |
| return null; |
| } |
| return JavaPluginUtils.resolveCapturedType(info, designedType); |
| } |
| |
| private static List<? extends TypeMirror> computeAssignment(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| AssignmentTree at = (AssignmentTree) parent.getLeaf(); |
| TypeMirror type = null; |
| |
| if (at.getVariable() == error) { |
| type = info.getTrees().getTypeMirror(new TreePath(parent, at.getExpression())); |
| |
| if (type != null) { |
| //anonymous class? |
| type = JavaPluginUtils.convertIfAnonymous(type); |
| |
| if (type.getKind() == TypeKind.EXECUTABLE) { |
| //TODO: does not actualy work, attempt to solve situations like: |
| //t = Collections.emptyList() |
| //t = Collections.<String>emptyList(); |
| //see also testCreateFieldMethod1 and testCreateFieldMethod2 tests: |
| type = ((ExecutableType) type).getReturnType(); |
| } |
| } |
| } |
| |
| if (at.getExpression() == error) { |
| type = info.getTrees().getTypeMirror(new TreePath(parent, at.getVariable())); |
| } |
| |
| //class or field: |
| if (type == null) { |
| return null; |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(type); |
| } |
| |
| private static List<? extends TypeMirror> computeEnhancedForLoop(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| EnhancedForLoopTree efl = (EnhancedForLoopTree) parent.getLeaf(); |
| |
| if (efl.getExpression() != error) { |
| return null; |
| } |
| |
| TypeMirror argument = info.getTrees().getTypeMirror(new TreePath(new TreePath(parent, efl.getVariable()), efl.getVariable().getType())); |
| |
| if (argument == null) |
| return null; |
| |
| if (argument.getKind().isPrimitive()) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getArrayType(argument)); |
| } |
| |
| TypeElement iterable = info.getElements().getTypeElement("java.lang.Iterable"); //NOI18N |
| if (iterable == null) { |
| return null; |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getDeclaredType(iterable, argument)); |
| } |
| |
| private static List<? extends TypeMirror> computeArrayAccess(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ArrayAccessTree aat = (ArrayAccessTree) parent.getLeaf(); |
| |
| if (aat.getExpression() == error) { |
| TreePath parentParent = parent.getParentPath(); |
| List<? extends TypeMirror> upperTypes = resolveType(types, info, parentParent, aat, offset, null, null); |
| |
| if (upperTypes == null) { |
| return null; |
| } |
| |
| List<TypeMirror> arrayTypes = new ArrayList<TypeMirror>(); |
| |
| for (TypeMirror tm : upperTypes) { |
| if (tm == null) |
| continue; |
| switch (tm.getKind()) { |
| case VOID: |
| case EXECUTABLE: |
| case WILDCARD: |
| case PACKAGE: |
| continue; |
| } |
| |
| arrayTypes.add(info.getTypes().getArrayType(tm)); |
| } |
| |
| if (arrayTypes.isEmpty()) |
| return null; |
| |
| return arrayTypes; |
| } |
| |
| if (aat.getIndex() == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getPrimitiveType(TypeKind.INT)); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeVariableDeclaration(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| VariableTree vt = (VariableTree) parent.getLeaf(); |
| |
| if (vt.getInitializer() == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTrees().getTypeMirror(new TreePath(parent, vt.getType()))); |
| } |
| |
| TreePath context = parent.getParentPath(); |
| if (vt.getType() != error || context == null) { |
| return null; |
| } |
| |
| switch (context.getLeaf().getKind()) { |
| case ENHANCED_FOR_LOOP: |
| ExpressionTree iterableTree = ((EnhancedForLoopTree) context.getLeaf()).getExpression(); |
| TreePath iterablePath = new TreePath(context, iterableTree); |
| TypeMirror type = getIterableGenericType(info, iterablePath); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| return Collections.singletonList(type); |
| default: |
| types.add(ElementKind.CLASS); |
| return Collections.<TypeMirror>emptyList(); |
| } |
| } |
| |
| private static List<? extends TypeMirror> computeAssert(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| AssertTree at = (AssertTree) parent.getLeaf(); |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| if (at.getCondition() == error) { |
| return Collections.singletonList(info.getTypes().getPrimitiveType(TypeKind.BOOLEAN)); |
| } |
| |
| if (at.getDetail() == error) { |
| return Collections.singletonList(info.getElements().getTypeElement("java.lang.Object").asType()); |
| } |
| |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeParenthesis(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ParenthesizedTree pt = (ParenthesizedTree) parent.getLeaf(); |
| |
| if (pt.getExpression() != error) { |
| return null; |
| } |
| |
| TreePath parentParent = parent.getParentPath(); |
| List<? extends TypeMirror> upperTypes = resolveType(types, info, parentParent, pt, offset, null, null); |
| |
| if (upperTypes == null) { |
| return null; |
| } |
| |
| return upperTypes; |
| } |
| |
| private static List<? extends TypeMirror> computeConditionalExpression(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ConditionalExpressionTree cet = (ConditionalExpressionTree) parent.getLeaf(); |
| |
| if (cet.getCondition() == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getPrimitiveType(TypeKind.BOOLEAN)); |
| } |
| |
| if (cet.getTrueExpression() == error || cet.getFalseExpression() == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return resolveType(types, info, parent.getParentPath(), cet, offset, null, null); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computePrimitiveType(Set<ElementKind> types, CompilationInfo info, Tree expression, Tree error, TypeKind kind) { |
| if (expression == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getPrimitiveType(kind)); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeReferenceType(Set<ElementKind> types, CompilationInfo info, Tree expression, Tree error, String type) { |
| if (expression == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getElements().getTypeElement(type).asType()); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeImport(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ImportTree tree = (ImportTree) parent.getLeaf(); |
| |
| if (tree.getQualifiedIdentifier() == error) { |
| types.add(ElementKind.ANNOTATION_TYPE); |
| types.add(ElementKind.CLASS); |
| types.add(ElementKind.ENUM); |
| types.add(ElementKind.INTERFACE); |
| |
| return Collections.singletonList(info.getElements().getTypeElement("java.lang.Object").asType()); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeUnary(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| UnaryTree tree = (UnaryTree) parent.getLeaf(); |
| |
| if (tree.getExpression() == error) { |
| List<? extends TypeMirror> parentTypes = resolveType(types, info, parent.getParentPath(), tree, offset, null, null); |
| |
| if (parentTypes != null) { |
| //may contain only "void", ignore: |
| if (parentTypes.size() != 1) { |
| return parentTypes; |
| } |
| if (parentTypes.get(0).getKind() != TypeKind.VOID) { |
| return parentTypes; |
| } |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getPrimitiveType(TypeKind.INT)); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeReturn(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ReturnTree rt = (ReturnTree) parent.getLeaf(); |
| |
| if (rt.getExpression() == error) { |
| TreePath method = findMethod(parent); |
| |
| if (method == null) { |
| return null; |
| } |
| |
| Element el = info.getTrees().getElement(method); |
| |
| if (el == null || el.getKind() != ElementKind.METHOD) { |
| return null; |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(((ExecutableElement) el).getReturnType()); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeTypeParameter(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| TypeParameterTree tpt = (TypeParameterTree) parent.getLeaf(); |
| |
| for (Tree t : tpt.getBounds()) { |
| if (t == error) { |
| types.add(ElementKind.CLASS); //XXX: class/interface/enum/annotation? |
| return null; |
| } |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeParametrizedType(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset, TypeMirror[] typeParameterBound, int[] numTypeParameters) { |
| ParameterizedTypeTree ptt = (ParameterizedTypeTree) parent.getLeaf(); |
| |
| if (ptt.getType() == error) { |
| Tree gpt = parent.getParentPath().getLeaf(); |
| if (TreeUtilities.CLASS_TREE_KINDS.contains(gpt.getKind()) && ((ClassTree)gpt).getExtendsClause() == ptt) { |
| types.add(ElementKind.CLASS); |
| } else if (TreeUtilities.CLASS_TREE_KINDS.contains(gpt.getKind()) && ((ClassTree)gpt).getImplementsClause().contains(ptt)) { |
| types.add(ElementKind.INTERFACE); |
| } else { |
| types.add(ElementKind.CLASS); |
| types.add(ElementKind.INTERFACE); |
| } |
| |
| if (numTypeParameters != null) { |
| numTypeParameters[0] = ptt.getTypeArguments().size(); |
| } |
| return null; |
| } |
| |
| TypeMirror resolved = info.getTrees().getTypeMirror(parent); |
| DeclaredType resolvedDT = null; |
| |
| if (resolved != null && resolved.getKind() == TypeKind.DECLARED) { |
| resolvedDT = (DeclaredType) resolved; |
| } |
| |
| int index = 0; |
| |
| for (Tree t : ptt.getTypeArguments()) { |
| if (t == error) { |
| if (resolvedDT != null && typeParameterBound != null) { |
| List<? extends TypeMirror> typeArguments = ((DeclaredType) resolvedDT.asElement().asType()).getTypeArguments(); |
| |
| if (typeArguments.size() > index) { |
| typeParameterBound[0] = ((TypeVariable) typeArguments.get(index)).getUpperBound(); |
| } |
| } |
| |
| types.add(ElementKind.CLASS); //XXX: class/interface/enum/annotation? |
| return null; |
| } |
| |
| index++; |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeClass(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| ClassTree ct = (ClassTree) parent.getLeaf(); |
| |
| if (ct.getExtendsClause() == error) { |
| types.add(ElementKind.CLASS); |
| return null; |
| } |
| |
| for (Tree t : ct.getImplementsClause()) { |
| if (t == error) { |
| types.add(ElementKind.INTERFACE); |
| return null; |
| } |
| } |
| |
| //XXX: annotation types... |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeNewArray(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| NewArrayTree nat = (NewArrayTree) parent.getLeaf(); |
| |
| if (nat.getType() == error) { |
| types.add(ElementKind.CLASS); |
| types.add(ElementKind.ENUM); |
| types.add(ElementKind.INTERFACE); |
| |
| return null; |
| } |
| |
| for (Tree dimension : nat.getDimensions()) { |
| if (dimension == error) { |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(info.getTypes().getPrimitiveType(TypeKind.INT)); |
| } |
| } |
| |
| for (Tree init : nat.getInitializers()) { |
| if (init == error) { |
| TypeMirror whole = info.getTrees().getTypeMirror(parent); |
| |
| if (whole == null || whole.getKind() != TypeKind.ARRAY) |
| return null; |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return Collections.singletonList(((ArrayType) whole).getComponentType()); |
| } |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeMethodInvocation(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| MethodInvocationTree nat = (MethodInvocationTree) parent.getLeaf(); |
| boolean errorInRealArguments = false; |
| |
| for (Tree param : nat.getArguments()) { |
| errorInRealArguments |= param == error; |
| } |
| |
| if (errorInRealArguments) { |
| List<TypeMirror> proposedTypes = new ArrayList<TypeMirror>(); |
| int[] proposedIndex = new int[1]; |
| List<ExecutableElement> ee = fuzzyResolveMethodInvocation(info, parent, proposedTypes, proposedIndex); |
| |
| if (ee.isEmpty()) { //cannot be resolved |
| return null; |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return proposedTypes; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @since 2.12 |
| */ |
| public static @NonNull List<ExecutableElement> fuzzyResolveMethodInvocation(CompilationInfo info, TreePath path, List<TypeMirror> proposed, int[] index) { |
| assert path.getLeaf().getKind() == Kind.METHOD_INVOCATION || path.getLeaf().getKind() == Kind.NEW_CLASS; |
| |
| if (path.getLeaf().getKind() == Kind.METHOD_INVOCATION) { |
| List<TypeMirror> actualTypes = new LinkedList<TypeMirror>(); |
| MethodInvocationTree mit = (MethodInvocationTree) path.getLeaf(); |
| |
| for (Tree a : mit.getArguments()) { |
| TreePath tp = new TreePath(path, a); |
| actualTypes.add(info.getTrees().getTypeMirror(tp)); |
| } |
| |
| String methodName; |
| TypeMirror on; |
| |
| switch (mit.getMethodSelect().getKind()) { |
| case IDENTIFIER: |
| Scope s = info.getTrees().getScope(path); |
| TypeElement enclosingClass = s.getEnclosingClass(); |
| on = enclosingClass != null ? enclosingClass.asType() : null; |
| methodName = ((IdentifierTree) mit.getMethodSelect()).getName().toString(); |
| break; |
| case MEMBER_SELECT: |
| on = info.getTrees().getTypeMirror(new TreePath(path, ((MemberSelectTree) mit.getMethodSelect()).getExpression())); |
| methodName = ((MemberSelectTree) mit.getMethodSelect()).getIdentifier().toString(); |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| |
| if (on == null || on.getKind() != TypeKind.DECLARED) { |
| return Collections.emptyList(); |
| } |
| |
| return resolveMethod(info, actualTypes, (DeclaredType) on, false, false, methodName, proposed, index); |
| } |
| |
| if (path.getLeaf().getKind() == Kind.NEW_CLASS) { |
| List<TypeMirror> actualTypes = new LinkedList<TypeMirror>(); |
| NewClassTree nct = (NewClassTree) path.getLeaf(); |
| |
| for (Tree a : nct.getArguments()) { |
| TreePath tp = new TreePath(path, a); |
| actualTypes.add(info.getTrees().getTypeMirror(tp)); |
| } |
| |
| TypeMirror on = info.getTrees().getTypeMirror(new TreePath(path, nct.getIdentifier())); |
| |
| if (on == null || on.getKind() != TypeKind.DECLARED) { |
| return Collections.emptyList(); |
| } |
| |
| return resolveMethod(info, actualTypes, (DeclaredType) on, false, true, null, proposed, index); |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| private static List<ExecutableElement> resolveMethod(CompilationInfo info, List<TypeMirror> foundTypes, DeclaredType on, boolean statik, boolean constr, String name, List<TypeMirror> candidateTypes, int[] index) { |
| if (on.asElement() == null) return Collections.emptyList(); |
| |
| List<ExecutableElement> found = new LinkedList<ExecutableElement>(); |
| |
| OUTER: |
| for (ExecutableElement ee : execsIn(info, (TypeElement) on.asElement(), constr, name)) { |
| TypeMirror currType = ((TypeElement) ee.getEnclosingElement()).asType(); |
| if (!info.getTypes().isSubtype(on, currType) && !on.asElement().equals(((DeclaredType)currType).asElement())) //XXX: fix for #132627, a clearer fix may exist |
| continue; |
| if (ee.getParameters().size() == foundTypes.size() /*XXX: variable arg count*/) { |
| TypeMirror innerCandidate = null; |
| int innerIndex = -1; |
| ExecutableType et = (ExecutableType) info.getTypes().asMemberOf(on, ee); |
| Iterator<? extends TypeMirror> formal = et.getParameterTypes().iterator(); |
| Iterator<? extends TypeMirror> actual = foundTypes.iterator(); |
| boolean mismatchFound = false; |
| int i = 0; |
| |
| while (formal.hasNext() && actual.hasNext()) { |
| TypeMirror currentFormal = formal.next(); |
| TypeMirror currentActual = actual.next(); |
| |
| if (!info.getTypes().isAssignable(currentActual, currentFormal) || currentActual.getKind() == TypeKind.ERROR) { |
| if (mismatchFound) { |
| //only one mismatch supported: |
| continue OUTER; |
| } |
| mismatchFound = true; |
| innerCandidate = currentFormal; |
| innerIndex = i; |
| } |
| |
| i++; |
| } |
| |
| if (mismatchFound) { |
| if (candidateTypes.isEmpty()) { |
| index[0] = innerIndex; |
| candidateTypes.add(innerCandidate); |
| found.add(ee); |
| } else { |
| //see testFuzzyResolveConstructor2: |
| if (index[0] == innerIndex) { |
| boolean add = true; |
| for (TypeMirror tm : candidateTypes) { |
| if (info.getTypes().isSameType(tm, innerCandidate)) { |
| add = false; |
| break; |
| } |
| } |
| if (add) { |
| candidateTypes.add(innerCandidate); |
| found.add(ee); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return found; |
| } |
| |
| private static Iterable<ExecutableElement> execsIn(CompilationInfo info, TypeElement e, boolean constr, String name) { |
| if (constr) { |
| return ElementFilter.constructorsIn(info.getElements().getAllMembers(e)); |
| } |
| |
| List<ExecutableElement> result = new LinkedList<ExecutableElement>(); |
| |
| for (ExecutableElement ee : ElementFilter.methodsIn(info.getElements().getAllMembers(e))) { |
| if (name.equals(ee.getSimpleName().toString())) { |
| result.add(ee); |
| } |
| } |
| |
| return result; |
| } |
| |
| private static List<? extends TypeMirror> computeNewClass(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| NewClassTree nct = (NewClassTree) parent.getLeaf(); |
| boolean errorInRealArguments = false; |
| |
| for (Tree param : nct.getArguments()) { |
| errorInRealArguments |= param == error; |
| } |
| |
| if (errorInRealArguments) { |
| List<TypeMirror> proposedTypes = new ArrayList<TypeMirror>(); |
| int[] proposedIndex = new int[1]; |
| List<ExecutableElement> ee = fuzzyResolveMethodInvocation(info, parent, proposedTypes, proposedIndex); |
| |
| if (ee.isEmpty()) { //cannot be resolved |
| return null; |
| } |
| |
| types.add(ElementKind.PARAMETER); |
| types.add(ElementKind.LOCAL_VARIABLE); |
| types.add(ElementKind.FIELD); |
| |
| return proposedTypes; |
| } |
| |
| Tree id = nct.getIdentifier(); |
| |
| if (id.getKind() == Kind.PARAMETERIZED_TYPE) { |
| id = ((ParameterizedTypeTree) id).getType(); |
| } |
| |
| if (id == error) { |
| return resolveType(EnumSet.noneOf(ElementKind.class), info, parent.getParentPath(), nct, offset, null, null); |
| } |
| |
| return null; |
| } |
| |
| private static List<? extends TypeMirror> computeArrayType(Set<ElementKind> types, CompilationInfo info, TreePath parent, Tree error, int offset) { |
| types.add(ElementKind.CLASS); |
| |
| return Collections.singletonList(info.getElements().getTypeElement("java.lang.Object").asType()); |
| } |
| |
| private static final Set<Kind> STOP_LOOKING_FOR_METHOD = EnumSet.of(Kind.METHOD, Kind.ANNOTATION_TYPE, Kind.CLASS, Kind.ENUM, Kind.INTERFACE, Kind.COMPILATION_UNIT); |
| |
| private static TreePath findMethod(TreePath tp) { |
| while (!STOP_LOOKING_FOR_METHOD.contains(tp.getLeaf().getKind())) { |
| tp = tp.getParentPath(); |
| } |
| |
| if (tp.getLeaf().getKind() == Kind.METHOD) { |
| return tp; |
| } |
| |
| return null; |
| } |
| |
| } |