Search in sources :

Example 1 with JCFieldAccess

use of com.sun.tools.javac.tree.JCTree.JCFieldAccess in project lombok by rzwitserloot.

the class HandleBuilder method handle.

@Override
public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
    Builder builderInstance = annotation.getInstance();
    // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
    boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
    boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
    String builderMethodName = builderInstance.builderMethodName();
    String buildMethodName = builderInstance.buildMethodName();
    String builderClassName = builderInstance.builderClassName();
    String toBuilderMethodName = "toBuilder";
    boolean toBuilder = builderInstance.toBuilder();
    java.util.List<Name> typeArgsForToBuilder = null;
    if (builderMethodName == null)
        builderMethodName = "builder";
    if (buildMethodName == null)
        buildMethodName = "build";
    if (builderClassName == null)
        builderClassName = "";
    if (!checkName("builderMethodName", builderMethodName, annotationNode))
        return;
    if (!checkName("buildMethodName", buildMethodName, annotationNode))
        return;
    if (!builderClassName.isEmpty()) {
        if (!checkName("builderClassName", builderClassName, annotationNode))
            return;
    }
    @SuppressWarnings("deprecation") Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class;
    deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder);
    JavacNode parent = annotationNode.up();
    java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
    JCExpression returnType;
    List<JCTypeParameter> typeParams = List.nil();
    List<JCExpression> thrownExceptions = List.nil();
    Name nameOfBuilderMethod;
    JavacNode tdParent;
    JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
    boolean addCleaning = false;
    boolean isStatic = true;
    if (parent.get() instanceof JCClassDecl) {
        tdParent = parent;
        JCClassDecl td = (JCClassDecl) tdParent.get();
        ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();
        @SuppressWarnings("deprecation") boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
        for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
            JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
            // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
            if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode))
                continue;
            BuilderFieldData bfd = new BuilderFieldData();
            bfd.rawName = fd.name;
            bfd.name = removePrefixFromField(fieldNode);
            bfd.type = fd.vartype;
            bfd.singularData = getSingularData(fieldNode);
            addObtainVia(bfd, fieldNode);
            builderFields.add(bfd);
            allFields.append(fieldNode);
        }
        new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode);
        returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
        typeParams = td.typarams;
        thrownExceptions = List.nil();
        nameOfBuilderMethod = null;
        if (builderClassName.isEmpty())
            builderClassName = td.name.toString() + "Builder";
    } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
        JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
        if (!jmd.typarams.isEmpty()) {
            annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
            return;
        }
        tdParent = parent.up();
        JCClassDecl td = (JCClassDecl) tdParent.get();
        returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
        typeParams = td.typarams;
        thrownExceptions = jmd.thrown;
        nameOfBuilderMethod = null;
        if (builderClassName.isEmpty())
            builderClassName = td.name.toString() + "Builder";
    } else if (fillParametersFrom != null) {
        tdParent = parent.up();
        JCClassDecl td = (JCClassDecl) tdParent.get();
        JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
        isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
        JCExpression fullReturnType = jmd.restype;
        returnType = fullReturnType;
        typeParams = jmd.typarams;
        thrownExceptions = jmd.thrown;
        nameOfBuilderMethod = jmd.name;
        if (returnType instanceof JCTypeApply) {
            returnType = ((JCTypeApply) returnType).clazz;
        }
        if (builderClassName.isEmpty()) {
            if (returnType instanceof JCFieldAccess) {
                builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
            } else if (returnType instanceof JCIdent) {
                Name n = ((JCIdent) returnType).name;
                for (JCTypeParameter tp : typeParams) {
                    if (tp.name.equals(n)) {
                        annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
                        return;
                    }
                }
                builderClassName = n.toString() + "Builder";
            } else if (returnType instanceof JCPrimitiveTypeTree) {
                builderClassName = returnType.toString() + "Builder";
                if (Character.isLowerCase(builderClassName.charAt(0))) {
                    builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
                }
            } else {
                // This shouldn't happen.
                System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
                builderClassName = td.name.toString() + "Builder";
            }
        }
        if (toBuilder) {
            final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
            if (returnType instanceof JCArrayTypeTree) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            Name simpleName;
            String pkg;
            List<JCExpression> tpOnRet = List.nil();
            if (fullReturnType instanceof JCTypeApply) {
                tpOnRet = ((JCTypeApply) fullReturnType).arguments;
            }
            if (returnType instanceof JCIdent) {
                simpleName = ((JCIdent) returnType).name;
                pkg = null;
            } else if (returnType instanceof JCFieldAccess) {
                JCFieldAccess jcfa = (JCFieldAccess) returnType;
                simpleName = jcfa.name;
                pkg = unpack(jcfa.selected);
                if (pkg.startsWith("ERR:")) {
                    String err = pkg.substring(4, pkg.indexOf("__ERR__"));
                    annotationNode.addError(err);
                    return;
                }
            } else {
                annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName());
                return;
            }
            if (pkg != null && !parent.getPackageDeclaration().equals(pkg)) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            if (!tdParent.getName().contentEquals(simpleName)) {
                annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
                return;
            }
            List<JCTypeParameter> tpOnMethod = jmd.typarams;
            List<JCTypeParameter> tpOnType = ((JCClassDecl) tdParent.get()).typarams;
            typeArgsForToBuilder = new ArrayList<Name>();
            for (JCTypeParameter tp : tpOnMethod) {
                int pos = -1;
                int idx = -1;
                for (JCExpression tOnRet : tpOnRet) {
                    idx++;
                    if (!(tOnRet instanceof JCIdent))
                        continue;
                    if (((JCIdent) tOnRet).name != tp.name)
                        continue;
                    pos = idx;
                }
                if (pos == -1 || tpOnType.size() <= pos) {
                    annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + tp.name + " is not part of the return type.");
                    return;
                }
                typeArgsForToBuilder.add(tpOnType.get(pos).name);
            }
        }
    } else {
        annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
        return;
    }
    if (fillParametersFrom != null) {
        for (JavacNode param : fillParametersFrom.down()) {
            if (param.getKind() != Kind.ARGUMENT)
                continue;
            BuilderFieldData bfd = new BuilderFieldData();
            JCVariableDecl raw = (JCVariableDecl) param.get();
            bfd.name = raw.name;
            bfd.rawName = raw.name;
            bfd.type = raw.vartype;
            bfd.singularData = getSingularData(param);
            addObtainVia(bfd, param);
            builderFields.add(bfd);
        }
    }
    JavacNode builderType = findInnerClass(tdParent, builderClassName);
    if (builderType == null) {
        builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast);
    } else {
        JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get();
        if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
            annotationNode.addError("Existing Builder must be a static inner class.");
            return;
        } else if (!isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
            annotationNode.addError("Existing Builder must be a non-static inner class.");
            return;
        }
        sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
        /* generate errors for @Singular BFDs that have one already defined node. */
        {
            for (BuilderFieldData bfd : builderFields) {
                SingularData sd = bfd.singularData;
                if (sd == null)
                    continue;
                JavacSingularizer singularizer = sd.getSingularizer();
                if (singularizer == null)
                    continue;
                if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) {
                    bfd.singularData = null;
                }
            }
        }
    }
    for (BuilderFieldData bfd : builderFields) {
        if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
            if (bfd.singularData.getSingularizer().requiresCleaning()) {
                addCleaning = true;
                break;
            }
        }
        if (bfd.obtainVia != null) {
            if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
                bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
                return;
            }
            if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
                bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
                return;
            }
        }
    }
    generateBuilderFields(builderType, builderFields, ast);
    if (addCleaning) {
        JavacTreeMaker maker = builderType.getTreeMaker();
        JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
        injectFieldAndMarkGenerated(builderType, uncleanField);
    }
    if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
        JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, annotationNode);
        if (cd != null)
            injectMethod(builderType, cd);
    }
    for (BuilderFieldData bfd : builderFields) {
        makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
    }
    if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
        JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
        if (md != null)
            injectMethod(builderType, md);
    }
    if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
        java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
        for (BuilderFieldData bfd : builderFields) {
            fieldNodes.addAll(bfd.createdFields);
        }
        JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
        if (md != null)
            injectMethod(builderType, md);
    }
    if (addCleaning)
        injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
    if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
        JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams);
        recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
        if (md != null)
            injectMethod(tdParent, md);
    }
    if (toBuilder) {
        switch(methodExists(toBuilderMethodName, tdParent, 0)) {
            case EXISTS_BY_USER:
                annotationNode.addWarning("Not generating toBuilder() as it already exists.");
                return;
            case NOT_EXISTS:
                List<JCTypeParameter> tps = typeParams;
                if (typeArgsForToBuilder != null) {
                    ListBuffer<JCTypeParameter> lb = new ListBuffer<JCTypeParameter>();
                    JavacTreeMaker maker = tdParent.getTreeMaker();
                    for (Name n : typeArgsForToBuilder) {
                        lb.append(maker.TypeParameter(n, List.<JCExpression>nil()));
                    }
                    tps = lb.toList();
                }
                JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
                if (md != null)
                    injectMethod(tdParent, md);
        }
    }
    recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
}
Also used : JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) Builder(lombok.Builder) ListBuffer(com.sun.tools.javac.util.ListBuffer) ArrayList(java.util.ArrayList) JCTypeApply(com.sun.tools.javac.tree.JCTree.JCTypeApply) JavacSingularizer(lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer) Name(com.sun.tools.javac.util.Name) JCPrimitiveTypeTree(com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree) JavacNode(lombok.javac.JavacNode) JCArrayTypeTree(com.sun.tools.javac.tree.JCTree.JCArrayTypeTree) JCClassDecl(com.sun.tools.javac.tree.JCTree.JCClassDecl) JCIdent(com.sun.tools.javac.tree.JCTree.JCIdent) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) JavacTreeMaker(lombok.javac.JavacTreeMaker) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) JCTypeParameter(com.sun.tools.javac.tree.JCTree.JCTypeParameter) JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) SingularData(lombok.javac.handlers.JavacSingularsRecipes.SingularData)

