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