private void dropMethodsAndErrors()

in java/java.source.base/src/org/netbeans/modules/java/source/indexing/VanillaCompileWorker.java [538:1150]


    private void dropMethodsAndErrors(com.sun.tools.javac.util.Context ctx, CompilationUnitTree cut, DiagnosticListenerImpl dc) {
        HandledUnits hu = ctx.get(HandledUnits.class);
        if (hu == null) {
            hu = new HandledUnits();
            ctx.put(HandledUnits.class, hu);
        }
        if (hu.handled.contains(cut)) {
            //already seen
            return ;
        }
        hu.handled.add(cut);
        Names names = Names.instance(ctx);
        Symtab syms = Symtab.instance(ctx);
        Trees trees = Trees.instance(BasicJavacTask.instance(ctx));
        Types types = Types.instance(ctx);
        TreeMaker make = TreeMaker.instance(ctx);
        Elements el = JavacElements.instance(ctx);
        Source source = Source.instance(ctx);
        boolean hasMatchException = el.getTypeElement("java.lang.MatchException") != null;
        DeferredCompletionFailureHandler dcfh = DeferredCompletionFailureHandler.instance(ctx);
        //TODO: should preserve error types!!!
        new TreePathScanner<Void, Void>() {
            private Set<JCNewClass> anonymousClasses = Collections.newSetFromMap(new LinkedHashMap<>());
            private ClassSymbol currentClass = null;
            private TreeMap<Long, List<Diagnostic<? extends JavaFileObject>>> diags;

            @Override
            public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
                diags = dc.peekDiagnostics(cut.getSourceFile())
                          .stream()
                          .filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
                          .collect(Collectors.toMap(d -> d.getPosition(),
                                                    d -> Collections.singletonList(d),
                                                    (dl1, dl2) -> Stream.of(dl1, dl2)
                                                                        .flatMap(dl -> dl.stream())
                                                                        .collect(Collectors.toList()),
                                                    () -> new TreeMap<>()));
                super.visitCompilationUnit(node, p);
                //TODO: if diagnostics are remaining, make all classes non-usable
                return null;
            }
            
            @Override
            public Void visitVariable(VariableTree node, Void p) {
                JCTree.JCVariableDecl decl = (JCTree.JCVariableDecl) node;
                scan(node.getModifiers(), null);
                scan(node.getType(), null);
                scan(node.getNameExpression(), null);
                if (TreeUtilities.CLASS_TREE_KINDS.contains(getCurrentPath().getParentPath().getLeaf().getKind())) {
                    boolean prevErrorFound = errorFound;
                    errorFound = false;
                    scan(node.getInitializer(), null);
                    SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> blockDiags = treeDiags(node.getInitializer());
                    if (errorFound || !blockDiags.isEmpty()) {
                        JCClassDecl clazz = (JCClassDecl) getCurrentPath().getParentPath().getLeaf();
                        if ((decl.mods.flags & Flags.ENUM) == 0) {
                            decl.init = null;
                        } else {
                            Symbol sym = clazz.sym.members().findFirst(clazz.sym.name.table.names.init);
                            JCNewClass nct = make.NewClass(null, com.sun.tools.javac.util.List.nil(), make.Ident(sym), sym.asType().getParameterTypes().map(this::nullExpression), null);
                            nct.constructor = sym;
                            nct.constructorType = sym.type;
                            decl.init = nct.setType(clazz.type);
                        }
                        clazz.defs = clazz.defs.prepend(make.Block(node.getModifiers().getFlags().contains(Modifier.STATIC) ? Flags.STATIC : 0, com.sun.tools.javac.util.List.of(throwTree(blockDiags))));
                        blockDiags.clear();
                    }
                    errorFound = prevErrorFound;
                } else {
                    scan(node.getInitializer(), null);
                }
                decl.type = error2Object(decl.type);
                decl.sym.type = error2Object(decl.sym.type);
                clearAnnotations(decl.sym.getMetadata());
                return null;
            }

            private JCExpression nullExpression(Type type) {
                if (type.isPrimitive()) {
                    return make.Literal(type.getTag(), 0).setType(syms.booleanType);
                } else {
                    return make.Literal(TypeTag.BOT, null).setType(syms.botType);
                }
            }

            @Override
            public Void visitMethod(MethodTree node, Void p) {
                JCTree.JCMethodDecl decl = (JCTree.JCMethodDecl) node;
                Symbol.MethodSymbol msym = decl.sym;
                super.visitMethod(node, p);
                //TODO: fix up modifiers:
//                if (Collections.disjoint(msym.getModifiers(), EnumSet.of(Modifier.NATIVE, Modifier.ABSTRACT))) {
//                    JCTree.JCNewClass nct =
//                            make.NewClass(null,
//                                          com.sun.tools.javac.util.List.nil(),
//                                          make.QualIdent(syms.runtimeExceptionType.tsym),
//                                          com.sun.tools.javac.util.List.of(make.Literal("")),
//                                          null);
//                    nct.type = syms.runtimeExceptionType;
//                    nct.constructor = syms.runtimeExceptionType.tsym.members().getSymbols(
//                            s -> s.getKind() == ElementKind.CONSTRUCTOR && s.type.getParameterTypes().size() == 1 && s.type.getParameterTypes().head.tsym == syms.stringType.tsym
//                    ).iterator().next();
//                    decl.body = make.Block(0, com.sun.tools.javac.util.List.of(make.Throw(nct)));
//                } else {
//                    decl.body = null;
//                }
                Type.MethodType mt;
                if (msym.type.hasTag(TypeTag.FORALL)) {
                    ForAll fa = (ForAll) msym.type;
                    fa.tvars = error2Object(fa.tvars);
                    mt = fa.asMethodType();
                } else {
                    mt = (Type.MethodType) msym.type;
                }
                clearMethodType(mt);
                if (msym.erasure_field != null && msym.erasure_field.hasTag(TypeTag.METHOD))
                    clearMethodType((Type.MethodType) msym.erasure_field);
                clearAnnotations(decl.sym.getMetadata());
                if (decl.sym.defaultValue != null && isAnnotationErroneous(decl.sym.defaultValue)) {
                    decl.sym.defaultValue = null;
                }
                return null;
            }

            private void clearMethodType(Type.MethodType mt) {
                mt.restype = error2Object(mt.restype);
                mt.argtypes = error2Object(mt.argtypes);
                mt.thrown = error2Object(mt.thrown);
            }

            @Override
            public Void visitBlock(BlockTree node, Void p) {
                boolean prevErrorFound = errorFound;
                errorFound = false;
                super.visitBlock(node, p);
                SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> blockDiags = treeDiags(node);
                if (errorFound || !blockDiags.isEmpty()) {
                    ((JCBlock) node).stats = com.sun.tools.javac.util.List.of(throwTree(blockDiags));
                    blockDiags.clear();
                }
                errorFound = prevErrorFound;
                return null;
            }

            private JCStatement throwTree(SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> diags) {
                String message = diags.isEmpty() ? null
                                                 : DIAGNOSTIC_TO_TEXT.apply(diags.values().iterator().next().get(0));
                return throwTree(message);
            }

            private JCStatement throwTree(String message) {
                message = message == null ? "Uncompilable code"
                                          : "Uncompilable code - " + message;
                JCNewClass nct =
                        make.NewClass(null,
                                      com.sun.tools.javac.util.List.nil(),
                                      make.QualIdent(syms.runtimeExceptionType.tsym),
                                      com.sun.tools.javac.util.List.of(make.Literal(message)),
                                      null);
                nct.type = syms.runtimeExceptionType;
                //find the constructor for RuntimeException(String):
                for (Element el : ElementFilter.constructorsIn(syms.runtimeExceptionType.tsym.getEnclosedElements())) {
                    Symbol s = (Symbol) el;

                    if (s.getKind() == ElementKind.CONSTRUCTOR && s.type.getParameterTypes().size() == 1 && s.type.getParameterTypes().head.tsym == syms.stringType.tsym) {
                        nct.constructor = s;
                        break;
                    }
                }
                return make.Throw(nct);
            }

            @Override
            public Void visitClass(ClassTree node, Void p) {
                Set<JCNewClass> oldAnonymousClasses = anonymousClasses;
                ClassSymbol prevCurrentClass = currentClass;
                boolean prevErrorFound = errorFound;
                errorFound = false;
                try {
                    anonymousClasses = Collections.newSetFromMap(new LinkedHashMap<>());
                JCClassDecl clazz = (JCTree.JCClassDecl) node;
                Symbol.ClassSymbol csym = clazz.sym;
                if (isErroneousClass(csym)) {
                    //likely a duplicate of another class, don't touch:
                    return null;
                }
                if (isOtherClass(csym)) {
                    // Something went somewhere the csym.type is Type.Unknown,
                    // do not go any further
                    return null;
                }
                currentClass = csym;
                Type.ClassType ct = (Type.ClassType) csym.type;
                if (csym == syms.objectType.tsym) {
                    ct.all_interfaces_field = com.sun.tools.javac.util.List.nil();
                    ct.allparams_field = com.sun.tools.javac.util.List.nil();
                    ct.interfaces_field = com.sun.tools.javac.util.List.nil();
                    ct.typarams_field = com.sun.tools.javac.util.List.nil();
                    ct.supertype_field = Type.noType;
                } else {
                    ct.all_interfaces_field = error2Object(ct.all_interfaces_field);
                    ct.allparams_field = error2Object(ct.allparams_field);
                    ct.interfaces_field = error2Object(ct.interfaces_field);
                    ct.typarams_field = error2Object(ct.typarams_field);
                    ct.supertype_field = error2Object(ct.supertype_field);
                }
                clearAnnotations(clazz.sym.getMetadata());
                for (RecordComponent rc : clazz.sym.getRecordComponents()) {
                    rc.type = error2Object(rc.type);
                    scan(rc.accessorMeth, p);
                    if (rc.accessor == null) {
                        //the accessor is not created when the component type matches
                        //a non-arg j.l.Object method (which is a compile-time error)
                        //but the missing accessor will break Lower.
                        //initialize the field:
                        rc.accessor = new MethodSymbol(0, names.empty, new MethodType(com.sun.tools.javac.util.List.nil(), syms.errType, com.sun.tools.javac.util.List.nil(), syms.methodClass), clazz.sym);
                    }
                }
                for (JCTree def : clazz.defs) {
                    boolean errorClass = isErroneousClass(def);
                    if (errorClass) {
                        ClassSymbol member = ((JCClassDecl) def).sym;
                        if (member != null) {
                            csym.members_field.remove(member);
                        }
                    }
                    if (errorClass || def.hasTag(JCTree.Tag.ERRONEOUS)) {
                        clazz.defs = com.sun.tools.javac.util.List.filter(clazz.defs, def);
                    }
                }
                fixRecordMethods(clazz);
                super.visitClass(node, p);
                //remove anonymous classes that remained in the tree from anonymousClasses:
                new TreeScanner<Void, Void>() {
                        @Override
                        public Void visitNewClass(NewClassTree node, Void p) {
                            anonymousClasses.remove(node);
                            return super.visitNewClass(node, p);
                        }

                        @Override
                        public Void visitClass(ClassTree nestedNode, Void p) {
                            if (nestedNode == node) {
                                return super.visitClass(nestedNode, p);
                            }
                            return null;
                        }
                }.scan(node, null);
                if (!anonymousClasses.isEmpty()) {
                    anonymousClasses.forEach(a -> addAnonymousClass(clazz, a));
                }
                SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> classDiags = treeDiags(node);
                if (errorFound || !classDiags.isEmpty()) {
                    clazz.defs = clazz.defs.prepend(make.Block(Flags.STATIC, com.sun.tools.javac.util.List.of(throwTree(classDiags))));
                    classDiags.clear();
                }
                } finally {
                    errorFound = prevErrorFound;
                    currentClass = prevCurrentClass;
                    anonymousClasses = oldAnonymousClasses;
                }
                return null;
            }

            private final Set<String> RECORD_METHODS = new HashSet<>(Arrays.asList("toString", "hashCode", "equals"));

            private void fixRecordMethods(JCClassDecl clazz) {
                if ((clazz.sym.flags() & Flags.RECORD) == 0) {
                    return ;
                }
                Handler prevHandler = dcfh.setHandler(dcfh.speculativeCodeHandler);
                try {
                    try {
                        syms.objectMethodsType.tsym.flags();
                    } catch (CompletionFailure cf) {
                        //ignore
                    }
                    if (!syms.objectMethodsType.tsym.type.isErroneous()) {
                        //ObjectMethods exist:
                        return ;
                    }
                } finally {
                    dcfh.setHandler(prevHandler);
                }
                for (Symbol s : clazz.sym.members().getSymbols(s -> (s.flags() & Flags.RECORD) != 0 && s.kind == Kind.MTH && RECORD_METHODS.contains(s.name.toString()))) {
                    clazz.defs = clazz.defs.prepend(make.MethodDef((MethodSymbol) s, make.Block(0, com.sun.tools.javac.util.List.of(throwTree("java.lang.runtime.ObjectMethods does not exist!")))));
                    s.flags_field &= ~Flags.RECORD;
                }
            }

            private JCStatement clearAndWrapAnonymous(JCNewClass nc) {
                new TreeScanner<Void, Void>() {
                    @Override
                    public Void visitClass(ClassTree node, Void p) {
                        //no nested classes
                        return null;
                    }
                    @Override
                    public Void visitNewClass(NewClassTree node, Void p) {
                        if (node.getClassBody() != null) {
                            addAnonymousClass(nc.def, (JCNewClass) node);
                        }
                        return super.visitNewClass(node, p);
                    }
                    @Override
                    public Void visitMethod(MethodTree node, Void p) {
                        if (!node.getName().contentEquals(ANONYMOUS_CLASSES_METHOD)) {
                            return super.visitMethod(node, p);
                        }
                        return null;
                    }
                }.scan(nc.def.defs, null);
                for (JCTree t : nc.def.defs) {
                    switch (t.getTag()) {
                        case METHODDEF:
                            JCMethodDecl m = (JCMethodDecl) t;
                            if (!m.name.contentEquals(ANONYMOUS_CLASSES_METHOD)) {
                                m.body = make.Block(0, com.sun.tools.javac.util.List.of(throwTree(Collections.emptySortedMap())));
                            }
                            break;
                        case VARDEF:
                            ((JCVariableDecl) t).init = nullExpression(((JCVariableDecl) t).type);
                            break;
                    }
                }
                nc.type = nc.def.type;
                MethodSymbol constructor = (MethodSymbol) nc.constructor;
                ListBuffer<JCExpression> args = new ListBuffer<>();
                int startIdx = 0;
                if (nc.getEnclosingExpression() != null) {
                    startIdx = 1;
                }
                if (nc.clazz.type.tsym.isEnum()) {
                    args.add(nullExpression(syms.stringType));
                    args.add(nullExpression(syms.intType));
                }
                //XXX: should not touch constructor.params, as it can be null - but there is not test for that:
                com.sun.tools.javac.util.List<Type> paramTypes = constructor.type.getParameterTypes();
                for (Type paramType : paramTypes.subList(startIdx, paramTypes.size())) {
                    args.add(nullExpression(paramType));
                }
                nc.args = args.toList();
                return make.Exec(nc);
            }

            private static final String ANONYMOUS_CLASSES_METHOD = "$$anonymousClasses";

            private void addAnonymousClass(JCClassDecl current, JCNewClass anonymous) {
                Optional<JCMethodDecl> anonymousClassesMethodCandidate = current.defs.stream().filter(d -> d.hasTag(Tag.METHODDEF)).map(d -> (JCMethodDecl) d).filter(m -> m.name.contentEquals(ANONYMOUS_CLASSES_METHOD)).findAny();
                JCMethodDecl anonymousClassesMethodTree;
                if (!anonymousClassesMethodCandidate.isPresent()) {
                    MethodSymbol anonymousClassesMethod = new MethodSymbol(0, current.name.table.fromString(ANONYMOUS_CLASSES_METHOD), new MethodType(com.sun.tools.javac.util.List.nil(), syms.voidType, com.sun.tools.javac.util.List.nil(), syms.methodClass), current.sym);
                    current.sym.members_field.enter(anonymousClassesMethod);
                    anonymousClassesMethodTree = make.MethodDef(anonymousClassesMethod, make.Block(0, com.sun.tools.javac.util.List.nil()));
                    current.defs = current.defs.prepend(anonymousClassesMethodTree);
                } else {
                    anonymousClassesMethodTree = anonymousClassesMethodCandidate.get();
                }
                anonymousClassesMethodTree.body.stats = anonymousClassesMethodTree.body.stats.prepend(clearAndWrapAnonymous(anonymous));
            }

            private SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> treeDiags(Tree node) {
                long start = trees.getSourcePositions().getStartPosition(cut, node);
                long end = trees.getSourcePositions().getEndPosition(cut, node);
                SortedMap<Long, List<Diagnostic<? extends JavaFileObject>>> classDiags = start < end ? diags.subMap(start, end) : new TreeMap<>();
                return classDiags;
            }

            @Override
            public Void visitNewClass(NewClassTree node, Void p) {
                //TODO: fix constructors:
                JCNewClass nc = (JCNewClass) node;
                if (node.getClassBody() != null && !nc.clazz.type.hasTag(TypeTag.ERROR)) {
                    if (nc.constructor.kind == Kind.MTH) {
                        //make sure this class is generated even if the code is erroneous:
                        anonymousClasses.add(nc);
                    } else {
                        //TODO: if there is no constructor, skip for now - is there a better solution? See testNewClass
                        errorFound = true;
                    }
                }
//                    nct.constructor = constructor;
//                    nct.constructorType = constructor.type;
//                    nct.def = null;
                errorFound |= isErroneous(trees.getTypeMirror(getCurrentPath())); //isErroneous - enough?
                return super.visitNewClass(node, p);
            }

            @Override
            public Void visitIdentifier(IdentifierTree node, Void p) {
                errorFound |= isErroneous(trees.getTypeMirror(getCurrentPath())); //isErroneous - enough?
                return super.visitIdentifier(node, p);
            }

            @Override
            public Void visitMemberSelect(MemberSelectTree node, Void p) {
                errorFound |= isErroneous(trees.getTypeMirror(getCurrentPath())); //isErroneous - enough?
                if (node.getExpression().getKind() == Tree.Kind.IDENTIFIER &&
                    ((IdentifierTree) node.getExpression()).getName().contentEquals("super")) {
                    TreePath selected = new TreePath(getCurrentPath(), node.getExpression());
                    Element selectedEl = trees.getElement(selected);
                    if (selectedEl != null && !Objects.equals(selectedEl.getEnclosingElement(), currentClass)) {
                        errorFound = true;
                    }
                }
                return super.visitMemberSelect(node, p);
            }

            @Override
            public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
                Type varArgs = ((JCMethodInvocation) node).varargsElement;
                if (varArgs != null) {
                    errorFound |= isErroneous(varArgs);
                }
                return super.visitMethodInvocation(node, p);
            }

            @Override
            public Void visitMemberReference(MemberReferenceTree node, Void p) {
                JCMemberReference ref = (JCMemberReference) node;
                ref.target = error2Object(ref.target);
                return super.visitMemberReference(node, p);
            }

            @Override
            public Void visitBindingPattern(BindingPatternTree node, Void p) {
                return super.visitBindingPattern(node, p);
            }

            @Override
            public Void visitDeconstructionPattern(DeconstructionPatternTree node, Void p) {
                errorFound |= !hasMatchException;
                return super.visitDeconstructionPattern(node, p);
            }

            @Override
            public Void visitSwitch(SwitchTree node, Void p) {
                JCSwitch swt = (JCSwitch) node;
                handleSwitch(swt.patternSwitch);
                return super.visitSwitch(node, p);
            }

            @Override
            public Void visitSwitchExpression(SwitchExpressionTree node, Void p) {
                JCSwitchExpression swt = (JCSwitchExpression) node;
                handleSwitch(swt.patternSwitch);
                return super.visitSwitchExpression(node, p);
            }

            private void handleSwitch(boolean patternSwitch) {
                if (patternSwitch && !Feature.PATTERN_SWITCH.allowedInSource(source)) {
                    errorFound = true;
                }
            }

            @Override
            public Void scan(Tree tree, Void p) {
                if (tree != null && ExpressionTree.class.isAssignableFrom(tree.getClass())) {
                    errorFound |= isErroneous(trees.getTypeMirror(new TreePath(getCurrentPath(), tree))); //isErroneous - enough?
                }
                return super.scan(tree, p);
            }

            private void clearAnnotations(SymbolMetadata metadata) {
                if (metadata == null)
                    return;

                //TODO: type annotations, etc.
                com.sun.tools.javac.util.List<Attribute.Compound> annotations = metadata.getDeclarationAttributes();
                com.sun.tools.javac.util.List<Attribute.Compound> prev = null;
                while (annotations.nonEmpty()) {
                    if (isAnnotationErroneous(annotations.head)) {
                        if (prev == null) {
                            metadata.reset();
                            metadata.setDeclarationAttributes(annotations.tail);
                        } else {
                            prev.tail = annotations.tail;
                        }
                    } else {
                        prev = annotations;
                    }
                    annotations = annotations.tail;
                }
            }

            private boolean isAnnotationErroneous(Attribute annotation) {
                if (isErroneous(annotation.type)) {
                    return true;
                } else if (annotation instanceof Attribute.Array) {
                    for (Attribute nested : ((Attribute.Array) annotation).values) {
                        if (isAnnotationErroneous(nested)) {
                            return true;
                        }
                    }
                    return false;
                } else if (annotation instanceof Attribute.Class) {
                    return isErroneous(((Attribute.Class) annotation).classType);
                } else if (annotation instanceof Attribute.Compound) {
                    for (Pair<MethodSymbol, Attribute> p : ((Attribute.Compound) annotation).values) {
                        if (isAnnotationErroneous(p.snd)) {
                            return true;
                        }
                    }
                    return false;
                } else if (annotation instanceof Attribute.Constant) {
                    return false;
                } else if (annotation instanceof Attribute.Enum) {
                    return false;
                } else if (annotation instanceof Attribute.Error) {
                    return true;
                } else {
                    //let's skip all unknown attributes, as we cannot check if they are fine or not
                    return true;
                }
            }

            private boolean isErroneous(TypeMirror type) {
                return type == null || type.getKind() == TypeKind.ERROR || type.getKind() == TypeKind.NONE || type.getKind() == TypeKind.OTHER || (type.getKind() == TypeKind.ARRAY && isErroneous(((ArrayType) type).getComponentType()));
            }

            private boolean errorFound;
            private final Map<Type, Boolean> seen = new IdentityHashMap<>();

            private Type error2Object(Type t) {
                if (t == null)
                    return null;

                if (isErroneous(t)) {
                    errorFound = true;
                    return syms.objectType;
                }

                Boolean err = seen.get(t);
                if (err != null) {
                    errorFound |= err;
                    return t;
                }

                seen.put(t, false);
                boolean prevErrorFound = errorFound;
                errorFound = false;
                switch (t.getKind()) {
                    case DECLARED: {
                        resolveErrors((ClassType) t);
                        break;
                    }
                    case WILDCARD: {
                        Type.WildcardType wt = ((Type.WildcardType) t);
                        wt.type = error2Object(wt.type);
                        TypeVar tv = wt.bound;
                        if (tv != null) {
                            clearTypeVar(tv);
                        }
                        break;
                    }
                    case TYPEVAR: {
                        clearTypeVar((Type.TypeVar) t);
                        break;
                    }
                    case ARRAY: {
                        Type.ArrayType at = (Type.ArrayType) t;
                        Type component = error2Object(at.elemtype);
                        if (component != at.elemtype) {
                            at.elemtype = types.makeArrayType(component);
                        }
                        break;
                    }
                }
                seen.put(t, errorFound);
                errorFound |= prevErrorFound;

                return t;
            }

            private void clearTypeVar(Type.TypeVar tv) {
                String[] boundNames = {"bound", "_bound"};
                for (String boundName : boundNames) {
                    try {
                        Field bound = tv.getClass().getDeclaredField(boundName);
                        bound.setAccessible(true);
                        bound.set(tv, error2Object((Type) bound.get(tv)));
                    } catch (IllegalAccessException | NoSuchFieldException | SecurityException ex) {
                        JavaIndex.LOG.log(Level.FINEST, null, ex);
                    }
                }
                tv.lower = error2Object(tv.lower);
            }

            private com.sun.tools.javac.util.List<Type> error2Object(com.sun.tools.javac.util.List<Type> types) {
                if (types == null)
                    return null;

                ListBuffer<Type> lb = new ListBuffer<>();
                boolean changed = false;
                for (Type t : types) {
                    Type nue = error2Object(t);
                    changed |= nue != t;
                    lb.append(nue);
                }
                return changed ? lb.toList() : types;
            }

            private void resolveErrors(ClassType ct) {
                if (ct.tsym == syms.objectType.tsym) return ;
                ct.all_interfaces_field = error2Object(ct.all_interfaces_field);
                ct.allparams_field = error2Object(ct.allparams_field); //TODO: should replace with bounds
                ct.interfaces_field = error2Object(ct.interfaces_field);
                ct.typarams_field = error2Object(ct.typarams_field);
                ct.supertype_field = error2Object(ct.supertype_field);
            }
        }.scan(cut, null);
        fixedListener.accept(((JCCompilationUnit) cut).sourcefile, cut);
    }