Repository: checkstyle/checkstyle

Problem statement:
Add ignoreAnnotatedBy property to ParameterNumberCheck
Check name: [ParameterNumberCheck](https://checkstyle.org/config_sizes.html#ParameterNumber)

https://checkstyle.org/checks/sizes/parameternumber.html#ParameterNumber

## Problem

https://www.baeldung.com/jackson-annotations#1-jsoncreator
https://fasterxml.github.io/jackson-annotations/javadoc/2.7/com/fasterxml/jackson/annotation/JsonCreator.html
If I implement external API, I have no power over the number of parameters, e.g. when creating a DTO class for REST API and deserializing JSON to an object by constructor annotated by @JsonCreator

```
class MyDto {
  private final String a1;
  private final String a2;
  private final String a3;
  private final String a4;
  private final String a5;
  private final String a6;
  private final String a7;
  private final String a8;
  private final String a9;

  @JsonCreator
  MyDto(String a1, String a2, String a3, String a4, String a5, String a6, String a7, String a8, String a9) {
    this.a1 = a1;
    this.a2 = a2;
    this.a3 = a3;
    this.a4 = a4;
    this.a5 = a5;
    this.a6 = a6;
    this.a7 = a7;
    this.a8 = a8;
    this.a9 = a9;
  }
}
```

## Solution

I'd like to configure check to ignore this constructor. In general, ignore all methods annotated by `@JsonCreator`, e.g.

```
 <module name="ParameterNumber">
   <property name="max" value="5"/>
   <property name="ignoreMethodAnnotatedBy" value="JsonCreator"/>
 </module>
```

The PR with the proposal is here https://github.com/checkstyle/checkstyle/pull/11339

Check already has property `ignoreOverriddenMethods`  so new property will make it more general.


Hints:
Nice!
Supression of constructors with this annotation can be achieved via [SuppresionXpathSingleFilter](https://checkstyle.sourceforge.io/config_filters.html#SuppressionXpathSingleFilter):

```
➜  java cat Test.java 
public class Test {
    private final String a1;
    private final String a2;
    private final String a3;
    private final String a4;
    private final String a5;
    private final String a6;
    private final String a7;
    private final String a8;
    private final String a9;

    @JsonCreator
    Test(String a1, String a2, String a3, String a4, String a5, String a6, String a7, String a8, String a9) {
        this.a1 = a1;
        this.a2 = a2;
        this.a3 = a3;
        this.a4 = a4;
        this.a5 = a5;
        this.a6 = a6;
        this.a7 = a7;
        this.a8 = a8;
        this.a9 = a9;
    }
}

@interface JsonCreator {}

➜  java javac Test.java

➜  java cat config.xml 
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <module name="TreeWalker">
        <module name="ParameterNumber">
            <property name="max" value="5"/>
        </module>
        <module name="SuppressionXpathSingleFilter">
            <property name="checks" value="ParameterNumber"/>
            <property name="query"
                      value="//CTOR_DEF/IDENT[preceding-sibling::MODIFIERS/ANNOTATION/IDENT[@text='JsonCreator']]"/>
        </module>

    </module>
</module>

➜  java java -jar checkstyle-9.3-all.jar -c config.xml Test.java
Starting audit...
Audit done.


```

@tfij we typically try to avoid adding properties to checks when we can handle suppression of special cases with some filter. If you prefer to use a suppression file, you can use the same XPath with https://checkstyle.sourceforge.io/config_filters.html#SuppressionXpathFilter.

XPath can also be extended by the `|` operator to cover method definitions:

```
➜  java cat Test.java
public class Test {
    private final String a1;
    private final String a2;
    private final String a3;
    private final String a4;
    private final String a5;
    private final String a6;
    private final String a7;
    private final String a8;
    private final String a9;

    @JsonCreator
    Test(String a1, String a2, String a3, String a4, String a5, String a6, String a7, String a8, String a9) {
        this.a1 = a1;
        this.a2 = a2;
        this.a3 = a3;
        this.a4 = a4;
        this.a5 = a5;
        this.a6 = a6;
        this.a7 = a7;
        this.a8 = a8;
        this.a9 = a9;
    }

    @JsonCreator
    void someMethod(int a, int b, int c, int d, int e, int f) {}
}

@interface JsonCreator {}

➜  java javac Test.java

➜  java cat config.xml 
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <module name="TreeWalker">
        <module name="ParameterNumber">
            <property name="max" value="5"/>
        </module>
        <module name="SuppressionXpathSingleFilter">
            <property name="checks" value="ParameterNumber"/>
            <property name="query"
                      value="//CTOR_DEF/IDENT[preceding-sibling::MODIFIERS/ANNOTATION/IDENT[@text='JsonCreator']]
                            | //METHOD_DEF/IDENT[preceding-sibling::MODIFIERS/ANNOTATION/IDENT[@text='JsonCreator']]"/>
        </module>

    </module>
</module>

➜  java java -jar checkstyle-9.3-all.jar -c config.xml Test.java
Starting audit...
Audit done.


```
@nmancus1 , @rnveach , @strkkk , @pbludov ,
lets think/vote/re-consider on our decision to avoid extra suppresion/ignore properties. We use to make this decision as we had no time to provide updates to all user requests, so Xpath is definitely workaround for user, but it is not very good for configurations as it imply two modules that are hard to keep together and manage later on.

in the same time, updating Checks to have such properties might also be not that good, as potentially all Checks might need something like this. And it is magic of certain library that not everyone is using. But in the same time instrumenting code with magic annotations is definitely a trend.

As for me, I prefer to keep checks simple, and only add properties if they cannot be covered by some reasonable/ reliable XPath or other filter. So,

> avoid extra suppresion/ignore properties

would be my vote. 

As experience has shown, checks with lots of properties become harder to maintain; not just from a simple code standpoint, but also the logic of how the properties interact with each other. 

Just as a related note - I think that we need to keep a "repository" of sorts (or entire website page) for all of the XPath related modules that we create, to help users more readily find what they need (with examples).
usually we resolved such cases, by example in documentation (xdoc) on how to handle frequent edge cases, so googling usually helps to find solution.
I agree with @romani 
> it imply two modules that are hard to keep together and manage later on

How about making it possible to set suppress property for any check configuration? In this case

```
<module name="ParameterNumber">
    <property name="max" value="5"/>
    <suppress-check query="//CTOR_DEF/IDENT[preceding-sibling::MODIFIERS/ANNOTATION/IDENT[@text='JsonCreator']]
                            | //METHOD_DEF/IDENT[preceding-sibling::MODIFIERS/ANNOTATION/IDENT[@text='JsonCreator']]" />
</module>
```

or something like this.

It will solve a problem with two modules maintained separately.

It is a generic solution, consistent for all checks and can reuse some existing code.
we did not make it like this few years ago, as implementation of Xpath was not mature enough, we still have items to finish to make it for all Checks. May be we can start sooner, somebody need to do deep thinking on this.

Even we will have this option, it is not user friendly approach and can help users for edge cases and workaround some defects. But we still need to think on most common ways to suppress and allow them to be done by Check properties. This border of xpath to Check special property is not defined by us and not clear for us.  
I am against complicating the check. Such property will fix this particular issue, but it won't work for another.
Suppressions are more flexible.
I think I might like the idea of adding suppressions to the check itself, locking the 2 together ( https://github.com/checkstyle/checkstyle/issues/11340#issuecomment-1049579521 ). The only downside to that is there would need to be some type of double checking that this new suppression is only suppressing the violations of this check and not accidentally picking up some other check by user configuration error.

I am probably more extreme in that we have `ignoreOverriddenMethods` which is already looking at annotations. Why don't we just deprecate that property and add a new one that extends it to all annotation and then the user can add `@Override` or any other annotation. (`ignoreOverriddenMethods` is default to `false`, so we would continue that default in the new property). To me this makes more sense as it allows, like this issue wants, a more broader usage.

Otherwise, I don't think I can get behind having the 2 options live together in the same check.
Deprecation is long for us and painful process for users. Better to keep two options and let them work by same code inside.

Xpath is too brutal to user, it tooks awhile for users to understand how to do this, and how to make xpath expression that is required. @tfij, imagine we did not give you xpath expression, how much time you will spend to make it ?

But in the same time Lombok like libraries become very popular way of codding now. So suppression by annotation might be well used approach.
Agree, xpath is very mysterious. Mainly because I don't know what XML structure I'm scanning.
I imagine that if user hadn't found a ready or similar solution in the documentation or on stackoverflow, it would take hours to work out this by themself.
After reading through the discussion here, and thinking on this some more, I have reconsidered my general position on check properties. If a new check property that is a “feature” is proposed, I stay with my original position. But - if new property is some simple suppression (i.e. checking for a particular annotation) , we should consider it. Most of the time, this will just require some Boolean method used to guard the `log` method.

We do not want to make Checkstyle difficult to configure or inflexible, this will only frustrate users. 
I more care about users experience, implementation pain is limited to few people during PR and eventually forgotten. We use/reconfigure checkstyle more than we spend time to code it.

Xpath suppression was good approach to resolve problem on user side quickly, temporary hack. And it might continue to serve this purpose.

But custom ignore properties are a way more easy to use.
Ignore by annotation will be most used ignore/suppression approach. As annotation imply some third-party features/magic that happening in runtime or compilation time,
Checkstyle  is not working at that time, so we have to skip validation in some cases.

We did not have time to implement ignore properties in Checks, that is why we invested more in xpath to let users has workaround sooner, but if implementation is provided by community, I think it is good to accept it. Especially if we see that such ignore approach can be widely reused.

Just example of property to ignore: https://github.com/checkstyle/checkstyle/commit/0b3a2606e535eb14d2d9dcd6c79d00db17c22f91 that is named "exclude" but meaning is the same as ignore/suppress.
Let's make a decision here, in regards to this particular issue. If we want to continue discussion about adding check properties in general, let's create a Github discussion and continue there.

After thinking on @romani 's comments above, I am for approval of this issue.
One more example of Lombok usage that cause unwanted violation https://github.com/checkstyle/checkstyle/issues/11415
Hi, is there anything I can do to help make the decision?
I hope it will be the approval of this proposal :)
@tfij, can I ask you to help :
We need to review all Checks that has same property and list all of them, and we need to choose a good name from existing or just new.

For now I think we should name it like: ignoreAnnothedBy or something similar. We should avoid usage of "method" or similar words to let it be used for any Check target.

Can we also analyze what Checks are highly likely to have such property in future? And what Check unlikely to change behavior by annotated target.
one more example how annotation make false positive for static analisys - https://github.com/checkstyle/checkstyle/pull/11481
pmd using property `ignoredAnnotations` 
@romani I can handle it next week.

One question, by 

> review all Checks that has same property and list all of them

Do you mean some kind of grid of all checks? or some subset of checks? What format do you expect?
All Checks, any readable format, to see what we have already 
@romani 
This is a list of all checks with all not static fields (type and name).
https://gist.github.com/tfij/58f1b0e1e5e6e15c0a3730f46872762e
Hope it will be useful. Reach out if you need any other data.
@tfij, thanks a lot.
can ask you grep properties that are about ignorance/skip/exclude by annotations.
For now, there is a small number of parameters related with annotations:
```
MissingJavadocMethodCheck | allowedAnnotations
JavadocTypeCheck          | allowedAnnotations
VisibilityModifierCheck   | ignoreAnnotationCanonicalNames, ignoreAnnotationShortNames
DesignForExtensionCheck   | ignoredAnnotations
MagicNumberCheck          | ignoreAnnotationElementDefaults, ignoreAnnotation
JavadocMethodCheck        | allowedAnnotations
```

By the way, I have another case for excluding the check by annotation:
`HideUtilityClassConstructorCheck` should not report a violation if a class is annotated with lombok [@UtilityClass](https://projectlombok.org/features/experimental/UtilityClass) annotation.
Sorry that issue lost from my attention for more than year.

I am ok to approve issue with new property `ignoreAnnotatedBy`.

@nrmancuso , pre-approved at https://github.com/checkstyle/checkstyle/issues/11340#issuecomment-1062967967

@rnveach , please share your opinion and if you agree , please put approved label.
My only question on adding this type of properties is why go through this trouble when xpath suppression can do the same thing.

I also question if the property will use the FQCP for the annotation, or if it will just be the class name.

Otherwise, I am good.
>  if the property will use the FQCP for the annotation,

yes

>  why go through this trouble when xpath suppression can do the same thing.

xpath is very complicated concept, it is good for one time workaround, but for cases where lombok is popular (people are lazy to type and lombok is  popular to generate code for people, all IDEs support it, so it become a part of our life). and Lombok is not the only library that does this, there are many. Consolidation of whole config in one `<module>`  is easy for user concept.

I am not ready to discuss embebded xpath suppression property in each module for now, but looks like we slowly going to this decision.
```
 <module name="ParameterNumber">
   <property name="max" value="5"/>
   <property name="ignoreAnnotatedBy" value="JsonCreator"/>
 </module>
```

sounds like ignore parameter that is annotated by, not a method. But check is actually targeting method, so it is not confusion.

~Sounds like we have to come back to originnal:~
```
 <module name="ParameterNumber">
   <property name="max" value="5"/>
   <property name="ignoreMethodAnnotatedBy" value="JsonCreator"/>
 </module>
```
to be very explicit where annotation is expected. Ideally user should not read documentation to understand how to configure Check.

@rnveach , please approve.
I'm OK with `ignoreMethodAnnotatedBy`.
However, you have to make a decision :)
@rnveach , @nrmancuso , please review and approve.

@tfij, if no response will be there before 1 january 2023, please mention me here, I will approve issue.
I would prefer this new property to be called `ignoreAnnotatedBy` for two reasons:

1. ParameterNumber also checks constructors (which is sort of a special method)
2. As we extend this concept to other checks, property name can be consistent and users don’t have to be concerned with details

We could also devise some alternative general naming such as `ignoreTargetAnnotatedBy` or something similar.
Ok, let's move forward with `ignoreAnnotatedBy`.
Everyone is welcome to finish this PR https://github.com/checkstyle/checkstyle/pull/11339, just reuse code and move this PR across finish line.

@sktpy, do you have time to help us with this issue ?
Yes, I am on it.

Patch:
diff --git a/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionParameterNumberTest.java b/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionParameterNumberTest.java
--- a/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionParameterNumberTest.java
+++ b/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionParameterNumberTest.java
@@ -103,4 +103,30 @@ public void testIgnoreOverriddenMethods() throws Exception {
         runVerifications(moduleConfig, fileToProcess, expectedViolations, expectedXpathQueries);
     }
 
+    @Test
+    public void testIgnoreAnnotatedBy() throws Exception {
+        final String filePath =
+                getPath("SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy.java");
+        final File fileToProcess = new File(filePath);
+
+        final DefaultConfiguration moduleConfig = createModuleConfig(ParameterNumberCheck.class);
+        moduleConfig.addProperty("ignoreAnnotatedBy", "MyAnno");
+        moduleConfig.addProperty("max", "2");
+
+        final String[] expectedViolations = {
+            "15:34: " + getCheckMessage(ParameterNumberCheck.class, MSG_KEY, 2, 3),
+        };
+
+        final List<String> expectedXpathQueries = Collections.singletonList(
+                "/COMPILATION_UNIT/CLASS_DEF"
+                + "[./IDENT[@text='SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy']]"
+                + "/OBJBLOCK/CLASS_DEF[./IDENT[@text='InnerClass']]"
+                + "/OBJBLOCK/STATIC_INIT/SLIST/EXPR/LITERAL_NEW[./IDENT[@text='Object']]"
+                + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='method']]"
+                + "/SLIST/LITERAL_IF/SLIST/EXPR/LITERAL_NEW[./IDENT[@text='Object']]"
+                + "/OBJBLOCK/METHOD_DEF/IDENT[@text='checkedMethod']"
+        );
+
+        runVerifications(moduleConfig, fileToProcess, expectedViolations, expectedXpathQueries);
+    }
 }
