001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.HashSet; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import com.puppycrawl.tools.checkstyle.StatelessCheck; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.DetailAST; 031import com.puppycrawl.tools.checkstyle.api.FullIdent; 032import com.puppycrawl.tools.checkstyle.api.TokenTypes; 033import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 034import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 035 036/** 037 * <p> 038 * Checks that specified types are not declared to be thrown. 039 * Declaring that a method throws {@code java.lang.Error} or 040 * {@code java.lang.RuntimeException} is almost never acceptable. 041 * </p> 042 * <ul> 043 * <li> 044 * Property {@code illegalClassNames} - Specify throw class names to reject. 045 * Type is {@code java.lang.String[]}. 046 * Default value is {@code Error, RuntimeException, Throwable, java.lang.Error, 047 * java.lang.RuntimeException, java.lang.Throwable}. 048 * </li> 049 * <li> 050 * Property {@code ignoredMethodNames} - Specify names of methods to ignore. 051 * Type is {@code java.lang.String[]}. 052 * Default value is {@code finalize}. 053 * </li> 054 * <li> 055 * Property {@code ignoreOverriddenMethods} - allow to ignore checking overridden methods 056 * (marked with {@code Override} or {@code java.lang.Override} annotation). 057 * Type is {@code boolean}. 058 * Default value is {@code true}. 059 * </li> 060 * </ul> 061 * <p> 062 * To configure the check: 063 * </p> 064 * <pre> 065 * <module name="IllegalThrows"/> 066 * </pre> 067 * <p>Example:</p> 068 * <pre> 069 * public class Test { 070 * public void func1() throws RuntimeException {} // violation 071 * public void func2() throws Exception {} // ok 072 * public void func3() throws Error {} // violation 073 * public void func4() throws Throwable {} // violation 074 * public void func5() throws NullPointerException {} // ok 075 * @Override 076 * public void toString() throws Error {} // ok 077 * } 078 * </pre> 079 * <p> 080 * To configure the check rejecting throws NullPointerException from methods: 081 * </p> 082 * <pre> 083 * <module name="IllegalThrows"> 084 * <property name="illegalClassNames" value="NullPointerException"/> 085 * </module> 086 * </pre> 087 * <p>Example:</p> 088 * <pre> 089 * public class Test { 090 * public void func1() throws RuntimeException {} // ok 091 * public void func2() throws Exception {} // ok 092 * public void func3() throws Error {} // ok 093 * public void func4() throws Throwable {} // ok 094 * public void func5() throws NullPointerException {} // violation 095 * @Override 096 * public void toString() throws Error {} // ok 097 * } 098 * </pre> 099 * <p> 100 * To configure the check ignoring method named "func1()": 101 * </p> 102 * <pre> 103 * <module name="IllegalThrows"> 104 * <property name="ignoredMethodNames" value="func1"/> 105 * </module> 106 * </pre> 107 * <p>Example:</p> 108 * <pre> 109 * public class Test { 110 * public void func1() throws RuntimeException {} // ok 111 * public void func2() throws Exception {} // ok 112 * public void func3() throws Error {} // violation 113 * public void func4() throws Throwable {} // violation 114 * public void func5() throws NullPointerException {} // ok 115 * @Override 116 * public void toString() throws Error {} // ok 117 * } 118 * </pre> 119 * <p> 120 * To configure the check to warn on overridden methods: 121 * </p> 122 * <pre> 123 * <module name="IllegalThrows"> 124 * <property name="ignoreOverriddenMethods" value="false"/> 125 * </module> 126 * </pre> 127 * <p>Example:</p> 128 * <pre> 129 * public class Test { 130 * public void func1() throws RuntimeException {} // violation 131 * public void func2() throws Exception {} // ok 132 * public void func3() throws Error {} // violation 133 * public void func4() throws Throwable {} // violation 134 * public void func5() throws NullPointerException {} // ok 135 * @Override 136 * public void toString() throws Error {} // violation 137 * } 138 * </pre> 139 * <p> 140 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 141 * </p> 142 * <p> 143 * Violation Message Keys: 144 * </p> 145 * <ul> 146 * <li> 147 * {@code illegal.throw} 148 * </li> 149 * </ul> 150 * 151 * @since 4.0 152 */ 153@StatelessCheck 154public final class IllegalThrowsCheck extends AbstractCheck { 155 156 /** 157 * A key is pointing to the warning message text in "messages.properties" 158 * file. 159 */ 160 public static final String MSG_KEY = "illegal.throw"; 161 162 /** Specify names of methods to ignore. */ 163 private final Set<String> ignoredMethodNames = 164 Arrays.stream(new String[] {"finalize", }).collect(Collectors.toCollection(HashSet::new)); 165 166 /** Specify throw class names to reject. */ 167 private final Set<String> illegalClassNames = Arrays.stream( 168 new String[] {"Error", "RuntimeException", "Throwable", "java.lang.Error", 169 "java.lang.RuntimeException", "java.lang.Throwable", }) 170 .collect(Collectors.toCollection(HashSet::new)); 171 172 /** 173 * Allow to ignore checking overridden methods (marked with {@code Override} 174 * or {@code java.lang.Override} annotation). 175 */ 176 private boolean ignoreOverriddenMethods = true; 177 178 /** 179 * Setter to specify throw class names to reject. 180 * 181 * @param classNames 182 * array of illegal exception classes 183 */ 184 public void setIllegalClassNames(final String... classNames) { 185 illegalClassNames.clear(); 186 illegalClassNames.addAll( 187 CheckUtil.parseClassNames(classNames)); 188 } 189 190 @Override 191 public int[] getDefaultTokens() { 192 return getRequiredTokens(); 193 } 194 195 @Override 196 public int[] getRequiredTokens() { 197 return new int[] {TokenTypes.LITERAL_THROWS}; 198 } 199 200 @Override 201 public int[] getAcceptableTokens() { 202 return getRequiredTokens(); 203 } 204 205 @Override 206 public void visitToken(DetailAST detailAST) { 207 final DetailAST methodDef = detailAST.getParent(); 208 // Check if the method with the given name should be ignored. 209 if (!isIgnorableMethod(methodDef)) { 210 DetailAST token = detailAST.getFirstChild(); 211 while (token != null) { 212 final FullIdent ident = FullIdent.createFullIdent(token); 213 final String identText = ident.getText(); 214 if (illegalClassNames.contains(identText)) { 215 log(token, MSG_KEY, identText); 216 } 217 token = token.getNextSibling(); 218 } 219 } 220 } 221 222 /** 223 * Checks if current method is ignorable due to Check's properties. 224 * 225 * @param methodDef {@link TokenTypes#METHOD_DEF METHOD_DEF} 226 * @return true if method is ignorable. 227 */ 228 private boolean isIgnorableMethod(DetailAST methodDef) { 229 return shouldIgnoreMethod(methodDef.findFirstToken(TokenTypes.IDENT).getText()) 230 || ignoreOverriddenMethods 231 && AnnotationUtil.hasOverrideAnnotation(methodDef); 232 } 233 234 /** 235 * Check if the method is specified in the ignore method list. 236 * 237 * @param name the name to check 238 * @return whether the method with the passed name should be ignored 239 */ 240 private boolean shouldIgnoreMethod(String name) { 241 return ignoredMethodNames.contains(name); 242 } 243 244 /** 245 * Setter to specify names of methods to ignore. 246 * 247 * @param methodNames array of ignored method names 248 */ 249 public void setIgnoredMethodNames(String... methodNames) { 250 ignoredMethodNames.clear(); 251 Collections.addAll(ignoredMethodNames, methodNames); 252 } 253 254 /** 255 * Setter to allow to ignore checking overridden methods 256 * (marked with {@code Override} or {@code java.lang.Override} annotation). 257 * 258 * @param ignoreOverriddenMethods Check's property. 259 */ 260 public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) { 261 this.ignoreOverriddenMethods = ignoreOverriddenMethods; 262 } 263 264}