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