diff --git a/src/it/resources/org/checkstyle/suppressionxpathfilter/parameternumber/SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy.java b/src/it/resources/org/checkstyle/suppressionxpathfilter/parameternumber/SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy.java
new file mode 100644
--- /dev/null
+++ b/src/it/resources/org/checkstyle/suppressionxpathfilter/parameternumber/SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy.java
@@ -0,0 +1,26 @@
+package org.checkstyle.suppressionxpathfilter.parameternumber;
+
+public class SuppressionXpathRegressionParameterNumberIgnoreAnnotatedBy {
+    static class InnerClass {
+        static {
+            new Object() {
+                void method() {
+                    if (true) {
+                        new Object() {
+                            @MyAnno
+                            void ignoredMethod(int a, @MyAnno int b, int c) {
+
+                            }
+
+                            void checkedMethod(int a, @MyAnno int b, int c) { // warn
+
+                            }
+                        };
+                    }
+                }
+            };
+        }
+    }
+
+    @interface MyAnno {}
+}
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheck.java
@@ -19,6 +19,9 @@
 
 package com.puppycrawl.tools.checkstyle.checks.sizes;
 
+import java.util.Collections;
+import java.util.Set;
+
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
@@ -32,6 +35,12 @@
  * </p>
  * <ul>
  * <li>