Example 2 with JCFieldAccess

use of com.sun.tools.javac.tree.JCTree.JCFieldAccess in project lombok by rzwitserloot.

the class HandleBuilder method unpack.

private static void unpack(StringBuilder sb, JCExpression expr) {
    if (expr instanceof JCIdent) {
        sb.append(((JCIdent) expr).name.toString());
        return;
    }
    if (expr instanceof JCFieldAccess) {
        JCFieldAccess jcfa = (JCFieldAccess) expr;
        unpack(sb, jcfa.selected);
        sb.append(".").append(jcfa.name.toString());
        return;
    }
    if (expr instanceof JCTypeApply) {
        sb.setLength(0);
        sb.append("ERR:");
        sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
        sb.append("__ERR__");
        return;
    }
    sb.setLength(0);
    sb.append("ERR:");
    sb.append("Expected a type of some sort, not a " + expr.getClass().getName());
    sb.append("__ERR__");
}
Also used : JCIdent(com.sun.tools.javac.tree.JCTree.JCIdent) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) JCTypeApply(com.sun.tools.javac.tree.JCTree.JCTypeApply)

Example 3 with JCFieldAccess

use of com.sun.tools.javac.tree.JCTree.JCFieldAccess in project lombok by rzwitserloot.

the class HandleCleanup method handle.

