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