+ * Property {@code ignoreAnnotatedBy} - Ignore methods and constructors
+ * annotated with the specified annotation(s).
+ * Type is {@code java.lang.String[]}.
+ * Default value is {@code ""}.
+ * </li>
+ * <li>
  * Property {@code ignoreOverriddenMethods} - Ignore number of parameters for
  * methods with {@code @Override} annotation.
  * Type is {@code boolean}.
@@ -86,6 +95,11 @@ public class ParameterNumberCheck
     /** Ignore number of parameters for methods with {@code @Override} annotation. */
     private boolean ignoreOverriddenMethods;
 
+    /**
+     * Ignore methods and constructors annotated with the specified annotation(s).
+     */
+    private Set<String> ignoreAnnotatedBy = Collections.emptySet();
+
     /**
      * Setter to specify the maximum number of parameters allowed.
      *
@@ -106,6 +120,16 @@ public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
         this.ignoreOverriddenMethods = ignoreOverriddenMethods;
     }
 
+    /**
+     * Setter to ignore methods and constructors annotated with the specified annotation(s).
+     *
+     * @param annotationNames specified annotation(s)
+     * @since 10.15.0
+     */
+    public void setIgnoreAnnotatedBy(String... annotationNames) {
+        ignoreAnnotatedBy = Set.of(annotationNames);
+    }
+
     @Override
     public int[] getDefaultTokens() {
         return getAcceptableTokens();
@@ -132,16 +156,33 @@ public void visitToken(DetailAST ast) {
     }
 
     /**
-     * Determine whether to ignore number of parameters for the method.
+     * Determine whether to ignore number of parameters.
      *
      * @param ast the token to process
-     * @return true if this is overridden method and number of parameters should be ignored
-     *         false otherwise
+     * @return true if number of parameters should be ignored.
      */
     private boolean shouldIgnoreNumberOfParameters(DetailAST ast) {
-        // if you override a method, you have no power over the number of parameters
-        return ignoreOverriddenMethods
-                && AnnotationUtil.hasOverrideAnnotation(ast);
+        return isIgnoredOverriddenMethod(ast) || isAnnotatedByIgnoredAnnotations(ast);
+    }
+
+    /**
+     * Checks if method is overridden and should be ignored.
+     *
+     * @param ast method definition to check
+     * @return true if method is overridden and should be ignored.
+     */
+    private boolean isIgnoredOverriddenMethod(DetailAST ast) {
+        return ignoreOverriddenMethods && AnnotationUtil.hasOverrideAnnotation(ast);
+    }
+
+    /**
+     * Checks if method or constructor is annotated by ignored annotation(s).
+     *
+     * @param ast method or constructor definition to check
+     * @return true if annotated by ignored annotation(s).
+     */
+    private boolean isAnnotatedByIgnoredAnnotations(DetailAST ast) {
+        return AnnotationUtil.containsAnnotation(ast, ignoreAnnotatedBy);
     }
 
 }


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ParameterNumberCheckTest.java
@@ -123,4 +123,35 @@ public void testIgnoreOverriddenMethodsFalse()
                 getPath("InputParameterNumber2.java"), expected);
     }
 
