Set<AssignInstruction> getOverwrittenFields() {
    // Contains instructions where overwrite without read is detected
    // Initially: all assignment instructions; during DFA traverse, some are removed
    Set<AssignInstruction> overwrites = StreamEx.of(myInstructions).select(AssignInstruction.class).toSet();
    if (overwrites.isEmpty()) return Collections.emptySet();
    Set<AssignInstruction> visited = new HashSet<>();
    boolean ok = runDfa(false, (instruction, beforeVars) -> {
      // beforeVars: IDs of variables that were written but not read yet
      if (beforeVars.isEmpty() && !(instruction instanceof AssignInstruction)) return beforeVars;
      if (instruction instanceof FlushFieldsInstruction) {
        return new BitSet();
      }
      BitSet afterVars = (BitSet)beforeVars.clone();
      boolean skipDependent = false;
      StreamEx<DfaVariableValue> readVariables;
      if (instruction instanceof AssignInstruction) {
        visited.add((AssignInstruction)instruction);
        DfaValue value = ((AssignInstruction)instruction).getAssignedValue();
        if (value instanceof DfaVariableValue) {
          int id = value.getID();
          readVariables = StreamEx.of(((DfaVariableValue)value).getDependentVariables());
          if (!beforeVars.get(id)) {
            overwrites.remove(instruction);
            afterVars.set(id);
          }
        } else {
          readVariables = StreamEx.empty();
        }
        skipDependent = true;
      }
      else 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 &&
                           ((PsiField)value.getPsiVariable()).hasModifierProperty(PsiModifier.STATIC));
      }
      else if (instruction instanceof FinishElementInstruction) {
        readVariables = StreamEx.of(((FinishElementInstruction)instruction).getVarsToFlush());
      }
      else {
        readVariables = getReadVariables(instruction);
      }
      if (instruction instanceof PushInstruction) {
        // Avoid forgetting about qualifier.field on qualifier.field = x;
        DfaAnchor anchor = ((PushInstruction)instruction).getDfaAnchor();
        PsiExpression expression = anchor instanceof JavaExpressionAnchor ? ((JavaExpressionAnchor)anchor).getExpression() : null;
        skipDependent = expression != null && PsiUtil.skipParenthesizedExprUp(expression).getParent() instanceof PsiReferenceExpression
                      && ExpressionUtils.getCallForQualifier(expression) == null;
      }
      if (!skipDependent) {
        readVariables = readVariables.flatMap(v -> StreamEx.of(v.getDependentVariables()).prepend(v)).distinct();
      }
      readVariables.forEach(v -> afterVars.clear(v.getID()));
      return afterVars;
    });
    overwrites.retainAll(visited);
    return ok ? overwrites : Collections.emptySet();
  }
Set<AssignInstruction> getOverwrittenFields()