@Override
public void handle(AnnotationValues<Cleanup> annotation, JCAnnotation ast, JavacNode annotationNode) {
    handleFlagUsage(annotationNode, ConfigurationKeys.CLEANUP_FLAG_USAGE, "@Cleanup");
    if (inNetbeansEditor(annotationNode))
        return;
    deleteAnnotationIfNeccessary(annotationNode, Cleanup.class);
    String cleanupName = annotation.getInstance().value();
    if (cleanupName.length() == 0) {
        annotationNode.addError("cleanupName cannot be the empty string.");
        return;
    }
    if (annotationNode.up().getKind() != Kind.LOCAL) {
        annotationNode.addError("@Cleanup is legal only on local variable declarations.");
        return;
    }
    JCVariableDecl decl = (JCVariableDecl) annotationNode.up().get();
    if (decl.init == null) {
        annotationNode.addError("@Cleanup variable declarations need to be initialized.");
        return;
    }
    JavacNode ancestor = annotationNode.up().directUp();
    JCTree blockNode = ancestor.get();
    final List<JCStatement> statements;
    if (blockNode instanceof JCBlock) {
        statements = ((JCBlock) blockNode).stats;
    } else if (blockNode instanceof JCCase) {
        statements = ((JCCase) blockNode).stats;
    } else if (blockNode instanceof JCMethodDecl) {
        statements = ((JCMethodDecl) blockNode).body.stats;
    } else {
        annotationNode.addError("@Cleanup is legal only on a local variable declaration inside a block.");
        return;
    }
    boolean seenDeclaration = false;
    ListBuffer<JCStatement> newStatements = new ListBuffer<JCStatement>();
    ListBuffer<JCStatement> tryBlock = new ListBuffer<JCStatement>();
    for (JCStatement statement : statements) {
        if (!seenDeclaration) {
            if (statement == decl)
                seenDeclaration = true;
            newStatements.append(statement);
        } else {
            tryBlock.append(statement);
        }
    }
    if (!seenDeclaration) {
        annotationNode.addError("LOMBOK BUG: Can't find this local variable declaration inside its parent.");
        return;
    }
    doAssignmentCheck(annotationNode, tryBlock.toList(), decl.name);
    JavacTreeMaker maker = annotationNode.getTreeMaker();
    JCFieldAccess cleanupMethod = maker.Select(maker.Ident(decl.name), annotationNode.toName(cleanupName));
    List<JCStatement> cleanupCall = List.<JCStatement>of(maker.Exec(maker.Apply(List.<JCExpression>nil(), cleanupMethod, List.<JCExpression>nil())));
    JCExpression preventNullAnalysis = preventNullAnalysis(maker, annotationNode, maker.Ident(decl.name));
    JCBinary isNull = maker.Binary(CTC_NOT_EQUAL, preventNullAnalysis, maker.Literal(CTC_BOT, null));
    JCIf ifNotNullCleanup = maker.If(isNull, maker.Block(0, cleanupCall), null);
    Context context = annotationNode.getContext();
    JCBlock finalizer = recursiveSetGeneratedBy(maker.Block(0, List.<JCStatement>of(ifNotNullCleanup)), ast, context);
    newStatements.append(setGeneratedBy(maker.Try(setGeneratedBy(maker.Block(0, tryBlock.toList()), ast, context), List.<JCCatch>nil(), finalizer), ast, context));
    if (blockNode instanceof JCBlock) {
        ((JCBlock) blockNode).stats = newStatements.toList();
    } else if (blockNode instanceof JCCase) {
        ((JCCase) blockNode).stats = newStatements.toList();
    } else if (blockNode instanceof JCMethodDecl) {
        ((JCMethodDecl) blockNode).body.stats = newStatements.toList();
    } else
        throw new AssertionError("Should not get here");
    ancestor.rebuild();
}
Also used : Context(com.sun.tools.javac.util.Context) JCBlock(com.sun.tools.javac.tree.JCTree.JCBlock) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) JavacTreeMaker(lombok.javac.JavacTreeMaker) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) ListBuffer(com.sun.tools.javac.util.ListBuffer) JCTree(com.sun.tools.javac.tree.JCTree) JCBinary(com.sun.tools.javac.tree.JCTree.JCBinary) JCStatement(com.sun.tools.javac.tree.JCTree.JCStatement) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) JCExpression(com.sun.tools.javac.tree.JCTree.JCExpression) JavacNode(lombok.javac.JavacNode) JCIf(com.sun.tools.javac.tree.JCTree.JCIf) JCCase(com.sun.tools.javac.tree.JCTree.JCCase)

