@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;
  }
Example #2
0
  /**
   * 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);
  }
Example #3
0
  @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);
 }
Example #6
0
  @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);
    }
  }