in java/java.hints/src/org/netbeans/modules/java/hints/errors/CreateElement.java [148:505]
private static List<Fix> analyzeImpl(CompilationInfo info, String diagnosticKey, int offset) throws IOException {
if ("compiler.err.invalid.mref".equals(diagnosticKey)) {
return computeMissingMemberRefFixes(info, offset);
}
TreePath errorPath = ErrorHintsProvider.findUnresolvedElement(info, offset);
if (errorPath == null) {
return Collections.<Fix>emptyList();
}
if (CAST_KEY.equals(diagnosticKey) && errorPath.getParentPath() != null && errorPath.getParentPath().getLeaf().getKind() == Kind.METHOD_INVOCATION) {
MethodInvocationTree mit = (MethodInvocationTree) errorPath.getParentPath().getLeaf();
errorPath = new TreePath(errorPath.getParentPath(), mit.getMethodSelect());
offset = (int) info.getTrees().getSourcePositions().getStartPosition(errorPath.getCompilationUnit(), errorPath.getLeaf());
}
if (info.getElements().getTypeElement("java.lang.Object") == null) { // NOI18N
// broken java platform
return Collections.<Fix>emptyList();
}
TreePath parent = null;
TreePath firstClass = null;
TreePath firstMethod = null;
TreePath firstVar = null;
TreePath firstInitializer = null;
TreePath methodInvocation = null;
TreePath newClass = null;
boolean baseType = false;
boolean lookupMethodInvocation = true;
boolean lookupNCT = true;
TreePath path = info.getTreeUtilities().pathFor(Math.max((int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), errorPath.getLeaf()), offset) + 1);
while(path != null) {
Tree leaf = path.getLeaf();
Kind leafKind = leaf.getKind();
if (!baseType && TreeUtilities.CLASS_TREE_KINDS.contains(leafKind) && parent != null && (((ClassTree)leaf).getExtendsClause() == parent.getLeaf() || ((ClassTree)leaf).getImplementsClause().contains(parent.getLeaf())))
baseType = true;
if (parent != null && parent.getLeaf() == errorPath.getLeaf())
parent = path;
if (leaf == errorPath.getLeaf() && parent == null)
parent = path;
if (TreeUtilities.CLASS_TREE_KINDS.contains(leafKind) && firstClass == null)
firstClass = path;
if (leafKind == Kind.METHOD && firstMethod == null && firstClass == null)
firstMethod = path;
//static/dynamic initializer:
if ( leafKind == Kind.BLOCK && TreeUtilities.CLASS_TREE_KINDS.contains(path.getParentPath().getLeaf().getKind())
&& firstMethod == null && firstClass == null)
firstInitializer = path;
if (leafKind == Kind.ANNOTATION) {
// discard any methods, since there cannot be method calls in anno values
methodInvocation = null;
lookupMethodInvocation = false;
}
if (lookupMethodInvocation && leafKind == Kind.METHOD_INVOCATION) {
methodInvocation = path;
}
if (leafKind == Kind.VARIABLE) {
firstVar = path;
}
if (lookupNCT && leafKind == Kind.NEW_CLASS) {
newClass = path;
}
if (leafKind == Kind.MEMBER_SELECT) {
lookupMethodInvocation = leaf == errorPath.getLeaf();
}
if (leafKind != Kind.MEMBER_SELECT && leafKind != Kind.IDENTIFIER) {
lookupMethodInvocation = false;
}
if (leafKind != Kind.MEMBER_SELECT && leafKind != Kind.IDENTIFIER && leafKind != Kind.PARAMETERIZED_TYPE) {
lookupNCT = false;
}
path = path.getParentPath();
}
if (parent == null || parent.getLeaf() == errorPath.getLeaf())
return Collections.<Fix>emptyList();
Element e = info.getTrees().getElement(errorPath);
if (e == null) {
return Collections.<Fix>emptyList();
}
Set<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
Name name = e.getSimpleName();
if (name == null) {
if (ErrorHintsProvider.ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "e.simpleName=null"); // NOI18N
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "offset=" + offset); // NOI18N
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "errorTree=" + errorPath.getLeaf()); // NOI18N
}
return Collections.<Fix>emptyList();
}
String simpleName = name.toString();
final TypeElement source = firstClass != null ? (TypeElement) info.getTrees().getElement(firstClass) : null;
Element target = null;
boolean wasMemberSelect = false;
if (errorPath.getLeaf().getKind() == Kind.MEMBER_SELECT) {
TreePath exp = new TreePath(errorPath, ((MemberSelectTree) errorPath.getLeaf()).getExpression());
TypeMirror targetType = info.getTrees().getTypeMirror(exp);
if (targetType != null) {
if (targetType.getKind() == TypeKind.DECLARED) {
Element expElement = info.getTrees().getElement(exp);
if (isClassLikeElement(expElement)) {
modifiers.add(Modifier.STATIC);
}
Element targetElement = info.getTypes().asElement(targetType);
if (isClassLikeElement(targetElement)) {
target = (TypeElement) targetElement;
}
} else if (targetType.getKind() == TypeKind.PACKAGE) {
target = info.getTrees().getElement(exp);
}
}
wasMemberSelect = true;
} else {
Element enclosingElement = e.getEnclosingElement();
if(enclosingElement != null && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) //unresolved element inside annot.
target = enclosingElement;
else
if (errorPath.getLeaf().getKind() == Kind.IDENTIFIER) {
//TODO: Handle Annotations
target = source;
if (firstMethod != null) {
if (((MethodTree)firstMethod.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC)) {
modifiers.add(Modifier.STATIC);
}
} else if (firstInitializer != null) {
if (((BlockTree) firstInitializer.getLeaf()).isStatic()) {
modifiers.add(Modifier.STATIC);
}
} else if (firstVar != null && ((VariableTree)firstVar.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC)) {
modifiers.add(Modifier.STATIC);
}
}
}
if (target == null) {
if (ErrorHintsProvider.ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "target=null"); // NOI18N
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "offset=" + offset); // NOI18N
ErrorHintsProvider.ERR.log(ErrorManager.INFORMATIONAL, "errorTree=" + errorPath.getLeaf()); // NOI18N
}
return Collections.<Fix>emptyList();
}
if (target instanceof TypeElement)
modifiers.addAll(Utilities.getAccessModifiers(info, source, (TypeElement) target).getRequiredModifiers());
else
modifiers.add(Modifier.PUBLIC);
List<Fix> result = new ArrayList<Fix>();
if (methodInvocation != null) {
//create method:
MethodInvocationTree mit = (MethodInvocationTree) methodInvocation.getLeaf();
//return type:
Set<ElementKind> fixTypes = EnumSet.noneOf(ElementKind.class);
List<? extends TypeMirror> types = resolveType(fixTypes, info, methodInvocation.getParentPath(), methodInvocation.getLeaf(), offset, null, null);
if (types == null || types.isEmpty()) {
return Collections.<Fix>emptyList();
}
try {
result.addAll(prepareCreateMethodFix(info, methodInvocation, modifiers, (TypeElement) target, simpleName, mit.getArguments(), types));
} catch (IllegalArgumentException ex) {
// FIXME: see issue #243028; EXECUTABLE somehow gets here and causes an exception. Let's log all the necessary info incl. source
LOG.log(Level.INFO, "Unexpected exception, perhaps a type that cannot be converted to a Handle. See issue #243028 for more details. Please attach" +
"the following ide.log to the issue");
LOG.log(Level.INFO, "Caused by source:\n==============\n" +
info.getSnapshot().getText().toString() + "\n==============\n");
LOG.log(Level.INFO, "Caused by error at offset " + offset + ", tree: " + methodInvocation.getLeaf().toString(), ex);
LOG.log(Level.INFO, "Invocation starts at " + info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), methodInvocation.getLeaf()));
throw ex;
}
}
Set<ElementKind> fixTypes = EnumSet.noneOf(ElementKind.class);
TypeMirror[] superType = new TypeMirror[1];
int[] numTypeParameters = new int[1];
List<TypeMirror> types = (List<TypeMirror>)resolveType(fixTypes, info, parent, errorPath.getLeaf(), offset, superType, numTypeParameters);
ElementKind classType = getClassType(fixTypes);
if (!ErrorFixesFakeHint.enabled(info.getFileObject(), FixKind.CREATE_LOCAL_VARIABLE)) {
fixTypes.remove(ElementKind.LOCAL_VARIABLE);
}
final TypeMirror type;
if (types != null && !types.isEmpty()) {
List<TypeMirror> resolvedTypes = null;
int i = 0;
for (Iterator<TypeMirror> it = types.iterator(); it.hasNext(); ) {
final TypeMirror t = it.next();
final TypeMirror resolved = Utilities.resolveTypeForDeclaration(info, t);
if (resolved != t) {
if (resolvedTypes == null) {
resolvedTypes = new ArrayList(types);
types = resolvedTypes;
}
resolvedTypes.set(i, resolved);
}
i++;
}
//XXX: should reasonably consider all the found type candidates, not only the one:
type = types.get(0);
if (superType[0] == null) {
// the type must be already un-captured.
superType[0] = type;
}
} else {
type = null;
}
if (target.getKind() == ElementKind.PACKAGE) {
result.addAll(prepareCreateOuterClassFix(info, null, target, modifiers, simpleName, null, superType[0], classType != null ? classType : ElementKind.CLASS, numTypeParameters[0]));
return result;
}
TypeElement outermostTypeElement = source != null ? info.getElementUtilities().outermostTypeElement(source) : null;
if (newClass != null) {
NewClassTree nct = (NewClassTree) newClass.getLeaf();
Element clazz = info.getTrees().getElement(new TreePath(newClass, nct.getIdentifier()));
if (clazz == null || clazz.asType().getKind() == TypeKind.ERROR || (!clazz.getKind().isClass() && !clazz.getKind().isInterface())) {
//the class does not exist...
ExpressionTree ident = nct.getIdentifier();
int numTypeArguments = 0;
if (ident.getKind() == Kind.PARAMETERIZED_TYPE) {
numTypeArguments = ((ParameterizedTypeTree) ident).getTypeArguments().size();
}
if (wasMemberSelect) {
return prepareCreateInnerClassFix(info, newClass, (TypeElement) target, modifiers, simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments);
} else {
List<Fix> currentResult = new LinkedList<Fix>();
currentResult.addAll(prepareCreateOuterClassFix(info, newClass, source, EnumSet.of(Modifier.PUBLIC), simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments));
if (!baseType || outermostTypeElement != source)
currentResult.addAll(prepareCreateInnerClassFix(info, newClass, outermostTypeElement, EnumSet.of(outermostTypeElement != null && outermostTypeElement.getKind().isInterface() ? Modifier.PUBLIC : Modifier.PRIVATE, Modifier.STATIC), simpleName, nct.getArguments(), type, ElementKind.CLASS, numTypeArguments));
return currentResult;
}
}
if (nct.getClassBody() != null) {
return Collections.<Fix>emptyList();
}
TypeElement clazzTarget = (TypeElement) clazz;
result.addAll(prepareCreateMethodFix(info, newClass, Utilities.getAccessModifiers(info, source, clazzTarget).getRequiredModifiers(), clazzTarget, "<init>", nct.getArguments(), null)); //NOI18N
}
//field like or class (type):
if (classType != null && e.asType().getKind() == TypeKind.ERROR) {
if (wasMemberSelect) {
result.addAll(prepareCreateInnerClassFix(info, null, (TypeElement) target, modifiers, simpleName, null, superType[0], classType, numTypeParameters[0]));
} else {
result.addAll(prepareCreateOuterClassFix(info, null, source, EnumSet.noneOf(Modifier.class), simpleName, null, superType[0], classType, numTypeParameters[0]));
if (!baseType || outermostTypeElement != source)
result.addAll(prepareCreateInnerClassFix(info, null, outermostTypeElement, EnumSet.of(outermostTypeElement != null && outermostTypeElement.getKind().isInterface() ? Modifier.PUBLIC : Modifier.PRIVATE, Modifier.STATIC), simpleName, null, superType[0], classType, numTypeParameters[0]));
}
}
// check if this may be tested above, just after assignment to a type
if (type == null || type.getKind() == TypeKind.VOID || type.getKind() == TypeKind.OTHER || type.getKind() == TypeKind.NONE || type.getKind() == TypeKind.EXECUTABLE) {
return result;
}
//currently, we cannot handle error types:
if (Utilities.containsErrorsRecursively(type)) {
return result;
}
Collection<TypeVariable> typeVars = Utilities.containedTypevarsRecursively(type);
if (!Utilities.allTypeVarsAccessible(typeVars, target)) {
fixTypes.remove(ElementKind.FIELD);
}
if (fixTypes.contains(ElementKind.FIELD) && Utilities.isTargetWritable((TypeElement) target, info)) { //IZ 111048 -- don't offer anything if target file isn't writable
Element enclosingElement = e.getEnclosingElement();
if (enclosingElement != null && enclosingElement.getKind() == ElementKind.ANNOTATION_TYPE) {
// FileObject targetFile = SourceUtils.getFile(target, info.getClasspathInfo());
FileObject targetFile = SourceUtils.getFile(ElementHandle.create(target), info.getClasspathInfo());
if (targetFile != null) {
result.add(new CreateMethodFix(info, simpleName, modifiers, (TypeElement) target, type, types, Collections.<String>emptyList(), Collections.<TypeMirror>emptyList(), Collections.<String>emptyList(), targetFile));
}
return result;
} else {
FileObject targetFile = SourceUtils.getFile(ElementHandle.create(target), info.getClasspathInfo());
if (targetFile != null) {
if (target.getKind() == ElementKind.ENUM) {
if (source != null) { //TODO: maybe create a constant? - but the test below seems very suspicious:
if (source.equals(target)) {
result.add(new CreateFieldFix(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
} else {
result.add(new CreateEnumConstant(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
}
}
} else {
if (firstMethod != null && info.getTrees().getElement(firstMethod).getKind() == ElementKind.CONSTRUCTOR && ErrorFixesFakeHint.isCreateFinalFieldsForCtor(ErrorFixesFakeHint.getPreferences(targetFile, FixKind.CREATE_FINAL_FIELD_CTOR))) {
if (CreateElementUtilities.canDeclareVariableFinal(info, firstMethod, e)) {
modifiers.add(Modifier.FINAL);
}
}
if (ErrorFixesFakeHint.enabled(info.getFileObject(), ErrorFixesFakeHint.FixKind.CREATE_FINAL_FIELD_CTOR)) {
result.add(new CreateFieldFix(info, simpleName, modifiers, (TypeElement) target, type, targetFile));
}
}
}
}
}
if (!wasMemberSelect && (fixTypes.contains(ElementKind.LOCAL_VARIABLE) || fixTypes.contains(ElementKind.PARAMETER) || fixTypes.contains(ElementKind.RESOURCE_VARIABLE))) {
ExecutableElement ee = null;
if (firstMethod != null) {
ee = (ExecutableElement) info.getTrees().getElement(firstMethod);
}
int identifierPos = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), errorPath.getLeaf());
if (ee != null && fixTypes.contains(ElementKind.PARAMETER) && !Utilities.isMethodHeaderInsideGuardedBlock(info, (MethodTree) firstMethod.getLeaf()))
result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.PARAMETER, identifierPos).toEditorFix());
if ((firstMethod != null || firstInitializer != null) && fixTypes.contains(ElementKind.LOCAL_VARIABLE) && ErrorFixesFakeHint.enabled(ErrorFixesFakeHint.FixKind.CREATE_LOCAL_VARIABLE))
result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.LOCAL_VARIABLE, identifierPos).toEditorFix());
if (fixTypes.contains(ElementKind.RESOURCE_VARIABLE))
result.add(new AddParameterOrLocalFix(info, type, simpleName, ElementKind.RESOURCE_VARIABLE, identifierPos).toEditorFix());
}
return result;
}