Repository: javaparser/javaparser

Issue #4163: Calling MethodDeclaration.getDeclarationAsString leads to MethodDelaration.getComment returning no comment
Created: 2023-10-18T09:10:53Z
I am using JavaParser v.3.25.5 to get comments associated to methods.
However, the use of the method getDeclarationAsString remove all the comments.
Here is the code I am using:
    VoidVisitorAdapter<Object> visitor = new VoidVisitorAdapter<Object>() {
        @Override
        public void visit(MethodDeclaration n, Object arg) {
            System.out.println(n.getDeclarationAsString(true, true, true));
            System.out.println(n.getSignature());
            System.out.println(n.getComment());
        }
    };
    File f = new File("./android-30/javax/obex/ClientOperation.java");
    StaticJavaParser.getParserConfiguration().setAttributeComments(true);
    visitor.visit(StaticJavaParser.parse(f), null);

I have the following output:
public void setGetFinalFlag(boolean flag)
setGetFinalFlag(boolean)
Optional[]

If I comment the call to getDeclarationAsString, then I get the comment of the method.
setGetFinalFlag(boolean)
Optional[/**
 * Allows to set flag which will force GET to be always sent as single packet request with
 * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
 * require requests to be sent this way.
 */
]

Duplicate issue #4675: Inexplicable change in the MethodDeclaration, ClassOrInterfaceDeclaration, and CompilationUnit, when calling getDeclarationAsString
Created: 2025-02-11T14:03:28Z
Description
Let's start with a compilation unit like the following:
/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */
package org.apache.commons.cli;

/**
 * The class BasicParser provides a very simple implementation of the {@link Parser#flatten(Options,String[],boolean)
 * flatten} method.
 *
 * @deprecated since 1.3, use the {@link DefaultParser} instead
 */
@Deprecated
public class BasicParser extends Parser {

    /**
     * <p>
     * A simple implementation of {@link Parser}'s abstract {@link Parser#flatten(Options, String[], boolean) flatten}
     * method.
     * </p>
     *
     * <p>
     * <b>Note:</b> {@code options} and {@code stopAtNonOption} are not used in this {@code flatten} method.
     * </p>
     *
     * @param options The command line {@link Options}
     * @param arguments The command line arguments to be parsed
     * @param stopAtNonOption Specifies whether to stop flattening when an non option is found.
     * @return The {@code arguments} String array.
     */
    @Override
    protected String[] flatten(@SuppressWarnings("unused") final Options options, final String[] arguments, @SuppressWarnings("unused") final boolean stopAtNonOption) {
        // just echo the arguments
        return arguments;
    }
}

Processing the ClassOrInterfaceDeclaration defined within it (BasicParser) and extracting the method within it as a MethodDeclaration (the method flatten), calling the api getDeclarationAsString as in the following snippet of code:
cu.findAll(ClassOrInterfaceDeclaration.class).forEach(classDecl -> {
    String className = classDecl.getFullyQualifiedName().get().toString();
    for (MethodDeclaration method : classDecl.getMethods()) {
        ObjectNode methodJson = mapper.createObjectNode();
        methodJson.put("projectName", projectName);
        methodJson.put("className", className);
        methodJson.put("methodSignature", method.getDeclarationAsString(false, false, true));
        methodJson.put("methodSourceCode", method.toString());
        methodsArray.add(methodJson);
    }
});

Both the CompilationUnit, ClassOrInterfaceDeclaration, and MethodDeclaration lose the comments, resulting in the following content:
package org.apache.commons.cli;

@Deprecated
public class BasicParser extends Parser {

    @Override
    protected String[] flatten(@SuppressWarnings("unused") final Options options, final String[] arguments, @SuppressWarnings("unused") final boolean stopAtNonOption) {
        return arguments;
    }
}

Therefore, the following  method.toString() returns:
@Override
protected String[] flatten(@SuppressWarnings("unused") final Options options, final String[] arguments, @SuppressWarnings("unused") final boolean stopAtNonOption) {
    return arguments;
}

instead of:
/**
 * <p>
 * A simple implementation of {@link Parser}'s abstract {@link Parser#flatten(Options, String[], boolean) flatten}
 * method.
 * </p>
 *
 * <p>
 * <b>Note:</b> {@code options} and {@code stopAtNonOption} are not used in this {@code flatten} method.
 * </p>
 *
 * @param options The command line {@link Options}
 * @param arguments The command line arguments to be parsed
 * @param stopAtNonOption Specifies whether to stop flattening when an non option is found.
 * @return The {@code arguments} String array.
 */
@Override
protected String[] flatten(@SuppressWarnings("unused") final Options options, final String[] arguments, @SuppressWarnings("unused") final boolean stopAtNonOption) {
    // just echo the arguments
    return arguments;
}

And the whole CompilationUnit changes to
package org.apache.commons.cli;

@Deprecated
public class BasicParser extends Parser {

    @Override
    protected String[] flatten(@SuppressWarnings("unused") final Options options, final String[] arguments, @SuppressWarnings("unused") final boolean stopAtNonOption) {
        return arguments;
    }
}

Although is possible to overcome the problem with a method.clone().getDeclarationAsString(false, false, true), I suppose it is not a correct behavior and the api getDeclarationAsString should not have this side effect.
See the attached screen recording to analyze the unexpected behavior.

  
    
    

    Screen.Recording.mp4

Closing comment (2025-02-11T14:33:53Z): Related to #4163