Example 4 with JCFieldAccess

use of com.sun.tools.javac.tree.JCTree.JCFieldAccess in project lombok by rzwitserloot.

the class JavacImportList method applyNameToStarImports.

@Override
public Collection<String> applyNameToStarImports(String startsWith, String name) {
    ArrayList<String> out = new ArrayList<String>();
    if (pkgStr != null && topLevelName(pkgStr).equals(startsWith))
        out.add(pkgStr + "." + name);
    for (JCTree def : defs) {
        if (!(def instanceof JCImport))
            continue;
        if (((JCImport) def).staticImport)
            continue;
        JCTree qual = ((JCImport) def).qualid;
        if (!(qual instanceof JCFieldAccess))
            continue;
        String simpleName = ((JCFieldAccess) qual).name.toString();
        if (!"*".equals(simpleName))
            continue;
        String topLevelName = topLevelName(qual);
        if (topLevelName.equals(startsWith)) {
            out.add(((JCFieldAccess) qual).selected.toString() + "." + name);
        }
    }
    return out;
}
Also used : JCImport(com.sun.tools.javac.tree.JCTree.JCImport) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) ArrayList(java.util.ArrayList) JCTree(com.sun.tools.javac.tree.JCTree)

Example 5 with JCFieldAccess

use of com.sun.tools.javac.tree.JCTree.JCFieldAccess in project lombok by rzwitserloot.

