View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.BitSet;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.regex.Pattern;
27  
28  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
29  import com.puppycrawl.tools.checkstyle.PropertyType;
30  import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
31  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
32  import com.puppycrawl.tools.checkstyle.api.DetailAST;
33  import com.puppycrawl.tools.checkstyle.api.FullIdent;
34  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
36  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
37  
38  /**
39   * <p>
40   * Checks that particular classes or interfaces are never used.
41   * </p>
42   * <p>
43   * Rationale: Helps reduce coupling on concrete classes.
44   * </p>
45   * <p>
46   * For additional restriction of type usage see also:
47   * <a href="https://checkstyle.org/checks/coding/illegalinstantiation.html#IllegalInstantiation">
48   * IllegalInstantiation</a>,
49   * <a href="https://checkstyle.org/checks/imports/illegalimport.html#IllegalImport">
50   * IllegalImport</a>
51   * </p>
52   * <p>
53   * It is possible to set illegal class names via short or
54   * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7">canonical</a>
55   * name. Specifying illegal type invokes analyzing imports and Check puts violations at
56   * corresponding declarations (of variables, methods or parameters).
57   * This helps to avoid ambiguous cases, e.g.: {@code java.awt.List} was set as
58   * illegal class name, then, code like:
59   * </p>
60   * <pre>
61   * import java.util.List;
62   * ...
63   * List list; //No violation here
64   * </pre>
65   * <p>
66   * will be ok.
67   * </p>
68   * <p>
69   * In most cases it's justified to put following classes to <b>illegalClassNames</b>:
70   * </p>
71   * <ul>
72   * <li>GregorianCalendar</li>
73   * <li>Hashtable</li>
74   * <li>ArrayList</li>
75   * <li>LinkedList</li>
76   * <li>Vector</li>
77   * </ul>
78   * <p>
79   * as methods that are differ from interface methods are rarely used, so in most cases user will
80   * benefit from checking for them.
81   * </p>
82   * <ul>
83   * <li>
84   * Property {@code ignoredMethodNames} - Specify methods that should not be checked.
85   * Type is {@code java.lang.String[]}.
86   * Default value is {@code getEnvironment, getInitialContext}.
87   * </li>
88   * <li>
89   * Property {@code illegalAbstractClassNameFormat} - Specify RegExp for illegal abstract class
90   * names.
91   * Type is {@code java.util.regex.Pattern}.
92   * Default value is {@code "^(.*[.])?Abstract.*$"}.
93   * </li>
94   * <li>
95   * Property {@code illegalClassNames} - Specify classes that should not be used
96   * as types in variable declarations, return values or parameters.
97   * Type is {@code java.lang.String[]}.
98   * Default value is {@code HashMap, HashSet, LinkedHashMap, LinkedHashSet, TreeMap,
99   * TreeSet, java.util.HashMap, java.util.HashSet, java.util.LinkedHashMap,
100  * java.util.LinkedHashSet, java.util.TreeMap, java.util.TreeSet}.
101  * </li>
102  * <li>
103  * Property {@code legalAbstractClassNames} - Define abstract classes that may be used as types.
104  * Type is {@code java.lang.String[]}.
105  * Default value is {@code ""}.
106  * </li>
107  * <li>
108  * Property {@code memberModifiers} - Control whether to check only methods and fields with any
109  * of the specified modifiers.
110  * This property does not affect method calls nor method references nor record components.
111  * Type is {@code java.lang.String[]}.
112  * Validation type is {@code tokenTypesSet}.
113  * Default value is {@code ""}.
114  * </li>
115  * <li>
116  * Property {@code validateAbstractClassNames} - Control whether to validate abstract class names.
117  * Type is {@code boolean}.
118  * Default value is {@code false}.
119  * </li>
120  * <li>
121  * Property {@code tokens} - tokens to check
122  * Type is {@code java.lang.String[]}.
123  * Validation type is {@code tokenSet}.
124  * Default value is:
125  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
126  * ANNOTATION_FIELD_DEF</a>,
127  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
128  * CLASS_DEF</a>,
129  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
130  * INTERFACE_DEF</a>,
131  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_CALL">
132  * METHOD_CALL</a>,
133  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
134  * METHOD_DEF</a>,
135  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_REF">
136  * METHOD_REF</a>,
137  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF">
138  * PARAMETER_DEF</a>,
139  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
140  * VARIABLE_DEF</a>,
141  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF">
142  * PATTERN_VARIABLE_DEF</a>,
143  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
144  * RECORD_DEF</a>,
145  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_COMPONENT_DEF">
146  * RECORD_COMPONENT_DEF</a>.
147  * </li>
148  * </ul>
149  * <p>
150  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
151  * </p>
152  * <p>
153  * Violation Message Keys:
154  * </p>
155  * <ul>
156  * <li>
157  * {@code illegal.type}
158  * </li>
159  * </ul>
160  *
161  * @since 3.2
162  *
163  */
164 @FileStatefulCheck
165 public final class IllegalTypeCheck extends AbstractCheck {
166 
167     /**
168      * A key is pointing to the warning message text in "messages.properties"
169      * file.
170      */
171     public static final String MSG_KEY = "illegal.type";
172 
173     /** Types illegal by default. */
174     private static final String[] DEFAULT_ILLEGAL_TYPES = {
175         "HashSet",
176         "HashMap",
177         "LinkedHashMap",
178         "LinkedHashSet",
179         "TreeSet",
180         "TreeMap",
181         "java.util.HashSet",
182         "java.util.HashMap",
183         "java.util.LinkedHashMap",
184         "java.util.LinkedHashSet",
185         "java.util.TreeSet",
186         "java.util.TreeMap",
187     };
188 
189     /** Default ignored method names. */
190     private static final String[] DEFAULT_IGNORED_METHOD_NAMES = {
191         "getInitialContext",
192         "getEnvironment",
193     };
194 
195     /**
196      * Specify classes that should not be used as types in variable declarations,
197      * return values or parameters.
198      */
199     private final Set<String> illegalClassNames = new HashSet<>();
200     /** Illegal short classes. */
201     private final Set<String> illegalShortClassNames = new HashSet<>();
202     /** Define abstract classes that may be used as types. */
203     private final Set<String> legalAbstractClassNames = new HashSet<>();
204     /** Specify methods that should not be checked. */
205     private final Set<String> ignoredMethodNames = new HashSet<>();
206     /**
207      * Control whether to check only methods and fields with any of the specified modifiers.
208      * This property does not affect method calls nor method references nor record components.
209      */
210     @XdocsPropertyType(PropertyType.TOKEN_ARRAY)
211     private BitSet memberModifiers = new BitSet();
212 
213     /** Specify RegExp for illegal abstract class names. */
214     private Pattern illegalAbstractClassNameFormat = Pattern.compile("^(.*[.])?Abstract.*$");
215 
216     /**
217      * Control whether to validate abstract class names.
218      */
219     private boolean validateAbstractClassNames;
220 
221     /** Creates new instance of the check. */
222     public IllegalTypeCheck() {
223         setIllegalClassNames(DEFAULT_ILLEGAL_TYPES);
224         setIgnoredMethodNames(DEFAULT_IGNORED_METHOD_NAMES);
225     }
226 
227     /**
228      * Setter to specify RegExp for illegal abstract class names.
229      *
230      * @param pattern a pattern.
231      * @since 3.2
232      */
233     public void setIllegalAbstractClassNameFormat(Pattern pattern) {
234         illegalAbstractClassNameFormat = pattern;
235     }
236 
237     /**
238      * Setter to control whether to validate abstract class names.
239      *
240      * @param validateAbstractClassNames whether abstract class names must be ignored.
241      * @since 6.10
242      */
243     public void setValidateAbstractClassNames(boolean validateAbstractClassNames) {
244         this.validateAbstractClassNames = validateAbstractClassNames;
245     }
246 
247     @Override
248     public int[] getDefaultTokens() {
249         return getAcceptableTokens();
250     }
251 
252     @Override
253     public int[] getAcceptableTokens() {
254         return new int[] {
255             TokenTypes.ANNOTATION_FIELD_DEF,
256             TokenTypes.CLASS_DEF,
257             TokenTypes.IMPORT,
258             TokenTypes.INTERFACE_DEF,
259             TokenTypes.METHOD_CALL,
260             TokenTypes.METHOD_DEF,
261             TokenTypes.METHOD_REF,
262             TokenTypes.PARAMETER_DEF,
263             TokenTypes.VARIABLE_DEF,
264             TokenTypes.PATTERN_VARIABLE_DEF,
265             TokenTypes.RECORD_DEF,
266             TokenTypes.RECORD_COMPONENT_DEF,
267         };
268     }
269 
270     @Override
271     public void beginTree(DetailAST rootAST) {
272         illegalShortClassNames.clear();
273         illegalShortClassNames.addAll(illegalClassNames);
274     }
275 
276     @Override
277     public int[] getRequiredTokens() {
278         return new int[] {TokenTypes.IMPORT};
279     }
280 
281     @Override
282     public void visitToken(DetailAST ast) {
283         switch (ast.getType()) {
284             case TokenTypes.CLASS_DEF:
285             case TokenTypes.INTERFACE_DEF:
286             case TokenTypes.RECORD_DEF:
287                 visitTypeDef(ast);
288                 break;
289             case TokenTypes.METHOD_CALL:
290             case TokenTypes.METHOD_REF:
291                 visitMethodCallOrRef(ast);
292                 break;
293             case TokenTypes.METHOD_DEF:
294                 visitMethodDef(ast);
295                 break;
296             case TokenTypes.VARIABLE_DEF:
297             case TokenTypes.ANNOTATION_FIELD_DEF:
298             case TokenTypes.PATTERN_VARIABLE_DEF:
299                 visitVariableDef(ast);
300                 break;
301             case TokenTypes.RECORD_COMPONENT_DEF:
302                 checkClassName(ast);
303                 break;
304             case TokenTypes.PARAMETER_DEF:
305                 visitParameterDef(ast);
306                 break;
307             case TokenTypes.IMPORT:
308                 visitImport(ast);
309                 break;
310             default:
311                 throw new IllegalStateException(ast.toString());
312         }
313     }
314 
315     /**
316      * Checks if current method's return type or variable's type is verifiable
317      * according to <b>memberModifiers</b> option.
318      *
319      * @param methodOrVariableDef METHOD_DEF or VARIABLE_DEF ast node.
320      * @return true if member is verifiable according to <b>memberModifiers</b> option.
321      */
322     private boolean isVerifiable(DetailAST methodOrVariableDef) {
323         boolean result = true;
324         if (!memberModifiers.isEmpty()) {
325             final DetailAST modifiersAst = methodOrVariableDef
326                     .findFirstToken(TokenTypes.MODIFIERS);
327             result = isContainVerifiableType(modifiersAst);
328         }
329         return result;
330     }
331 
332     /**
333      * Checks is modifiers contain verifiable type.
334      *
335      * @param modifiers
336      *            parent node for all modifiers
337      * @return true if method or variable can be verified
338      */
339     private boolean isContainVerifiableType(DetailAST modifiers) {
340         boolean result = false;
341         for (DetailAST modifier = modifiers.getFirstChild(); modifier != null;
342                  modifier = modifier.getNextSibling()) {
343             if (memberModifiers.get(modifier.getType())) {
344                 result = true;
345                 break;
346             }
347         }
348         return result;
349     }
350 
351     /**
352      * Checks the super type and implemented interfaces of a given type.
353      *
354      * @param typeDef class or interface for check.
355      */
356     private void visitTypeDef(DetailAST typeDef) {
357         if (isVerifiable(typeDef)) {
358             checkTypeParameters(typeDef);
359             final DetailAST extendsClause = typeDef.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
360             if (extendsClause != null) {
361                 checkBaseTypes(extendsClause);
362             }
363             final DetailAST implementsClause = typeDef.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
364             if (implementsClause != null) {
365                 checkBaseTypes(implementsClause);
366             }
367         }
368     }
369 
370     /**
371      * Checks return type of a given method.
372      *
373      * @param methodDef method for check.
374      */
375     private void visitMethodDef(DetailAST methodDef) {
376         if (isCheckedMethod(methodDef)) {
377             checkClassName(methodDef);
378         }
379     }
380 
381     /**
382      * Checks type of parameters.
383      *
384      * @param parameterDef parameter list for check.
385      */
386     private void visitParameterDef(DetailAST parameterDef) {
387         final DetailAST grandParentAST = parameterDef.getParent().getParent();
388 
389         if (grandParentAST.getType() == TokenTypes.METHOD_DEF && isCheckedMethod(grandParentAST)) {
390             checkClassName(parameterDef);
391         }
392     }
393 
394     /**
395      * Checks type of given variable.
396      *
397      * @param variableDef variable to check.
398      */
399     private void visitVariableDef(DetailAST variableDef) {
400         if (isVerifiable(variableDef)) {
401             checkClassName(variableDef);
402         }
403     }
404 
405     /**
406      * Checks the type arguments of given method call/reference.
407      *
408      * @param methodCallOrRef method call/reference to check.
409      */
410     private void visitMethodCallOrRef(DetailAST methodCallOrRef) {
411         checkTypeArguments(methodCallOrRef);
412     }
413 
414     /**
415      * Checks imported type (as static and star imports are not supported by Check,
416      *  only type is in the consideration).<br>
417      * If this type is illegal due to Check's options - puts violation on it.
418      *
419      * @param importAst {@link TokenTypes#IMPORT Import}
420      */
421     private void visitImport(DetailAST importAst) {
422         if (!isStarImport(importAst)) {
423             final String canonicalName = getImportedTypeCanonicalName(importAst);
424             extendIllegalClassNamesWithShortName(canonicalName);
425         }
426     }
427 
428     /**
429      * Checks if current import is star import. E.g.:
430      * <p>
431      * {@code
432      * import java.util.*;
433      * }
434      * </p>
435      *
436      * @param importAst {@link TokenTypes#IMPORT Import}
437      * @return true if it is star import
438      */
439     private static boolean isStarImport(DetailAST importAst) {
440         boolean result = false;
441         DetailAST toVisit = importAst;
442         while (toVisit != null) {
443             toVisit = getNextSubTreeNode(toVisit, importAst);
444             if (toVisit != null && toVisit.getType() == TokenTypes.STAR) {
445                 result = true;
446                 break;
447             }
448         }
449         return result;
450     }
451 
452     /**
453      * Checks type and type arguments/parameters of given method, parameter, variable or
454      * method call/reference.
455      *
456      * @param ast node to check.
457      */
458     private void checkClassName(DetailAST ast) {
459         final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
460         checkType(type);
461         checkTypeParameters(ast);
462     }
463 
464     /**
465      * Checks the identifier of the given type.
466      *
467      * @param type node to check.
468      */
469     private void checkIdent(DetailAST type) {
470         final FullIdent ident = FullIdent.createFullIdent(type);
471         if (isMatchingClassName(ident.getText())) {
472             log(ident.getDetailAst(), MSG_KEY, ident.getText());
473         }
474     }
475 
476     /**
477      * Checks the {@code extends} or {@code implements} statement.
478      *
479      * @param clause DetailAST for either {@link TokenTypes#EXTENDS_CLAUSE} or
480      *               {@link TokenTypes#IMPLEMENTS_CLAUSE}
481      */
482     private void checkBaseTypes(DetailAST clause) {
483         DetailAST child = clause.getFirstChild();
484         while (child != null) {
485             if (child.getType() == TokenTypes.IDENT) {
486                 checkIdent(child);
487             }
488             else {
489                 TokenUtil.forEachChild(child, TokenTypes.TYPE_ARGUMENT, this::checkType);
490             }
491             child = child.getNextSibling();
492         }
493     }
494 
495     /**
496      * Checks the given type, its arguments and parameters.
497      *
498      * @param type node to check.
499      */
500     private void checkType(DetailAST type) {
501         checkIdent(type.getFirstChild());
502         checkTypeArguments(type);
503         checkTypeBounds(type);
504     }
505 
506     /**
507      * Checks the upper and lower bounds for the given type.
508      *
509      * @param type node to check.
510      */
511     private void checkTypeBounds(DetailAST type) {
512         final DetailAST upperBounds = type.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS);
513         if (upperBounds != null) {
514             checkType(upperBounds);
515         }
516         final DetailAST lowerBounds = type.findFirstToken(TokenTypes.TYPE_LOWER_BOUNDS);
517         if (lowerBounds != null) {
518             checkType(lowerBounds);
519         }
520     }
521 
522     /**
523      * Checks the type parameters of the node.
524      *
525      * @param node node to check.
526      */
527     private void checkTypeParameters(final DetailAST node) {
528         final DetailAST typeParameters = node.findFirstToken(TokenTypes.TYPE_PARAMETERS);
529         if (typeParameters != null) {
530             TokenUtil.forEachChild(typeParameters, TokenTypes.TYPE_PARAMETER, this::checkType);
531         }
532     }
533 
534     /**
535      * Checks the type arguments of the node.
536      *
537      * @param node node to check.
538      */
539     private void checkTypeArguments(final DetailAST node) {
540         DetailAST typeArguments = node.findFirstToken(TokenTypes.TYPE_ARGUMENTS);
541         if (typeArguments == null) {
542             typeArguments = node.getFirstChild().findFirstToken(TokenTypes.TYPE_ARGUMENTS);
543         }
544 
545         if (typeArguments != null) {
546             TokenUtil.forEachChild(typeArguments, TokenTypes.TYPE_ARGUMENT, this::checkType);
547         }
548     }
549 
550     /**
551      * Returns true if given class name is one of illegal classes or else false.
552      *
553      * @param className class name to check.
554      * @return true if given class name is one of illegal classes
555      *         or if it matches to abstract class names pattern.
556      */
557     private boolean isMatchingClassName(String className) {
558         final String shortName = className.substring(className.lastIndexOf('.') + 1);
559         return illegalClassNames.contains(className)
560                 || illegalShortClassNames.contains(shortName)
561                 || validateAbstractClassNames
562                     && !legalAbstractClassNames.contains(className)
563                     && illegalAbstractClassNameFormat.matcher(className).find();
564     }
565 
566     /**
567      * Extends illegal class names set via imported short type name.
568      *
569      * @param canonicalName
570      *     <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7">
571      *     Canonical</a> name of imported type.
572      */
573     private void extendIllegalClassNamesWithShortName(String canonicalName) {
574         if (illegalClassNames.contains(canonicalName)) {
575             final String shortName = canonicalName
576                 .substring(canonicalName.lastIndexOf('.') + 1);
577             illegalShortClassNames.add(shortName);
578         }
579     }
580 
581     /**
582      * Gets imported type's
583      * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.7">
584      *  canonical name</a>.
585      *
586      * @param importAst {@link TokenTypes#IMPORT Import}
587      * @return Imported canonical type's name.
588      */
589     private static String getImportedTypeCanonicalName(DetailAST importAst) {
590         final StringBuilder canonicalNameBuilder = new StringBuilder(256);
591         DetailAST toVisit = importAst;
592         while (toVisit != null) {
593             toVisit = getNextSubTreeNode(toVisit, importAst);
594             if (toVisit != null && toVisit.getType() == TokenTypes.IDENT) {
595                 if (canonicalNameBuilder.length() > 0) {
596                     canonicalNameBuilder.append('.');
597                 }
598                 canonicalNameBuilder.append(toVisit.getText());
599             }
600         }
601         return canonicalNameBuilder.toString();
602     }
603 
604     /**
605      * Gets the next node of a syntactical tree (child of a current node or
606      * sibling of a current node, or sibling of a parent of a current node).
607      *
608      * @param currentNodeAst Current node in considering
609      * @param subTreeRootAst SubTree root
610      * @return Current node after bypassing, if current node reached the root of a subtree
611      *        method returns null
612      */
613     private static DetailAST
614         getNextSubTreeNode(DetailAST currentNodeAst, DetailAST subTreeRootAst) {
615         DetailAST currentNode = currentNodeAst;
616         DetailAST toVisitAst = currentNode.getFirstChild();
617         while (toVisitAst == null) {
618             toVisitAst = currentNode.getNextSibling();
619             if (currentNode.getParent().equals(subTreeRootAst)) {
620                 break;
621             }
622             currentNode = currentNode.getParent();
623         }
624         return toVisitAst;
625     }
626 
627     /**
628      * Returns true if method has to be checked or false.
629      *
630      * @param ast method def to check.
631      * @return true if we should check this method.
632      */
633     private boolean isCheckedMethod(DetailAST ast) {
634         final String methodName =
635             ast.findFirstToken(TokenTypes.IDENT).getText();
636         return isVerifiable(ast) && !ignoredMethodNames.contains(methodName)
637                 && !AnnotationUtil.hasOverrideAnnotation(ast);
638     }
639 
640     /**
641      * Setter to specify classes that should not be used as types in variable declarations,
642      * return values or parameters.
643      *
644      * @param classNames array of illegal variable types
645      * @noinspection WeakerAccess
646      * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
647      * @since 3.2
648      */
649     public void setIllegalClassNames(String... classNames) {
650         illegalClassNames.clear();
651         Collections.addAll(illegalClassNames, classNames);
652     }
653 
654     /**
655      * Setter to specify methods that should not be checked.
656      *
657      * @param methodNames array of ignored method names
658      * @noinspection WeakerAccess
659      * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
660      * @since 3.2
661      */
662     public void setIgnoredMethodNames(String... methodNames) {
663         ignoredMethodNames.clear();
664         Collections.addAll(ignoredMethodNames, methodNames);
665     }
666 
667     /**
668      * Setter to define abstract classes that may be used as types.
669      *
670      * @param classNames array of legal abstract class names
671      * @noinspection WeakerAccess
672      * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
673      * @since 4.2
674      */
675     public void setLegalAbstractClassNames(String... classNames) {
676         Collections.addAll(legalAbstractClassNames, classNames);
677     }
678 
679     /**
680      * Setter to control whether to check only methods and fields with any of
681      * the specified modifiers.
682      * This property does not affect method calls nor method references nor record components.
683      *
684      * @param modifiers String contains modifiers.
685      * @since 6.3
686      */
687     public void setMemberModifiers(String modifiers) {
688         memberModifiers = TokenUtil.asBitSet(modifiers.split(","));
689     }
690 
691 }