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.Arrays;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.stream.Collectors;
27  
28  import com.puppycrawl.tools.checkstyle.StatelessCheck;
29  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30  import com.puppycrawl.tools.checkstyle.api.DetailAST;
31  import com.puppycrawl.tools.checkstyle.api.FullIdent;
32  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
34  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
35  
36  /**
37   * <p>
38   * Checks that specified types are not declared to be thrown.
39   * Declaring that a method throws {@code java.lang.Error} or
40   * {@code java.lang.RuntimeException} is almost never acceptable.
41   * </p>
42   * <ul>
43   * <li>
44   * Property {@code ignoreOverriddenMethods} - Allow to ignore checking overridden methods
45   * (marked with {@code Override} or {@code java.lang.Override} annotation).
46   * Type is {@code boolean}.
47   * Default value is {@code true}.
48   * </li>
49   * <li>
50   * Property {@code ignoredMethodNames} - Specify names of methods to ignore.
51   * Type is {@code java.lang.String[]}.
52   * Default value is {@code finalize}.
53   * </li>
54   * <li>
55   * Property {@code illegalClassNames} - Specify throw class names to reject.
56   * Type is {@code java.lang.String[]}.
57   * Default value is {@code Error, RuntimeException, Throwable, java.lang.Error,
58   * java.lang.RuntimeException, java.lang.Throwable}.
59   * </li>
60   * </ul>
61   * <p>
62   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
63   * </p>
64   * <p>
65   * Violation Message Keys:
66   * </p>
67   * <ul>
68   * <li>
69   * {@code illegal.throw}
70   * </li>
71   * </ul>
72   *
73   * @since 4.0
74   */
75  @StatelessCheck
76  public final class IllegalThrowsCheck extends AbstractCheck {
77  
78      /**
79       * A key is pointing to the warning message text in "messages.properties"
80       * file.
81       */
82      public static final String MSG_KEY = "illegal.throw";
83  
84      /** Specify names of methods to ignore. */
85      private final Set<String> ignoredMethodNames =
86          Arrays.stream(new String[] {"finalize", }).collect(Collectors.toCollection(HashSet::new));
87  
88      /** Specify throw class names to reject. */
89      private final Set<String> illegalClassNames = Arrays.stream(
90          new String[] {"Error", "RuntimeException", "Throwable", "java.lang.Error",
91                        "java.lang.RuntimeException", "java.lang.Throwable", })
92          .collect(Collectors.toCollection(HashSet::new));
93  
94      /**
95       * Allow to ignore checking overridden methods (marked with {@code Override}
96       * or {@code java.lang.Override} annotation).
97       */
98      private boolean ignoreOverriddenMethods = true;
99  
100     /**
101      * Setter to specify throw class names to reject.
102      *
103      * @param classNames
104      *            array of illegal exception classes
105      * @since 4.0
106      */
107     public void setIllegalClassNames(final String... classNames) {
108         illegalClassNames.clear();
109         illegalClassNames.addAll(
110                 CheckUtil.parseClassNames(classNames));
111     }
112 
113     @Override
114     public int[] getDefaultTokens() {
115         return getRequiredTokens();
116     }
117 
118     @Override
119     public int[] getRequiredTokens() {
120         return new int[] {TokenTypes.LITERAL_THROWS};
121     }
122 
123     @Override
124     public int[] getAcceptableTokens() {
125         return getRequiredTokens();
126     }
127 
128     @Override
129     public void visitToken(DetailAST detailAST) {
130         final DetailAST methodDef = detailAST.getParent();
131         // Check if the method with the given name should be ignored.
132         if (!isIgnorableMethod(methodDef)) {
133             DetailAST token = detailAST.getFirstChild();
134             while (token != null) {
135                 final FullIdent ident = FullIdent.createFullIdent(token);
136                 final String identText = ident.getText();
137                 if (illegalClassNames.contains(identText)) {
138                     log(token, MSG_KEY, identText);
139                 }
140                 token = token.getNextSibling();
141             }
142         }
143     }
144 
145     /**
146      * Checks if current method is ignorable due to Check's properties.
147      *
148      * @param methodDef {@link TokenTypes#METHOD_DEF METHOD_DEF}
149      * @return true if method is ignorable.
150      */
151     private boolean isIgnorableMethod(DetailAST methodDef) {
152         return shouldIgnoreMethod(methodDef.findFirstToken(TokenTypes.IDENT).getText())
153             || ignoreOverriddenMethods
154                && AnnotationUtil.hasOverrideAnnotation(methodDef);
155     }
156 
157     /**
158      * Check if the method is specified in the ignore method list.
159      *
160      * @param name the name to check
161      * @return whether the method with the passed name should be ignored
162      */
163     private boolean shouldIgnoreMethod(String name) {
164         return ignoredMethodNames.contains(name);
165     }
166 
167     /**
168      * Setter to specify names of methods to ignore.
169      *
170      * @param methodNames array of ignored method names
171      * @since 5.4
172      */
173     public void setIgnoredMethodNames(String... methodNames) {
174         ignoredMethodNames.clear();
175         Collections.addAll(ignoredMethodNames, methodNames);
176     }
177 
178     /**
179      * Setter to allow to ignore checking overridden methods
180      * (marked with {@code Override} or {@code java.lang.Override} annotation).
181      *
182      * @param ignoreOverriddenMethods Check's property.
183      * @since 6.4
184      */
185     public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
186         this.ignoreOverriddenMethods = ignoreOverriddenMethods;
187     }
188 
189 }