the class HandleDelegate method handle.

@Override
public void handle(AnnotationValues<Delegate> annotation, JCAnnotation ast, JavacNode annotationNode) {
    handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.DELEGATE_FLAG_USAGE, "@Delegate");
    @SuppressWarnings("deprecation") Class<? extends Annotation> oldDelegate = lombok.Delegate.class;
    deleteAnnotationIfNeccessary(annotationNode, Delegate.class, oldDelegate);
    Type delegateType;
    Name delegateName = annotationNode.toName(annotationNode.up().getName());
    DelegateReceiver delegateReceiver;
    JavacResolution reso = new JavacResolution(annotationNode.getContext());
    JCTree member = annotationNode.up().get();
    if (annotationNode.up().getKind() == Kind.FIELD) {
        if ((((JCVariableDecl) member).mods.flags & Flags.STATIC) != 0) {
            annotationNode.addError(LEGALITY_OF_DELEGATE);
            return;
        }
        delegateReceiver = DelegateReceiver.FIELD;
        delegateType = member.type;
        if (delegateType == null)
            reso.resolveClassMember(annotationNode.up());
        delegateType = member.type;
    } else if (annotationNode.up().getKind() == Kind.METHOD) {
        if (!(member instanceof JCMethodDecl)) {
            annotationNode.addError(LEGALITY_OF_DELEGATE);
            return;
        }
        JCMethodDecl methodDecl = (JCMethodDecl) member;
        if (!methodDecl.params.isEmpty() || (methodDecl.mods.flags & Flags.STATIC) != 0) {
            annotationNode.addError(LEGALITY_OF_DELEGATE);
            return;
        }
        delegateReceiver = DelegateReceiver.METHOD;
        delegateType = methodDecl.restype.type;
        if (delegateType == null)
            reso.resolveClassMember(annotationNode.up());
        delegateType = methodDecl.restype.type;
    } else {
        // As the annotation is legal on fields and methods only, javac itself will take care of printing an error message for this.
        return;
    }
    List<Object> delegateTypes = annotation.getActualExpressions("types");
    List<Object> excludeTypes = annotation.getActualExpressions("excludes");
    List<Type> toDelegate = new ArrayList<Type>();
    List<Type> toExclude = new ArrayList<Type>();
    if (delegateTypes.isEmpty()) {
        if (delegateType != null)
            toDelegate.add(delegateType);
    } else {
        for (Object dt : delegateTypes) {
            if (dt instanceof JCFieldAccess && ((JCFieldAccess) dt).name.toString().equals("class")) {
                Type type = ((JCFieldAccess) dt).selected.type;
                if (type == null)
                    reso.resolveClassMember(annotationNode);
                type = ((JCFieldAccess) dt).selected.type;
                if (type != null)
                    toDelegate.add(type);
            }
        }
    }
    for (Object et : excludeTypes) {
        if (et instanceof JCFieldAccess && ((JCFieldAccess) et).name.toString().equals("class")) {
            Type type = ((JCFieldAccess) et).selected.type;
            if (type == null)
                reso.resolveClassMember(annotationNode);
            type = ((JCFieldAccess) et).selected.type;
            if (type != null)
                toExclude.add(type);
        }
    }
    List<MethodSig> signaturesToDelegate = new ArrayList<MethodSig>();
    List<MethodSig> signaturesToExclude = new ArrayList<MethodSig>();
    Set<String> banList = new HashSet<String>();
    banList.addAll(METHODS_IN_OBJECT);
    try {
        for (Type t : toExclude) {
            if (t instanceof ClassType) {
                ClassType ct = (ClassType) t;
                addMethodBindings(signaturesToExclude, ct, annotationNode.getTypesUtil(), banList);
            } else {
                annotationNode.addError("@Delegate can only use concrete class types, not wildcards, arrays, type variables, or primitives.");
                return;
            }
        }
        for (MethodSig sig : signaturesToExclude) {
            banList.add(printSig(sig.type, sig.name, annotationNode.getTypesUtil()));
        }
        for (Type t : toDelegate) {
            if (t instanceof ClassType) {
                ClassType ct = (ClassType) t;
                addMethodBindings(signaturesToDelegate, ct, annotationNode.getTypesUtil(), banList);
            } else {
                annotationNode.addError("@Delegate can only use concrete class types, not wildcards, arrays, type variables, or primitives.");
                return;
            }
        }
        for (MethodSig sig : signaturesToDelegate) generateAndAdd(sig, annotationNode, delegateName, delegateReceiver);
    } catch (DelegateRecursion e) {
        annotationNode.addError(String.format(RECURSION_NOT_ALLOWED, e.member, e.type));
    }
}
Also used : JavacResolution(lombok.javac.JavacResolution) JCMethodDecl(com.sun.tools.javac.tree.JCTree.JCMethodDecl) JCFieldAccess(com.sun.tools.javac.tree.JCTree.JCFieldAccess) ArrayList(java.util.ArrayList) JCTree(com.sun.tools.javac.tree.JCTree) ClassType(com.sun.tools.javac.code.Type.ClassType) JCVariableDecl(com.sun.tools.javac.tree.JCTree.JCVariableDecl) Name(com.sun.tools.javac.util.Name) ClassType(com.sun.tools.javac.code.Type.ClassType) Type(com.sun.tools.javac.code.Type) ExecutableType(javax.lang.model.type.ExecutableType) Delegate(lombok.experimental.Delegate) HashSet(java.util.HashSet)

