View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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.LinkedList;
24  import java.util.List;
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.CheckUtils;
34  
35  /**
36   * Catching java.lang.Exception, java.lang.Error or java.lang.RuntimeException
37   * is almost never acceptable.
38   * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
39   * @author <a href="mailto:IliaDubinin91@gmail.com">Ilja Dubinin</a>
40   */
41  @StatelessCheck
42  public final class IllegalCatchCheck extends AbstractCheck {
43  
44      /**
45       * A key is pointing to the warning message text in "messages.properties"
46       * file.
47       */
48      public static final String MSG_KEY = "illegal.catch";
49  
50      /** Illegal class names. */
51      private final Set<String> illegalClassNames = Arrays.stream(new String[] {"Exception", "Error",
52          "RuntimeException", "Throwable", "java.lang.Error", "java.lang.Exception",
53          "java.lang.RuntimeException", "java.lang.Throwable", }).collect(Collectors.toSet());
54  
55      /**
56       * Set the list of illegal classes.
57       *
58       * @param classNames
59       *            array of illegal exception classes
60       */
61      public void setIllegalClassNames(final String... classNames) {
62          illegalClassNames.clear();
63          illegalClassNames.addAll(
64                  CheckUtils.parseClassNames(classNames));
65      }
66  
67      @Override
68      public int[] getDefaultTokens() {
69          return getRequiredTokens();
70      }
71  
72      @Override
73      public int[] getRequiredTokens() {
74          return new int[] {TokenTypes.LITERAL_CATCH};
75      }
76  
77      @Override
78      public int[] getAcceptableTokens() {
79          return getRequiredTokens();
80      }
81  
82      @Override
83      public void visitToken(DetailAST detailAST) {
84          final DetailAST parameterDef =
85              detailAST.findFirstToken(TokenTypes.PARAMETER_DEF);
86          final DetailAST excTypeParent =
87                  parameterDef.findFirstToken(TokenTypes.TYPE);
88          final List<DetailAST> excTypes = getAllExceptionTypes(excTypeParent);
89  
90          for (DetailAST excType : excTypes) {
91              final FullIdent ident = FullIdent.createFullIdent(excType);
92  
93              if (illegalClassNames.contains(ident.getText())) {
94                  log(detailAST, MSG_KEY, ident.getText());
95              }
96          }
97      }
98  
99      /**
100      * Finds all exception types in current catch.
101      * We need it till we can have few different exception types into one catch.
102      * @param parentToken - parent node for types (TYPE or BOR)
103      * @return list, that contains all exception types in current catch
104      */
105     private static List<DetailAST> getAllExceptionTypes(DetailAST parentToken) {
106         DetailAST currentNode = parentToken.getFirstChild();
107         final List<DetailAST> exceptionTypes = new LinkedList<>();
108         if (currentNode.getType() == TokenTypes.BOR) {
109             exceptionTypes.addAll(getAllExceptionTypes(currentNode));
110             currentNode = currentNode.getNextSibling();
111             if (currentNode != null) {
112                 exceptionTypes.add(currentNode);
113             }
114         }
115         else {
116             exceptionTypes.add(currentNode);
117             currentNode = currentNode.getNextSibling();
118             while (currentNode != null) {
119                 exceptionTypes.add(currentNode);
120                 currentNode = currentNode.getNextSibling();
121             }
122         }
123         return exceptionTypes;
124     }
125 
126 }