+    @Test
+    public void testIgnoreAnnotatedBy() throws Exception {
+        final String[] expected = {
+            "23:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "30:10: " + getCheckMessage(MSG_KEY, 2, 4),
+            "35:14: " + getCheckMessage(MSG_KEY, 2, 3),
+            "43:9: " + getCheckMessage(MSG_KEY, 2, 4),
+            "58:30: " + getCheckMessage(MSG_KEY, 2, 3),
+            "62:29: " + getCheckMessage(MSG_KEY, 2, 3),
+            "77:34: " + getCheckMessage(MSG_KEY, 2, 4),
+            "97:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "106:14: " + getCheckMessage(MSG_KEY, 2, 3),
+        };
+        verifyWithInlineConfigParser(
+                getPath("InputParameterNumberIgnoreAnnotatedBy.java"), expected);
+    }
+
+    @Test
+    public void testIgnoreAnnotatedByFullyQualifiedClassName() throws Exception {
+        final String[] expected = {
+            "15:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "17:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "23:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "27:10: " + getCheckMessage(MSG_KEY, 2, 3),
+            "41:14: " + getCheckMessage(MSG_KEY, 2, 3),
+            "45:14: " + getCheckMessage(MSG_KEY, 2, 3),
+        };
+        verifyWithInlineConfigParser(
+                getPath("InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.java"),
+                expected);
+    }
 }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedBy.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedBy.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedBy.java
@@ -0,0 +1,115 @@
+/*
+ParameterNumber
+max = 2
+ignoreAnnotatedBy = MyAnno, Session
+
+*/
+
+package com.puppycrawl.tools.checkstyle.checks.sizes.parameternumber;
+
+public class InputParameterNumberIgnoreAnnotatedBy {
+    @MyAnno
+    InputParameterNumberIgnoreAnnotatedBy(int a, int b, int c) {}
+
+    void method1(int a, int b) {}
+
+    @MyAnno
+    void method2(int a, int b, int c) {}
+
+    @Session(uid = "Y2K")
+    void method3(int a, int b, int c, int d) {}
+
+    @Wawa
+    void method5(int a, int b, int c) { // violation, 'More than 2 parameters (found 3).'
+    }
+
+    @Wawa
+    void method6(int a, int b) {}
+
+
+    void method7(int a, int b, int c, int d) { // violation, 'More than 2 parameters (found 4).'
+    }
+
+    class InnerClass extends InputParameterNumberIgnoreAnnotatedBy {
+        @Override
+        void method2(int a, int b, int c) { // violation, 'More than 2 parameters (found 3).'
+            int d = a + c;
+        }
+
+        InnerClass(int a, int b) {
+            super(1, 2, 3);
+        }
+
+        InnerClass(int a, int b, int c, int d) { // violation, 'More than 2 parameters (found 4).'
+            super(1, 2, 3);
+        }
+    }
+
+    static class Very {
+        static class Deep {
+            static class Rabbit {
+                enum Colors {
+                    GOLDEN(1, 2, 3) {
+                        void jump(int a, int b) {}
+                        @MyAnno
+                        void roll(int a, int b, int c) {}
+
+                        // violation below, 'More than 2 parameters (found 3).'
+                        void loaf(int a, int b, int c) {}
+                    };
+
+                    @Wawa
+                    private Colors(@MyAnno int a, int b, int c) {}
+                    // violation above, 'More than 2 parameters (found 3).'
+
+                    @Session(uid = ":D")
+                    private Colors(@Wawa int a, @Wawa int b, @Wawa int c, @Wawa int d) {}
+
+                    private Colors(int a) {}
+                }
+                static class Hole {
+                    static {
+                        new Object() {
+                            @MyAnno @Session(uid = "G0F")
+                            void method1(int a, int b, int c) {}
+
+                            // violation below, 'More than 2 parameters (found 4).'
+                            void method3(@MyAnno int a, @MyAnno int b, @MyAnno int c, int d) {}
+                        };
+                    }
+                }
+            }
+        }
+    }
+
+    @Wawa
+    @MyAnno
+    void method8(int a, int b, int c) {
+    }
+
+    @Session(uid = "H1") @Bit
+    @Wawa
+    void method9(int a, int b, int c) {
+    }
+
+    @Bit
+    @Wawa
+    void method10(int a, int b, int c) { // violation, 'More than 2 parameters (found 3).'
+    }
+
+    interface Reader {
+        void method1 (int a, int b);
+
+        @MyAnno
+        void method2 (int a, int b, int c);
+
+        void method3 (int a, int b, int c); // violation, 'More than 2 parameters (found 3).'
+    }
+
+    @interface Wawa {}
+    @interface Bit {}
+    @interface MyAnno {}
+    @interface Session {
+        String uid();
+    }
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/parameternumber/InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.java
@@ -0,0 +1,62 @@
+/*
+ParameterNumber
+max = 2
+ignoreAnnotatedBy = java.lang.Deprecated, InnerClass.InnerAnno, Session
+
+*/
+
+package com.puppycrawl.tools.checkstyle.checks.sizes.parameternumber;
+
+public class InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName {
+    @java.lang.Deprecated
+    void method3(int a, int b, int c) {}
+
+    @Deprecated
+    void method4(int a, int b, int c) {} // violation, 'More than 2 parameters (found 3).'
+
+    void method5(int a, int b, int c) {} // violation, 'More than 2 parameters (found 3).'
+
+    @Session
+    void method6(int a, int b, int c) {}
+
+    @InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.Session
+    void method7(int a, int b, int c) {} // violation, 'More than 2 parameters (found 3).'
+
+    @com.puppycrawl.tools.checkstyle.checks.sizes.parameternumber
+            .InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.Session
+    void method8(int a, int b, int c) {} // violation, 'More than 2 parameters (found 3).'
+
+    @com.puppycrawl.tools.checkstyle.checks.sizes.parameternumber
+            .InputParameterNumberIgnoreAnnotatedByFullyQualifiedClassName.Session
+    void method8(int a, int b) {}
+
+    private static class InnerClass {
+        private @interface InnerAnno {}
+
+        @InnerClass.InnerAnno
+        void method1(int a, int b, int c) {
+        }
+
+        @InnerAnno
+        void method2(int a, int b, int c) { // violation, 'More than 2 parameters (found 3).'
+        }
+
+        @Bit
+        void method3(int a, int b, int c) { // violation, 'More than 2 parameters (found 3).'
+        }
+
+        @InnerAnno
+        void method2(int a, int b) {
+        }
+
+        @Bit
+        void method3(int a, int b) {
+        }
+
+        void method4(int a, int b) {
+        }
+    }
+
+    @interface Session {}
+    @interface Bit{}
+}