Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SuppressWarnings annotation #74

Closed
wants to merge 5 commits into from

Conversation

Nimisha94
Copy link

Addresses #3

This PR allows users to suppress modernizer errors by adding @SuppressWarnings("modernizer") at class/method level.

@gaul
cc @jhaber @stevegutz


How can a user suppress modernizer warnings?

modernizer-annotation-processor should be added as a provided-scoped dependency in the project.
Any class or method can be annotated with @SuppressWarnings("modernizer") to suppress all modernizer errors
in that block of code.

Implementation strategy

  1. The code base has been organized into 3 modules:
    1. modernizer-maven-plugin has the main plugin code.
    2. modernizer-maven-policy has checkstyle rules and is added as a dependency to maven-checkstyle-plugin
      in the plugin's pom.
    3. modernizer-annotation-processor has annotation processing code and is added as a dependency in plugin.
  2. When a project is run, before compiling the source code in the project, annotation processor scans to see if there are any
    methods/classes annotated as @SuppressWarnings("modernizer"). These annotations can be added to methods or classes.
    1. If the annotation is on a class, the processor constructs a regex that matches the fully qualified class name of the
      annotated class and its subclasses.
      For example: org/gaul/package/ClassOne\$ClassTwo(\$.+)?
    2. If the annotation is on a method, the processor constructs a string with 4 parts, delimited by spaces - the fully
      qualified class name, the method name, the return type, and the list of parameters. These are normalized to be compared to the format
      expected by ASM.
      For example: org/gaul/package/ClassOne methodOne int[] java.util.List
  3. The results of step 2 are dumped into 2 different files - ignore-annotated-classes.txt and
    ignore-annotated-methods.txt. These files are created in the /target/modernizer/test and /target/modernizer/main
    directories of the processing environment. Adding the files to these directories makes them accessible to the plugin while excluding them
    from the jar of the project being built.
  4. The plugin reads the files created in the previous steps and uses them to ignore violations in the places specified.

Testing strategy

ModernizerSuppressionsEndToEndTestClass has helper classes/methods annotated with @SuppressWarnings("modernizer").
The processor scans this class before compiling and creates the ignore files.
End-to-end flow tests are added in ModernizerSuppressionsEndToEndTest which read the ignore files and check if
violations in the code are as expected.

We have benchmarked the plugin after this implementation, and haven't seen any performance issues.

Further improvements

  • The format of ignore-methods file could be improved by making it more structured by using XML, JSON, or YAML.
  • @SuppressWarnings("modernizer") blocks all modernizer errors in that code block. Suppressing specific checks/errors can
    be done by adding something like <id>ViolationIdentifier</id> to the violations file structure, which uniquely
    identifies each violation. We can then use @SuppressWarnings("modernizer:ViolationIdentifier") to suppress errors
    related to those particular checks in the code block.

Copy link
Owner

@gaul gaul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about my tardy response; this looks good! Could you address the minor comments and add usage information to the README? Also please rebase and address conflicts.

makeFile(new File(outputDir,
ModernizerAnnotationUtils.IGNORE_CLASSES_FILE_NAME),
annotatedElements.getAnnotatedClasses());
makeFile(new File(outputDir,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment about what these files do?

for (String element : annotatedElements) {
writer.write(element + "\n");
}
writer.close();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed since we close below?

} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (writer != null) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call Utils.closeQuietly instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utils is in the maven-plugin module, whereas this code is in the annotation-processor module. Do you want me to move it?

for (TypeElement annotation : annotations) {
AnnotatedElements annotatedElements =
getAnnotatedElements(roundEnv, annotation);
if (!(annotatedElements.getAnnotatedClasses().isEmpty() &&
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this means the same thing, it might read better as !isEmpty || !isEmpty.

try {
writer = new BufferedWriter(new FileWriter(file));
for (String element : annotatedElements) {
writer.write(element + "\n");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid StringBuilder via:

writer.write(element);
writer.write("\n");

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-base</artifactId>
<version>r03</version>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this dependency do given the above?

@stevie400
Copy link
Contributor

@gaul I believe I've addressed all your comments about the code and added a question where the best approach was not clear. Let me know what you think.

I apologize for the lengthy delay in responding: Nimisha was a co-op whose term ended and I was on leave for the past couple of months.

@electrum
Copy link
Contributor

electrum commented Nov 7, 2018

This is a decent approach, though I wonder if it wouldn't be simpler to introduce another annotation with CLASS retention similar to @SuppressFBWarnings used by FindBugs / SpotBugs.

Please also note that the correct way to use an annotation processor is the recently added <annotationProcessorPaths> configuration for the maven-compiler-plugin (not as a project dependency).

@gaul
Copy link
Owner

gaul commented Nov 10, 2018

This is a decent approach, though I wonder if it wouldn't be simpler to introduce another annotation with CLASS retention similar to @SuppressFBWarnings used by FindBugs / SpotBugs.

Could we explore this simpler approach? Sorry that I have been tardy with reviewing but don't understand much about annotation processing.

@stevie400
Copy link
Contributor

Can someone please close this PR? I'm opening a new one with @SuppressModernizer

@stevie400
Copy link
Contributor

New PR: #86

@gaul gaul mentioned this pull request Jan 30, 2019
@gaul
Copy link
Owner

gaul commented Jan 30, 2019

Pursuing a different approach in #86.

@gaul gaul closed this Jan 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants