private static PsiField createField( PsiLocalVariable local, PsiType forcedType, String fieldName, boolean includeInitializer) { @NonNls StringBuilder pattern = new StringBuilder(); pattern.append("private int "); pattern.append(fieldName); if (local.getInitializer() == null) { includeInitializer = false; } if (includeInitializer) { pattern.append("=0"); } pattern.append(";"); final Project project = local.getProject(); PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); try { PsiField field = factory.createFieldFromText(pattern.toString(), null); field.getTypeElement().replace(factory.createTypeElement(forcedType)); if (includeInitializer) { PsiExpression initializer = RefactoringUtil.convertInitializerToNormalExpression( local.getInitializer(), forcedType); field.getInitializer().replace(initializer); } for (PsiAnnotation annotation : local.getModifierList().getAnnotations()) { field.getModifierList().add(annotation.copy()); } return field; } catch (IncorrectOperationException e) { LOG.error(e); return null; } }
@Override public void visitField(PsiField field) { // There is a possible case that more than one field is declared for the same type like 'int i, // j;'. We want to process only // the first one then. PsiElement fieldPrev = getPreviousNonWsComment(field.getPrevSibling(), 0); if (fieldPrev instanceof PsiJavaToken && ((PsiJavaToken) fieldPrev).getTokenType() == JavaTokenType.COMMA) { return; } // There is a possible case that fields which share the same type declaration are located on // different document lines, e.g.: // int i1, // i2; // We want to consider only the first declaration then but need to expand its range to all // affected lines (up to semicolon). TextRange range = field.getTextRange(); PsiElement child = field.getLastChild(); boolean needSpecialProcessing = true; if (isSemicolon(child)) { needSpecialProcessing = false; } else if (child instanceof PsiComment) { // There is a possible field definition like below: // int f; // my comment. // The comment goes into field PSI here, that's why we need to handle it properly. PsiElement prev = getPreviousNonWsComment(child, range.getStartOffset()); needSpecialProcessing = prev != null && !isSemicolon(prev); } if (needSpecialProcessing) { for (PsiElement e = field.getNextSibling(); e != null; e = e.getNextSibling()) { if (e instanceof PsiWhiteSpace || e instanceof PsiComment) { // Skip white space and comment continue; } else if (e instanceof PsiJavaToken) { if (((PsiJavaToken) e).getTokenType() == JavaTokenType.COMMA) { // Skip comma continue; } else { break; } } else if (e instanceof PsiField) { PsiElement c = e.getLastChild(); if (c != null) { c = getPreviousNonWsComment(c, range.getStartOffset()); } // Stop if current field ends by a semicolon. if (c instanceof PsiErrorElement // Incomplete field without trailing semicolon || (c instanceof PsiJavaToken && ((PsiJavaToken) c).getTokenType() == JavaTokenType.SEMICOLON)) { range = TextRange.create(range.getStartOffset(), c.getTextRange().getEndOffset()); } else { continue; } } break; } } JavaElementArrangementEntry entry = createNewEntry(field, range, FIELD, field.getName(), true); processEntry(entry, field, field.getInitializer()); }
private static void checkCodeBlock( final PsiCodeBlock body, final Set<PsiField> candidates, Set<PsiField> usedFields) { try { final ControlFlow controlFlow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow(body, AllVariablesControlFlowPolicy.getInstance()); final List<PsiVariable> usedVars = ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize()); for (PsiVariable usedVariable : usedVars) { if (usedVariable instanceof PsiField) { final PsiField usedField = (PsiField) usedVariable; if (!usedFields.add(usedField)) { candidates.remove(usedField); // used in more than one code block } } } final List<PsiReferenceExpression> readBeforeWrites = ControlFlowUtil.getReadBeforeWrite(controlFlow); for (final PsiReferenceExpression readBeforeWrite : readBeforeWrites) { final PsiElement resolved = readBeforeWrite.resolve(); if (resolved instanceof PsiField) { final PsiField field = (PsiField) resolved; PsiElement parent = body.getParent(); if (!(parent instanceof PsiMethod) || !((PsiMethod) parent).isConstructor() || field.getInitializer() == null || field.hasModifierProperty(PsiModifier.STATIC)) { candidates.remove(field); } } } } catch (AnalysisCanceledException e) { candidates.clear(); } }
public void buildReferences() { PsiClass psiClass = getElement(); if (psiClass != null) { for (PsiClassInitializer classInitializer : psiClass.getInitializers()) { RefJavaUtil.getInstance().addReferences(psiClass, this, classInitializer.getBody()); } RefJavaUtil.getInstance().addReferences(psiClass, this, psiClass.getModifierList()); PsiField[] psiFields = psiClass.getFields(); for (PsiField psiField : psiFields) { getRefManager().getReference(psiField); final PsiExpression initializer = psiField.getInitializer(); if (initializer != null) { RefJavaUtil.getInstance().addReferences(psiClass, this, initializer); } } PsiMethod[] psiMethods = psiClass.getMethods(); for (PsiMethod psiMethod : psiMethods) { getRefManager().getReference(psiMethod); } getRefManager().fireBuildReferences(this); } }
@Nullable private static String getCannotRefactorMessage(PsiMember member) { if (member == null) { return RefactoringBundle.message("locate.caret.inside.a.method"); } if (member instanceof PsiMethod) { if (((PsiMethod) member).isConstructor()) { return RefactoringBundle.message("replace.with.method.call.does.not.work.for.constructors"); } final PsiCodeBlock body = ((PsiMethod) member).getBody(); if (body == null) { return RefactoringBundle.message("method.does.not.have.a.body", member.getName()); } final PsiStatement[] statements = body.getStatements(); if (statements.length == 0) { return RefactoringBundle.message("method.has.an.empty.body", member.getName()); } } else if (member instanceof PsiField) { final PsiField field = (PsiField) member; if (field.getInitializer() == null) { return "Field " + member.getName() + " doesn't have initializer"; } final PsiClass containingClass = field.getContainingClass(); if (!field.hasModifierProperty(PsiModifier.FINAL) || !field.hasModifierProperty(PsiModifier.STATIC) || containingClass == null || containingClass.getQualifiedName() == null) { return "Replace Duplicates works with constants only"; } } else { return "Caret should be inside method or constant"; } return null; }
private static boolean isExtensionPointNameDeclarationField(PsiField psiField) { // *do* allow non-public if (!psiField.hasModifierProperty(PsiModifier.FINAL) || !psiField.hasModifierProperty(PsiModifier.STATIC) || psiField.hasModifierProperty(PsiModifier.ABSTRACT)) { return false; } if (!psiField.hasInitializer()) { return false; } final PsiExpression initializer = psiField.getInitializer(); if (!(initializer instanceof PsiMethodCallExpression) && !(initializer instanceof PsiNewExpression)) { return false; } final PsiClass fieldClass = PsiTypesUtil.getPsiClass(psiField.getType()); if (fieldClass == null) { return false; } return ExtensionPointName.class.getName().equals(fieldClass.getQualifiedName()); }
@Override public ProblemDescriptor[] checkField( @NotNull PsiField field, @NotNull InspectionManager manager, boolean isOnTheFly) { final PsiExpression initializer = field.getInitializer(); if (initializer != null) { return getDescriptions(initializer, manager, isOnTheFly); } if (field instanceof PsiEnumConstant) { return getDescriptions(field, manager, isOnTheFly); } return null; }
private PsiElement addDeclarationWithFieldInitializerAndRetargetReferences( final PsiElementFactory elementFactory, final String localName, final PsiCodeBlock anchorBlock, final PsiElement anchor, final Set<PsiReference> refs) throws IncorrectOperationException { final PsiDeclarationStatement decl = elementFactory.createVariableDeclarationStatement( localName, myField.getType(), myField.getInitializer()); final PsiElement newDeclaration = anchorBlock.addBefore(decl, anchor); retargetReferences(elementFactory, localName, refs); return newDeclaration; }
@NotNull private Field fieldToField(@NotNull PsiField field, PsiClass psiClass) { Set<String> modifiers = modifiersListToModifiersSet(field.getModifierList()); if (field instanceof PsiEnumConstant) // TODO: remove instanceof { return new EnumConstant( new IdentifierImpl(field.getName()), // TODO modifiers, typeToType(field.getType()), elementToElement(((PsiEnumConstant) field).getArgumentList())); } return new Field( new IdentifierImpl(field.getName()), // TODO modifiers, typeToType(field.getType(), ConverterUtil.isAnnotatedAsNotNull(field.getModifierList())), createSureCallOnlyForChain(field.getInitializer(), field.getType()), // TODO: add modifiers countWritingAccesses(field, psiClass)); }
@Override protected ClassMember[] getAllOriginalMembers(PsiClass aClass) { PsiField[] fields = aClass.getFields(); ArrayList<ClassMember> array = new ArrayList<ClassMember>(); ImplicitUsageProvider[] implicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); fieldLoop: for (PsiField field : fields) { if (field.hasModifierProperty(PsiModifier.STATIC)) continue; if (field.hasModifierProperty(PsiModifier.FINAL) && field.getInitializer() != null) continue; for (ImplicitUsageProvider provider : implicitUsageProviders) { if (provider.isImplicitWrite(field)) continue fieldLoop; } array.add(new PsiFieldMember(field)); } return array.toArray(new ClassMember[array.size()]); }
@Override public void visitField(@NotNull PsiField field) { if (field.hasModifierProperty(PsiModifier.STATIC)) { return; } if (field.getInitializer() != null) { return; } final PsiAnnotation annotation = AnnotationUtil.findAnnotation(field, annotationNames); if (annotation != null) { return; } if (m_ignorePrimitives) { final PsiType fieldType = field.getType(); if (ClassUtils.isPrimitive(fieldType)) { return; } } final PsiClass aClass = field.getContainingClass(); if (aClass == null) { return; } for (ImplicitUsageProvider provider : Extensions.getExtensions(ImplicitUsageProvider.EP_NAME)) { if (provider.isImplicitWrite(field)) { return; } } final UninitializedReadCollector uninitializedReadsCollector = new UninitializedReadCollector(); if (!isInitializedInInitializer(field, uninitializedReadsCollector)) { final PsiMethod[] constructors = aClass.getConstructors(); for (final PsiMethod constructor : constructors) { final PsiCodeBlock body = constructor.getBody(); uninitializedReadsCollector.blockAssignsVariable(body, field); } } final PsiExpression[] badReads = uninitializedReadsCollector.getUninitializedReads(); for (PsiExpression expression : badReads) { registerError(expression); } }
private static String resolveEpName(PsiField psiField) { final PsiExpression initializer = psiField.getInitializer(); PsiExpressionList expressionList = null; if (initializer instanceof PsiMethodCallExpression) { expressionList = ((PsiMethodCallExpression) initializer).getArgumentList(); } else if (initializer instanceof PsiNewExpression) { expressionList = ((PsiNewExpression) initializer).getArgumentList(); } if (expressionList == null) return null; final PsiExpression[] expressions = expressionList.getExpressions(); if (expressions.length != 1) return null; final PsiExpression epNameExpression = expressions[0]; final PsiConstantEvaluationHelper helper = JavaPsiFacade.getInstance(psiField.getProject()).getConstantEvaluationHelper(); final Object o = helper.computeConstantExpression(epNameExpression); return o instanceof String ? (String) o : null; }
@Override public void visitField(@NotNull PsiField field) { super.visitField(field); if (!field.hasModifierProperty(PsiModifier.PRIVATE)) { return; } if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { return; } final PsiClass containingClass = PsiUtil.getTopLevelClass(field); if (!shouldCheckVariable(field, containingClass)) { return; } final ArrayReadWriteVisitor visitor = new ArrayReadWriteVisitor(field, !isSimpleArrayExpression(field.getInitializer())); containingClass.accept(visitor); final boolean written = visitor.isWritten(); if (!visitor.isReferenced() || written == visitor.isRead()) { return; } registerFieldError(field, Boolean.valueOf(written)); }
protected void performRefactoring(@NotNull UsageInfo[] usageInfos) { final PsiClass psiClass = buildClass(); if (psiClass == null) return; if (delegationRequired) { buildDelegate(); } myExtractEnumProcessor.performEnumConstantTypeMigration(usageInfos); final Set<PsiMember> members = new HashSet<PsiMember>(); for (PsiMethod method : methods) { final PsiMethod member = psiClass.findMethodBySignature(method, false); if (member != null) { members.add(member); } } for (PsiField field : fields) { final PsiField member = psiClass.findFieldByName(field.getName(), false); if (member != null) { members.add(member); final PsiExpression initializer = member.getInitializer(); if (initializer != null) { final boolean[] moveInitializerToConstructor = new boolean[1]; initializer.accept( new JavaRecursiveElementWalkingVisitor() { @Override public void visitReferenceExpression(PsiReferenceExpression expression) { super.visitReferenceExpression(expression); final PsiElement resolved = expression.resolve(); if (resolved instanceof PsiField && !members.contains(resolved)) { moveInitializerToConstructor[0] = true; } } }); if (moveInitializerToConstructor[0]) { final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myProject); PsiMethod[] constructors = psiClass.getConstructors(); if (constructors.length == 0) { final PsiMethod constructor = (PsiMethod) elementFactory.createConstructor().setName(psiClass.getName()); constructors = new PsiMethod[] {(PsiMethod) psiClass.add(constructor)}; } for (PsiMethod constructor : constructors) { MoveInstanceMembersUtil.moveInitializerToConstructor( elementFactory, constructor, member); } } } } } if (myGenerateAccessors) { final NecessaryAccessorsVisitor visitor = checkNecessaryGettersSetters4SourceClass(); for (PsiField field : visitor.getFieldsNeedingGetter()) { sourceClass.add(GenerateMembersUtil.generateGetterPrototype(field)); } for (PsiField field : visitor.getFieldsNeedingSetter()) { sourceClass.add(GenerateMembersUtil.generateSetterPrototype(field)); } } super.performRefactoring(usageInfos); if (myNewVisibility == null) return; for (PsiMember member : members) { VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usageInfos), member, myNewVisibility); } }
public static Set<PsiField> getNonInitializedFields(PsiElement element) { final PsiStatement statement = PsiTreeUtil.getParentOfType(element, PsiStatement.class); final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class, true, PsiClass.class); if (statement == null || method == null || !method.isConstructor()) { return Collections.emptySet(); } PsiElement parent = element.getParent(); while (parent != statement) { PsiElement next = parent.getParent(); if (next instanceof PsiAssignmentExpression && parent == ((PsiAssignmentExpression) next).getLExpression()) { return Collections.emptySet(); } if (parent instanceof PsiReferenceExpression && next instanceof PsiExpressionStatement) { return Collections.emptySet(); } parent = next; } final Set<PsiField> fields = new HashSet<PsiField>(); final PsiClass containingClass = method.getContainingClass(); assert containingClass != null; for (PsiField field : containingClass.getFields()) { if (!field.hasModifierProperty(PsiModifier.STATIC) && field.getInitializer() == null && !isInitializedImplicitly(field)) { fields.add(field); } } method.accept( new JavaRecursiveElementWalkingVisitor() { @Override public void visitAssignmentExpression(PsiAssignmentExpression expression) { if (expression.getTextRange().getStartOffset() < statement.getTextRange().getStartOffset()) { final PsiExpression lExpression = expression.getLExpression(); if (lExpression instanceof PsiReferenceExpression) { //noinspection SuspiciousMethodCalls fields.remove(((PsiReferenceExpression) lExpression).resolve()); } } super.visitAssignmentExpression(expression); } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { if (expression.getTextRange().getStartOffset() < statement.getTextRange().getStartOffset()) { final PsiReferenceExpression methodExpression = expression.getMethodExpression(); if (methodExpression.textMatches("this")) { fields.clear(); } } super.visitMethodCallExpression(expression); } }); return fields; }
@Nullable public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( @NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression, @NotNull Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) { if (!PsiUtil.isAccessedForWriting(expression)) return null; final PsiElement scope = variable instanceof PsiField ? variable.getParent() : variable.getParent() == null ? null : variable.getParent().getParent(); PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); if (codeBlock == null) return null; Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock); boolean alreadyAssigned = false; for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) { if (variableInfo.expression == expression) { alreadyAssigned = true; break; } } if (!alreadyAssigned) { if (!(variable instanceof PsiField)) return null; final PsiField field = (PsiField) variable; final PsiClass aClass = field.getContainingClass(); if (aClass == null) return null; // field can get assigned in other field initializers final PsiField[] fields = aClass.getFields(); boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); for (PsiField psiField : fields) { PsiExpression initializer = psiField.getInitializer(); if (psiField != field && psiField.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic && initializer != null && initializer != codeBlock && !variableDefinitelyNotAssignedIn(field, initializer)) { alreadyAssigned = true; break; } } if (!alreadyAssigned) { // field can get assigned in class initializers final PsiMember enclosingConstructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression); if (enclosingConstructorOrInitializer == null || !aClass .getManager() .areElementsEquivalent( enclosingConstructorOrInitializer.getContainingClass(), aClass)) { return null; } final PsiClassInitializer[] initializers = aClass.getInitializers(); for (PsiClassInitializer initializer : initializers) { if (initializer.hasModifierProperty(PsiModifier.STATIC) == field.hasModifierProperty(PsiModifier.STATIC)) { final PsiCodeBlock body = initializer.getBody(); if (body == codeBlock) return null; try { final ControlFlow controlFlow = getControlFlow(body); if (!ControlFlowUtil.isVariableDefinitelyNotAssigned(field, controlFlow)) { alreadyAssigned = true; break; } } catch (AnalysisCanceledException e) { // incomplete code return null; } } } } if (!alreadyAssigned && !field.hasModifierProperty(PsiModifier.STATIC)) { // then check if instance field already assigned in other constructor final PsiMethod ctr = codeBlock.getParent() instanceof PsiMethod ? (PsiMethod) codeBlock.getParent() : null; // assignment to final field in several constructors threatens us only if these are linked // (there is this() call in the beginning) final List<PsiMethod> redirectedConstructors = ctr != null && ctr.isConstructor() ? JavaHighlightUtil.getChainedConstructors(ctr) : null; for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) { PsiMethod redirectedConstructor = redirectedConstructors.get(j); PsiCodeBlock body = redirectedConstructor.getBody(); if (body != null && variableDefinitelyAssignedIn(variable, body)) { alreadyAssigned = true; break; } } } } if (alreadyAssigned) { String description = JavaErrorMessages.message("variable.already.assigned", variable.getName()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false)); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createDeferFinalAssignmentFix(variable, expression)); return highlightInfo; } return null; }
@Override public void onReferencesBuild(RefElement refElement) { if (refElement instanceof RefClass) { final PsiClass psiClass = (PsiClass) refElement.getElement(); if (psiClass != null) { if (refElement.isEntry()) { ((RefClassImpl) refElement).setFlag(false, CAN_BE_FINAL_MASK); } PsiMethod[] psiMethods = psiClass.getMethods(); PsiField[] psiFields = psiClass.getFields(); HashSet<PsiVariable> allFields = new HashSet<PsiVariable>(); ContainerUtil.addAll(allFields, psiFields); ArrayList<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>(); boolean hasInitializers = false; for (PsiClassInitializer initializer : psiClass.getInitializers()) { PsiCodeBlock body = initializer.getBody(); hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>(); ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables); for (PsiVariable psiVariable : writtenVariables) { if (allFields.contains(psiVariable)) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } else { instanceInitializerInitializedFields.add(psiVariable); } } } for (PsiVariable psiVariable : writtenVariables) { if (!instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); } } } for (PsiMethod psiMethod : psiMethods) { if (psiMethod.isConstructor()) { PsiCodeBlock body = psiMethod.getBody(); if (body != null) { hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false); for (PsiVariable psiVariable : writtenVariables) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } } List<PsiMethod> redirectedConstructors = HighlightControlFlowUtil.getChainedConstructors(psiMethod); if (redirectedConstructors == null || redirectedConstructors.isEmpty()) { List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow); ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables); good.addAll(instanceInitializerInitializedFields); allFields.retainAll(good); } else { allFields.removeAll(writtenVariables); } } } } for (PsiField psiField : psiFields) { if ((!hasInitializers || !allFields.contains(psiField)) && psiField.getInitializer() == null) { final RefFieldImpl refField = (RefFieldImpl) myManager.getReference(psiField); if (refField != null) { refField.setFlag(false, CAN_BE_FINAL_MASK); } } } } } else if (refElement instanceof RefMethod) { final RefMethod refMethod = (RefMethod) refElement; if (refMethod.isEntry()) { ((RefMethodImpl) refMethod).setFlag(false, CAN_BE_FINAL_MASK); } } }