@Override public boolean handle( AnnotationValues<Synchronized> annotation, JCAnnotation ast, Node annotationNode) { Node methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != Kind.METHOD || !(methodNode.get() instanceof JCMethodDecl)) { annotationNode.addError("@Synchronized is legal only on methods."); return true; } JCMethodDecl method = (JCMethodDecl) methodNode.get(); if ((method.mods.flags & Flags.ABSTRACT) != 0) { annotationNode.addError("@Synchronized is legal only on concrete methods."); return true; } boolean isStatic = (method.mods.flags & Flags.STATIC) != 0; String lockName = annotation.getInstance().value(); boolean autoMake = false; if (lockName.length() == 0) { autoMake = true; lockName = isStatic ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } TreeMaker maker = methodNode.getTreeMaker(); if (fieldExists(lockName, methodNode) == MemberExistsResult.NOT_EXISTS) { if (!autoMake) { annotationNode.addError("The field " + new String(lockName) + " does not exist."); return true; } JCExpression objectType = chainDots(maker, methodNode, "java", "lang", "Object"); // We use 'new Object[0];' because quite unlike 'new Object();', empty arrays *ARE* // serializable! JCNewArray newObjectArray = maker.NewArray( chainDots(maker, methodNode, "java", "lang", "Object"), List.<JCExpression>of(maker.Literal(TypeTags.INT, 0)), null); JCVariableDecl fieldDecl = maker.VarDef( maker.Modifiers(Flags.FINAL | (isStatic ? Flags.STATIC : 0)), methodNode.toName(lockName), objectType, newObjectArray); injectField(methodNode.up(), fieldDecl); } if (method.body == null) return false; JCExpression lockNode = maker.Ident(methodNode.toName(lockName)); method.body = maker.Block(0, List.<JCStatement>of(maker.Synchronized(lockNode, method.body))); methodNode.rebuild(); return true; }
/** * Adds the given new method declaration to the provided type AST Node. Can also inject * constructors. * * <p>Also takes care of updating the JavacAST. */ public static void injectMethod(JavacNode typeNode, JCMethodDecl method) { JCClassDecl type = (JCClassDecl) typeNode.get(); if (method.getName().contentEquals("<init>")) { // Scan for default constructor, and remove it. int idx = 0; for (JCTree def : type.defs) { if (def instanceof JCMethodDecl) { if ((((JCMethodDecl) def).mods.flags & Flags.GENERATEDCONSTR) != 0) { JavacNode tossMe = typeNode.getNodeFor(def); if (tossMe != null) tossMe.up().removeChild(tossMe); type.defs = addAllButOne(type.defs, idx); if (type.sym != null && type.sym.members_field != null) { type.sym.members_field.remove(((JCMethodDecl) def).sym); } break; } } idx++; } } addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method)); type.defs = type.defs.append(method); typeNode.add(method, Kind.METHOD); }
@Override public void visitMethodDef(JCMethodDecl tree) { super.visitMethodDef(tree); MethodSymbol meth = tree.sym; if (meth == null || meth.kind != Kinds.MTH) return; TreePath treePath = docenv.getTreePath(env.toplevel, env.enclClass, tree); if (meth.isConstructor()) docenv.makeConstructorDoc(meth, treePath); else if (isAnnotationTypeElement(meth)) docenv.makeAnnotationTypeElementDoc(meth, treePath); else docenv.makeMethodDoc(meth, treePath); // release resources tree.body = null; }
public static void main(String... args) throws IOException, URISyntaxException { if (args.length != 1) throw new IllegalStateException("Must provide class name!"); String testContent = null; List<File> sourcePath = new ArrayList<>(); for (String sourcePaths : System.getProperty("test.src.path").split(":")) { sourcePath.add(new File(sourcePaths)); } JavacFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); for (File sp : sourcePath) { File inp = new File(sp, args[0]); if (inp.canRead()) { testContent = fm.getRegularFile(inp.toPath()).getCharContent(true).toString(); } } if (testContent == null) throw new IllegalStateException(); final List<Diagnostic<?>> diagnostics = new ArrayList<>(); DiagnosticListener<JavaFileObject> collectDiagnostics = new DiagnosticListener<JavaFileObject>() { @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) { diagnostics.add(diagnostic); } }; JavaFileObject testFile = new TestFO(new URI("mem://" + args[0]), testContent); JavacTask task = JavacTool.create() .getTask( null, new TestFM(fm), collectDiagnostics, STANDARD_PARAMS, null, Arrays.asList(testFile)); final Trees trees = Trees.instance(task); final CompilationUnitTree cut = task.parse().iterator().next(); task.analyze(); final List<int[]> declarationSpans = new ArrayList<>(); new TreeScanner<Void, Void>() { @Override public Void visitClass(ClassTree node, Void p) { handleDeclaration(node); return super.visitClass(node, p); } @Override public Void visitMethod(MethodTree node, Void p) { handleDeclaration(node); return super.visitMethod(node, p); } @Override public Void visitVariable(VariableTree node, Void p) { handleDeclaration(node); return super.visitVariable(node, p); } @Override public Void visitNewClass(NewClassTree node, Void p) { if (node.getClassBody() != null) { scan(node.getClassBody().getMembers(), null); } return null; } private void handleDeclaration(Tree node) { int endPos = (int) trees.getSourcePositions().getEndPosition(cut, node); if (endPos == (-1)) { if (node.getKind() == Tree.Kind.METHOD && (((JCMethodDecl) node).getModifiers().flags & Flags.GENERATEDCONSTR) != 0) { return; } throw new IllegalStateException(); } declarationSpans.add( new int[] {(int) trees.getSourcePositions().getStartPosition(cut, node), endPos}); } }.scan(cut, null); for (final int[] declarationSpan : declarationSpans) { final String suppressWarnings = "@SuppressWarnings({\"deprecation\", \"unchecked\", \"serial\", \"divzero\"})"; final String updatedContent = testContent.substring(0, declarationSpan[0]) + suppressWarnings + testContent.substring(declarationSpan[0]); final List<Diagnostic<?>> foundErrors = new ArrayList<>(diagnostics); DiagnosticListener<JavaFileObject> verifyDiagnostics = new DiagnosticListener<JavaFileObject>() { @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) { long adjustedPos = diagnostic.getPosition(); if (adjustedPos >= declarationSpan[0]) adjustedPos -= suppressWarnings.length(); if (declarationSpan[0] <= adjustedPos && adjustedPos <= declarationSpan[1]) { throw new IllegalStateException("unsuppressed: " + diagnostic.getMessage(null)); } boolean found = false; for (Iterator<Diagnostic<?>> it = foundErrors.iterator(); it.hasNext(); ) { Diagnostic<?> d = it.next(); if (d.getPosition() == adjustedPos && d.getCode().equals(diagnostic.getCode())) { it.remove(); found = true; break; } } if (!found) { throw new IllegalStateException( "diagnostic not originally reported: " + diagnostic.getMessage(null)); } } }; JavaFileObject updatedFile = new TestFO(new URI("mem://" + args[0]), updatedContent); JavacTask testTask = JavacTool.create() .getTask( null, new TestFM(fm), verifyDiagnostics, STANDARD_PARAMS, null, Arrays.asList(updatedFile)); testTask.analyze(); for (Diagnostic<?> d : foundErrors) { if (d.getPosition() < declarationSpan[0] || declarationSpan[1] < d.getPosition()) { throw new IllegalStateException("missing: " + d.getMessage(null)); } } } }
public void visitMethodDef(JCMethodDecl node) { node.sym = null; super.visitMethodDef(node); }
@Override public void handle( AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); Builder builderInstance = annotation.getInstance(); String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); 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; } deleteAnnotationIfNeccessary(annotationNode, Builder.class); deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder"); JavacNode parent = annotationNode.up(); java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>(); java.util.List<Name> namesOfParameters = new ArrayList<Name>(); JCExpression returnType; List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrownExceptions = List.nil(); Name nameOfStaticBuilderMethod; JavacNode tdParent; JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null; 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(); // final fields with an initializer cannot be written to, so they can't be 'builderized'. // Unfortunately presence of @Value makes // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this // math ourselves. // 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; namesOfParameters.add(removePrefixFromField(fieldNode)); typesOfParameters.add(fd.vartype); allFields.append(fieldNode); } new HandleConstructor() .generateConstructor( tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) { if (!fillParametersFrom.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 = fillParametersFrom.thrown; nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null) { tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) { annotationNode.addError( "@Builder is only supported on types, constructors, and static methods."); return; } returnType = fillParametersFrom.restype; typeParams = fillParametersFrom.typarams; thrownExceptions = fillParametersFrom.thrown; nameOfStaticBuilderMethod = fillParametersFrom.name; if (builderClassName.isEmpty()) { if (returnType instanceof JCTypeApply) { returnType = ((JCTypeApply) returnType).clazz; } 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"; } } } else { annotationNode.addError( "@Builder is only supported on types, constructors, and static methods."); return; } if (fillParametersFrom != null) { for (JCVariableDecl param : fillParametersFrom.params) { namesOfParameters.add(param.name); typesOfParameters.add(param.vartype); } } JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast); } else { sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); } java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast); java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>(); for (JavacNode fieldNode : fieldNodes) { JCMethodDecl newMethod = makeSetterMethodForBuilder( builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain()); if (newMethod != null) newMethods.add(newMethod); } if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), null, annotationNode); if (cd != null) injectMethod(builderType, cd); } for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod); if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuildMethod( buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions); if (md != null) injectMethod(builderType, md); } if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = HandleToString.createToString( builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); if (md != null) injectMethod(builderType, md); } if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams); if (md != null) injectMethod(tdParent, md); } }