diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/BaseVariableAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/BaseVariableAnalyzer.java new file mode 100644 index 000000000000..43adb145438a --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/BaseVariableAnalyzer.java @@ -0,0 +1,118 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInspection.dataFlow; + +import com.intellij.codeInspection.dataFlow.instructions.Instruction; +import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction; +import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Pair; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FilteringIterator; +import com.intellij.util.containers.MultiMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import one.util.streamex.IntStreamEx; + +import java.util.*; +import java.util.function.BiFunction; + +public abstract class BaseVariableAnalyzer { + protected final Instruction[] myInstructions; + protected final MultiMap myForwardMap; + protected final MultiMap myBackwardMap; + protected final DfaValueFactory myFactory; + + public BaseVariableAnalyzer(ControlFlow flow) { + myFactory = flow.getFactory(); + myInstructions = flow.getInstructions(); + myForwardMap = calcForwardMap(); + myBackwardMap = calcBackwardMap(); + } + + private List getSuccessors(Instruction ins) { + return IntStreamEx.of(LoopAnalyzer.getSuccessorIndices(ins.getIndex(), myInstructions)).elements(myInstructions).toList(); + } + + protected MultiMap calcBackwardMap() { + MultiMap result = MultiMap.create(); + for (Instruction instruction : myInstructions) { + for (Instruction next : myForwardMap.get(instruction)) { + result.putValue(next, instruction); + } + } + return result; + } + + protected MultiMap calcForwardMap() { + MultiMap result = MultiMap.create(); + for (Instruction instruction : myInstructions) { + if (isInterestingInstruction(instruction)) { + for (Instruction next : getSuccessors(instruction)) { + while (true) { + if (isInterestingInstruction(next)) { + result.putValue(instruction, next); + break; + } + if (next.getIndex() + 1 >= myInstructions.length) { + break; + } + next = myInstructions[next.getIndex() + 1]; + } + } + } + } + return result; + } + + protected abstract boolean isInterestingInstruction(Instruction instruction); + + /** + * @return true if completed, false if "too complex" + */ + protected boolean runDfa(boolean forward, BiFunction handleState) { + Set entryPoints = new HashSet<>(); + if (forward) { + entryPoints.add(myInstructions[0]); + } + else { + entryPoints.addAll(ContainerUtil.findAll(myInstructions, FilteringIterator.instanceOf(ReturnInstruction.class))); + } + + Deque queue = new ArrayDeque<>(10); + for (Instruction i : entryPoints) { + queue.addLast(new InstructionState(i, new BitSet())); + } + + int limit = myForwardMap.size() * 100; + Map processed = new HashMap<>(); + int steps = 0; + while (!queue.isEmpty()) { + if (steps > limit) { + return false; + } + if (steps % 1024 == 0) { + ProgressManager.checkCanceled(); + } + InstructionState state = queue.removeFirst(); + Instruction instruction = state.first; + Collection nextInstructions = forward ? myForwardMap.get(instruction) : myBackwardMap.get(instruction); + BitSet nextVars = handleState.apply(instruction, state.second); + for (Instruction next : nextInstructions) { + IntSet instructionSet = processed.computeIfAbsent(nextVars, k -> new IntOpenHashSet()); + int index = next.getIndex() + 1; + if (!instructionSet.contains(index)) { + instructionSet.add(index); + queue.addLast(new InstructionState(next, nextVars)); + steps++; + } + } + } + return true; + } + + static class InstructionState extends Pair { + InstructionState(Instruction first, BitSet second) { + super(first, second); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java index 7385618bdf92..1bb95911326f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/LiveVariablesAnalyzer.java @@ -4,74 +4,25 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.DfaExpressionFactory; import com.intellij.codeInspection.dataFlow.value.DfaValue; -import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiMember; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.FilteringIterator; import com.intellij.util.containers.MultiMap; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import one.util.streamex.IntStreamEx; import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; -import java.util.function.BiFunction; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.function.Consumer; /** * @author peter */ -final class LiveVariablesAnalyzer { - private final DfaValueFactory myFactory; - private final Instruction[] myInstructions; - private final MultiMap myForwardMap; - private final MultiMap myBackwardMap; - +final class LiveVariablesAnalyzer extends BaseVariableAnalyzer { LiveVariablesAnalyzer(ControlFlow flow) { - myFactory = flow.getFactory(); - myInstructions = flow.getInstructions(); - myForwardMap = calcForwardMap(); - myBackwardMap = calcBackwardMap(); - } - - private List getSuccessors(Instruction ins) { - return IntStreamEx.of(LoopAnalyzer.getSuccessorIndices(ins.getIndex(), myInstructions)).elements(myInstructions).toList(); - } - - private MultiMap calcBackwardMap() { - MultiMap result = MultiMap.create(); - for (Instruction instruction : myInstructions) { - for (Instruction next : myForwardMap.get(instruction)) { - result.putValue(next, instruction); - } - } - return result; - } - - private MultiMap calcForwardMap() { - MultiMap result = MultiMap.create(); - for (Instruction instruction : myInstructions) { - if (isInterestingInstruction(instruction)) { - for (Instruction next : getSuccessors(instruction)) { - while (true) { - if (isInterestingInstruction(next)) { - result.putValue(instruction, next); - break; - } - if (next.getIndex() + 1 >= myInstructions.length) { - break; - } - next = myInstructions[next.getIndex() + 1]; - } - } - } - } - return result; + super(flow); } @Nullable @@ -102,7 +53,8 @@ final class LiveVariablesAnalyzer { return StreamEx.empty(); } - private boolean isInterestingInstruction(Instruction instruction) { + @Override + protected boolean isInterestingInstruction(Instruction instruction) { if (instruction == myInstructions[0]) return true; if (getReadVariables(instruction).findFirst().isPresent() || getWrittenVariable(instruction) != null) return true; return instruction instanceof FinishElementInstruction || @@ -195,54 +147,4 @@ final class LiveVariablesAnalyzer { } } } - - /** - * @return true if completed, false if "too complex" - */ - private boolean runDfa(boolean forward, BiFunction handleState) { - Set entryPoints = new HashSet<>(); - if (forward) { - entryPoints.add(myInstructions[0]); - } - else { - entryPoints.addAll(ContainerUtil.findAll(myInstructions, FilteringIterator.instanceOf(ReturnInstruction.class))); - } - - Deque queue = new ArrayDeque<>(10); - for (Instruction i : entryPoints) { - queue.addLast(new InstructionState(i, new BitSet())); - } - - int limit = myForwardMap.size() * 100; - Map processed = new HashMap<>(); - int steps = 0; - while (!queue.isEmpty()) { - if (steps > limit) { - return false; - } - if (steps % 1024 == 0) { - ProgressManager.checkCanceled(); - } - InstructionState state = queue.removeFirst(); - Instruction instruction = state.first; - Collection nextInstructions = forward ? myForwardMap.get(instruction) : myBackwardMap.get(instruction); - BitSet nextVars = handleState.apply(instruction, state.second); - for (Instruction next : nextInstructions) { - IntSet instructionSet = processed.computeIfAbsent(nextVars, k -> new IntOpenHashSet()); - int index = next.getIndex() + 1; - if (!instructionSet.contains(index)) { - instructionSet.add(index); - queue.addLast(new InstructionState(next, nextVars)); - steps++; - } - } - } - return true; - } } - -class InstructionState extends Pair { - InstructionState(Instruction first, BitSet second) { - super(first, second); - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInspection/defUse/DefUseInspection.java b/java/java-impl/src/com/intellij/codeInspection/defUse/DefUseInspection.java index 00bd7e5130da..3e84b4a896ca 100644 --- a/java/java-impl/src/com/intellij/codeInspection/defUse/DefUseInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/defUse/DefUseInspection.java @@ -6,6 +6,10 @@ import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil; import com.intellij.codeInspection.*; +import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; +import com.intellij.codeInspection.dataFlow.value.DfaValue; +import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; import com.intellij.codeInspection.ui.InspectionOptionsPanel; import com.intellij.java.JavaBundle; import com.intellij.psi.*; @@ -21,7 +25,6 @@ import com.siyeh.ig.psiutils.EquivalenceChecker; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.util.List; import java.util.*; public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { @@ -37,32 +40,31 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { return new JavaElementVisitor() { @Override public void visitMethod(PsiMethod method) { - checkCodeBlock(method.getBody(), holder, isOnTheFly); + checkCodeBlock(method.getBody(), holder); } @Override public void visitClassInitializer(PsiClassInitializer initializer) { - checkCodeBlock(initializer.getBody(), holder, isOnTheFly); + checkCodeBlock(initializer.getBody(), holder); } @Override public void visitLambdaExpression(PsiLambdaExpression expression) { PsiElement body = expression.getBody(); if (body instanceof PsiCodeBlock) { - checkCodeBlock((PsiCodeBlock)body, holder, isOnTheFly); + checkCodeBlock((PsiCodeBlock)body, holder); } } @Override public void visitField(PsiField field) { - checkField(field, holder, isOnTheFly); + checkField(field, holder); } }; } private void checkCodeBlock(final PsiCodeBlock body, - final ProblemsHolder holder, - final boolean isOnTheFly) { + final ProblemsHolder holder) { if (body == null) return; final Set usedVariables = new HashSet<>(); List unusedDefs = DefUseUtil.getUnusedDefs(body, usedVariables); @@ -78,7 +80,7 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { if (info.isRead() && REPORT_REDUNDANT_INITIALIZER) { PsiTypeElement typeElement = psiVariable.getTypeElement(); if (typeElement == null || !typeElement.isInferredType()) { - reportInitializerProblem(psiVariable, holder, isOnTheFly); + reportInitializerProblem(psiVariable, holder); } } } @@ -90,7 +92,7 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { // x = x = 5; reported by "Variable is assigned to itself" continue; } - reportAssignmentProblem(psiVariable, (PsiAssignmentExpression)context, holder, isOnTheFly); + reportAssignmentProblem(psiVariable, (PsiAssignmentExpression)context, holder); } else { if (context instanceof PsiPrefixExpression && REPORT_PREFIX_EXPRESSIONS || @@ -101,14 +103,46 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { } } } + + processFieldsViaDfa(body, holder); } - private static void reportInitializerProblem(PsiVariable psiVariable, ProblemsHolder holder, boolean isOnTheFly) { + private void processFieldsViaDfa(PsiCodeBlock body, ProblemsHolder holder) { + DfaValueFactory factory = new DfaValueFactory(holder.getProject(), body); + var flow = com.intellij.codeInspection.dataFlow.ControlFlow.buildFlow(body, factory, true); + if (flow != null) { + Set variables = new OverwrittenFieldAnalyzer(flow).getOverwrittenFields(); + for (AssignInstruction instruction : variables) { + DfaValue value = instruction.getAssignedValue(); + if (!(value instanceof DfaVariableValue)) continue; + PsiField field = ObjectUtils.tryCast(((DfaVariableValue)value).getPsiVariable(), PsiField.class); + if (field == null) continue; + PsiExpression lExpression = instruction.getLExpression(); + PsiExpression expression = instruction.getRExpression(); + if (lExpression == null) continue; + PsiElement parent = PsiUtil.skipParenthesizedExprUp(lExpression.getParent()); + if (parent instanceof PsiPrefixExpression && REPORT_PREFIX_EXPRESSIONS || + parent instanceof PsiPostfixExpression && REPORT_POSTFIX_EXPRESSIONS) { + holder.registerProblem(parent, JavaBundle.message("inspection.unused.assignment.problem.descriptor4", "#ref #loc")); + } + else if (parent instanceof PsiAssignmentExpression) { + if (expression instanceof PsiArrayInitializerExpression || + expression instanceof PsiNewExpression && ((PsiNewExpression)expression).getArrayInitializer() != null) { + // Due to implementation quirk, array initializers are reassigned in CFG, so false warnings appear there + continue; + } + reportAssignmentProblem(field, (PsiAssignmentExpression)parent, holder); + } + } + } + } + + private static void reportInitializerProblem(PsiVariable psiVariable, ProblemsHolder holder) { List fixes = ContainerUtil.createMaybeSingletonList( - isOnTheFlyOrNoSideEffects(isOnTheFly, psiVariable, psiVariable.getInitializer()) ? new RemoveInitializerFix() : null); + isOnTheFlyOrNoSideEffects(holder.isOnTheFly(), psiVariable, psiVariable.getInitializer()) ? new RemoveInitializerFix() : null); holder.registerProblem(ObjectUtils.notNull(psiVariable.getInitializer(), psiVariable), JavaBundle.message("inspection.unused.assignment.problem.descriptor2", - "" + psiVariable.getName() + "", "#ref #loc"), + "" + psiVariable.getName() + "", "#ref #loc"), ProblemHighlightType.LIKE_UNUSED_SYMBOL, fixes.toArray(LocalQuickFix.EMPTY_ARRAY) ); @@ -116,18 +150,17 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { private static void reportAssignmentProblem(PsiVariable psiVariable, PsiAssignmentExpression assignment, - ProblemsHolder holder, - boolean isOnTheFly) { + ProblemsHolder holder) { List fixes = ContainerUtil.createMaybeSingletonList( - isOnTheFlyOrNoSideEffects(isOnTheFly, psiVariable, assignment.getRExpression()) ? new RemoveAssignmentFix() : null); + isOnTheFlyOrNoSideEffects(holder.isOnTheFly(), psiVariable, assignment.getRExpression()) ? new RemoveAssignmentFix() : null); holder.registerProblem(assignment.getLExpression(), JavaBundle.message("inspection.unused.assignment.problem.descriptor3", - Objects.requireNonNull(assignment.getRExpression()).getText(), "#ref" + " #loc"), + Objects.requireNonNull(assignment.getRExpression()).getText(), "#ref" + " #loc"), ProblemHighlightType.LIKE_UNUSED_SYMBOL, fixes.toArray(LocalQuickFix.EMPTY_ARRAY) ); } - private void checkField(@NotNull PsiField field, @NotNull ProblemsHolder holder, boolean isOnTheFly) { + private void checkField(@NotNull PsiField field, @NotNull ProblemsHolder holder) { if (field.hasModifierProperty(PsiModifier.FINAL)) return; final PsiClass psiClass = field.getContainingClass(); if (psiClass == null) return; @@ -177,12 +210,12 @@ public class DefUseInspection extends AbstractBaseJavaLocalInspectionTool { if (wasDefinitelyAssigned) { if (fieldWrite.isInitializer()) { if (REPORT_REDUNDANT_INITIALIZER) { - reportInitializerProblem(field, holder, isOnTheFly); + reportInitializerProblem(field, holder); } } else { for (PsiAssignmentExpression assignment : fieldWrite.getAssignments()) { - reportAssignmentProblem(field, assignment, holder, isOnTheFly); + reportAssignmentProblem(field, assignment, holder); } } } diff --git a/java/java-impl/src/com/intellij/codeInspection/defUse/OverwrittenFieldAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/defUse/OverwrittenFieldAnalyzer.java new file mode 100644 index 000000000000..dbcc7f5b9d5a --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/defUse/OverwrittenFieldAnalyzer.java @@ -0,0 +1,123 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInspection.defUse; + +import com.intellij.codeInspection.dataFlow.BaseVariableAnalyzer; +import com.intellij.codeInspection.dataFlow.ControlFlow; +import com.intellij.codeInspection.dataFlow.instructions.*; +import com.intellij.codeInspection.dataFlow.value.DfaValue; +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.ig.psiutils.ExpressionUtils; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Analyze overwritten fields based on DFA-CFG (unlike usual CFG, it includes method calls, so we can know when field value may leak) + */ +final class OverwrittenFieldAnalyzer extends BaseVariableAnalyzer { + OverwrittenFieldAnalyzer(ControlFlow flow) { + super(flow); + } + + @NotNull + private StreamEx getReadVariables(Instruction instruction) { + if (instruction instanceof PushInstruction && !((PushInstruction)instruction).isReferenceWrite()) { + DfaValue value = ((PushInstruction)instruction).getValue(); + if (value instanceof DfaVariableValue) { + return StreamEx.of((DfaVariableValue)value); + } + } + else if (instruction instanceof EndOfInitializerInstruction) { + return StreamEx.of(myFactory.getValues()).select(DfaVariableValue.class) + .filter(var -> var.getPsiVariable() instanceof PsiMember); + } + return StreamEx.empty(); + } + + @Override + protected boolean isInterestingInstruction(Instruction instruction) { + if (instruction == myInstructions[0]) return true; + + if (instruction instanceof AssignInstruction && ((AssignInstruction)instruction).getAssignedValue() instanceof DfaVariableValue || + instruction instanceof MethodCallInstruction || + instruction instanceof FinishElementInstruction || + instruction instanceof GotoInstruction || + instruction instanceof ConditionalGotoInstruction || + instruction instanceof ControlTransferInstruction || + instruction instanceof FlushFieldsInstruction) { + return true; + } + return getReadVariables(instruction).findFirst().isPresent(); + } + + public Set getOverwrittenFields() { + Set overwrites = StreamEx.of(myInstructions).select(AssignInstruction.class).toSet(); + if (overwrites.isEmpty()) return Collections.emptySet(); + boolean hasFieldWrite = StreamEx.of(overwrites).map(AssignInstruction::getAssignedValue) + .select(DfaVariableValue.class) + .map(DfaVariableValue::getPsiVariable) + .anyMatch(f -> f instanceof PsiField); + if (!hasFieldWrite) return Collections.emptySet(); + Set visited = new HashSet<>(); + boolean ok = runDfa(false, (instruction, nextVars) -> { + if (instruction instanceof AssignInstruction) { + visited.add((AssignInstruction)instruction); + DfaValue value = ((AssignInstruction)instruction).getAssignedValue(); + if (value instanceof DfaVariableValue) { + int id = value.getID(); + if (!nextVars.get(id)) { + overwrites.remove(instruction); + BitSet clone = (BitSet)nextVars.clone(); + clone.set(id); + return clone; + } + } + } + if (nextVars.isEmpty()) return nextVars; + if (instruction instanceof FlushFieldsInstruction) { + return new BitSet(); + } + StreamEx readVariables; + if (instruction instanceof MethodCallInstruction) { + if (!((MethodCallInstruction)instruction).getMutationSignature().isPure()) { + return new BitSet(); + } + // We assume that pure methods may read only static fields and fields which are passed as parameters (directly or by qualifier). + // This might be incorrect in rare cases but allows finding many useful bugs. + readVariables = StreamEx.of(myFactory.getValues()) + .select(DfaVariableValue.class) + .filter(value -> value.getPsiVariable() instanceof PsiField && value.getPsiVariable().hasModifierProperty(PsiModifier.STATIC)); + } + else if (instruction instanceof FinishElementInstruction) { + readVariables = StreamEx.of(((FinishElementInstruction)instruction).getVarsToFlush()); + } + else { + readVariables = getReadVariables(instruction); + } + BitSet clone = (BitSet)nextVars.clone(); + boolean qualifierPush = false; + if (instruction instanceof PushInstruction) { + // Avoid forgetting about qualifier.field on qualifier.field = x; + PsiExpression expression = ((PushInstruction)instruction).getExpression(); + if (expression != null && PsiUtil.skipParenthesizedExprUp(expression).getParent() instanceof PsiReferenceExpression + && ExpressionUtils.getCallForQualifier(expression) == null) { + qualifierPush = true; + } + } + if (!qualifierPush) { + readVariables = readVariables.flatMap(v -> StreamEx.of(v.getDependentVariables()).prepend(v)).distinct(); + } + readVariables.forEach(v -> clone.clear(v.getID())); + return clone; + }); + overwrites.retainAll(visited); + return ok ? overwrites : Collections.emptySet(); + } +} + diff --git a/java/java-tests/testData/inspection/defUse/FieldOverwrite.java b/java/java-tests/testData/inspection/defUse/FieldOverwrite.java new file mode 100644 index 000000000000..82046f0b35bd --- /dev/null +++ b/java/java-tests/testData/inspection/defUse/FieldOverwrite.java @@ -0,0 +1,75 @@ +public class FieldOverwrite { + int val; + int val2; + int[] data; + + public FieldOverwrite(int field) { + this.val = 123; + this.val = field; + } + + void testArray() { + data = new int[] {10, 20, 30, 40}; + } + + void increment() { + val++; + val=2; + val+=3; + val=4; + } + + void use() { + val = 1; + val = calc(2); + } + + int calc(int x) { + return val * x; + } + + private int getVal() { + return val; + } + + void noUseInlining() { + val2 = 1; + val2 = getVal(); + } + + void test(FieldOverwrite fo) { + val = 1; + val = 2; + fo.val = 3; + fo.val = 4; + } + + // IDEA-195460 + private int intField; + private static int intStaticField; + + public void main() { + int intVar = 0; + intVar = intVar++; + System.out.println(intVar); + + intField = 0; + intField = intField++; + System.out.println(intField); + + + intStaticField = 0; + intStaticField = intStaticField++; + System.out.println(intStaticField); + } + + void testUseStatic() { + intStaticField = 1; + useStatic(); + intStaticField = 2; + } + + static void useStatic() { + System.out.println(intStaticField); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DefUseTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DefUseTest.java index a01196be58d5..ad3e57c65db8 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DefUseTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DefUseTest.java @@ -72,6 +72,7 @@ public class DefUseTest extends LightJavaCodeInsightFixtureTestCase { public void testFieldInitializerChainedConstructor() { doTest(); } public void testUnderAlwaysFalseCondition() { doTest(); } public void testLastInTry() { doTest(); } + public void testFieldOverwrite() { doTest(); } @NotNull @Override