Aggregations

JCFieldAccess (com.sun.tools.javac.tree.JCTree.JCFieldAccess)35 JCTree (com.sun.tools.javac.tree.JCTree)13 ExpressionTree (com.sun.source.tree.ExpressionTree)12 JCExpression (com.sun.tools.javac.tree.JCTree.JCExpression)11 JCIdent (com.sun.tools.javac.tree.JCTree.JCIdent)11 MethodInvocationTree (com.sun.source.tree.MethodInvocationTree)8 JCVariableDecl (com.sun.tools.javac.tree.JCTree.JCVariableDecl)7 Type (com.sun.tools.javac.code.Type)6 ListBuffer (com.sun.tools.javac.util.ListBuffer)6 JCClassDecl (com.sun.tools.javac.tree.JCTree.JCClassDecl)5 JCImport (com.sun.tools.javac.tree.JCTree.JCImport)5 JCMethodDecl (com.sun.tools.javac.tree.JCTree.JCMethodDecl)5 Name (com.sun.tools.javac.util.Name)5 SuggestedFix (com.google.errorprone.fixes.SuggestedFix)4 Symbol (com.sun.tools.javac.code.Symbol)4 JCPrimitiveTypeTree (com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree)4 JCTypeApply (com.sun.tools.javac.tree.JCTree.JCTypeApply)4 JavacTreeMaker (lombok.javac.JavacTreeMaker)4 Fix (com.google.errorprone.fixes.Fix)3 ClassSymbol (com.sun.tools.javac.code.Symbol.ClassSymbol)3