001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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.utils; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.Scope; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025 026/** 027 * Contains utility methods for working on scope. 028 * 029 */ 030public final class ScopeUtil { 031 032 /** Prevent instantiation. */ 033 private ScopeUtil() { 034 } 035 036 /** 037 * Returns the Scope specified by the modifier set. 038 * 039 * @param aMods root node of a modifier set 040 * @return a {@code Scope} value 041 */ 042 public static Scope getScopeFromMods(DetailAST aMods) { 043 // default scope 044 Scope returnValue = Scope.PACKAGE; 045 for (DetailAST token = aMods.getFirstChild(); token != null 046 && returnValue == Scope.PACKAGE; 047 token = token.getNextSibling()) { 048 if ("public".equals(token.getText())) { 049 returnValue = Scope.PUBLIC; 050 } 051 else if ("protected".equals(token.getText())) { 052 returnValue = Scope.PROTECTED; 053 } 054 else if ("private".equals(token.getText())) { 055 returnValue = Scope.PRIVATE; 056 } 057 } 058 return returnValue; 059 } 060 061 /** 062 * Returns the scope of the surrounding "block". 063 * 064 * @param node the node to return the scope for 065 * @return the Scope of the surrounding block 066 */ 067 public static Scope getSurroundingScope(DetailAST node) { 068 Scope returnValue = null; 069 for (DetailAST token = node.getParent(); 070 token != null; 071 token = token.getParent()) { 072 final int type = token.getType(); 073 if (TokenUtil.isTypeDeclaration(type)) { 074 final DetailAST mods = 075 token.findFirstToken(TokenTypes.MODIFIERS); 076 final Scope modScope = getScopeFromMods(mods); 077 if (returnValue == null || returnValue.isIn(modScope)) { 078 returnValue = modScope; 079 } 080 } 081 else if (type == TokenTypes.LITERAL_NEW) { 082 returnValue = Scope.ANONINNER; 083 // because Scope.ANONINNER is not in any other Scope 084 break; 085 } 086 } 087 088 return returnValue; 089 } 090 091 /** 092 * Returns whether a node is directly contained within a class block. 093 * 094 * @param node the node to check if directly contained within a class block. 095 * @return a {@code boolean} value 096 */ 097 public static boolean isInClassBlock(DetailAST node) { 098 return isInBlockOf(node, TokenTypes.CLASS_DEF); 099 } 100 101 /** 102 * Returns whether a node is directly contained within an interface block. 103 * 104 * @param node the node to check if directly contained within an interface block. 105 * @return a {@code boolean} value 106 */ 107 public static boolean isInInterfaceBlock(DetailAST node) { 108 return isInBlockOf(node, TokenTypes.INTERFACE_DEF); 109 } 110 111 /** 112 * Returns whether a node is directly contained within an annotation block. 113 * 114 * @param node the node to check if directly contained within an annotation block. 115 * @return a {@code boolean} value 116 */ 117 public static boolean isInAnnotationBlock(DetailAST node) { 118 return isInBlockOf(node, TokenTypes.ANNOTATION_DEF); 119 } 120 121 /** 122 * Returns whether a node is directly contained within a specified block. 123 * 124 * @param node the node to check if directly contained within a specified block. 125 * @param tokenType type of token. 126 * @return a {@code boolean} value 127 */ 128 private static boolean isInBlockOf(DetailAST node, int tokenType) { 129 boolean returnValue = false; 130 131 // Loop up looking for a containing interface block 132 for (DetailAST token = node.getParent(); 133 token != null && !returnValue; 134 token = token.getParent()) { 135 final int type = token.getType(); 136 if (type == tokenType) { 137 returnValue = true; 138 } 139 else if (type == TokenTypes.LITERAL_NEW || TokenUtil.isTypeDeclaration(type)) { 140 break; 141 } 142 } 143 144 return returnValue; 145 } 146 147 /** 148 * Returns whether a node is directly contained within an interface or 149 * annotation block. 150 * 151 * @param node the node to check if directly contained within an interface 152 * or annotation block. 153 * @return a {@code boolean} value 154 */ 155 public static boolean isInInterfaceOrAnnotationBlock(DetailAST node) { 156 return isInInterfaceBlock(node) || isInAnnotationBlock(node); 157 } 158 159 /** 160 * Returns whether a node is directly contained within an enum block. 161 * 162 * @param node the node to check if directly contained within an enum block. 163 * @return a {@code boolean} value 164 */ 165 public static boolean isInEnumBlock(DetailAST node) { 166 boolean returnValue = false; 167 168 // Loop up looking for a containing interface block 169 for (DetailAST token = node.getParent(); 170 token != null && !returnValue; 171 token = token.getParent()) { 172 final int type = token.getType(); 173 if (type == TokenTypes.ENUM_DEF) { 174 returnValue = true; 175 } 176 else if (type == TokenTypes.INTERFACE_DEF 177 || type == TokenTypes.ANNOTATION_DEF 178 || type == TokenTypes.CLASS_DEF 179 || type == TokenTypes.LITERAL_NEW) { 180 break; 181 } 182 } 183 184 return returnValue; 185 } 186 187 /** 188 * Returns whether the scope of a node is restricted to a code block. 189 * A code block is a method or constructor body, an initializer block, or lambda body. 190 * 191 * @param node the node to check 192 * @return a {@code boolean} value 193 */ 194 public static boolean isInCodeBlock(DetailAST node) { 195 boolean returnValue = false; 196 197 // Loop up looking for a containing code block 198 for (DetailAST token = node.getParent(); 199 token != null; 200 token = token.getParent()) { 201 final int type = token.getType(); 202 if (type == TokenTypes.METHOD_DEF 203 || type == TokenTypes.CTOR_DEF 204 || type == TokenTypes.INSTANCE_INIT 205 || type == TokenTypes.STATIC_INIT 206 || type == TokenTypes.LAMBDA) { 207 returnValue = true; 208 break; 209 } 210 } 211 212 return returnValue; 213 } 214 215 /** 216 * Returns whether a node is contained in the outer most type block. 217 * 218 * @param node the node to check 219 * @return a {@code boolean} value 220 */ 221 public static boolean isOuterMostType(DetailAST node) { 222 boolean returnValue = true; 223 for (DetailAST parent = node.getParent(); 224 parent != null; 225 parent = parent.getParent()) { 226 if (TokenUtil.isTypeDeclaration(parent.getType())) { 227 returnValue = false; 228 break; 229 } 230 } 231 232 return returnValue; 233 } 234 235 /** 236 * Determines whether a node is a local variable definition. 237 * I.e. if it is declared in a code block, a for initializer, 238 * or a catch parameter. 239 * 240 * @param node the node to check. 241 * @return whether aAST is a local variable definition. 242 */ 243 public static boolean isLocalVariableDef(DetailAST node) { 244 boolean localVariableDef = false; 245 // variable declaration? 246 if (node.getType() == TokenTypes.VARIABLE_DEF) { 247 final DetailAST parent = node.getParent(); 248 final int type = parent.getType(); 249 localVariableDef = type == TokenTypes.SLIST 250 || type == TokenTypes.FOR_INIT 251 || type == TokenTypes.FOR_EACH_CLAUSE; 252 } 253 // catch parameter? 254 if (node.getType() == TokenTypes.PARAMETER_DEF) { 255 final DetailAST parent = node.getParent(); 256 localVariableDef = parent.getType() == TokenTypes.LITERAL_CATCH; 257 } 258 259 if (node.getType() == TokenTypes.RESOURCE) { 260 localVariableDef = true; 261 } 262 return localVariableDef; 263 } 264 265 /** 266 * Determines whether a node is a class field definition. 267 * I.e. if a variable is not declared in a code block, a for initializer, 268 * or a catch parameter. 269 * 270 * @param node the node to check. 271 * @return whether a node is a class field definition. 272 */ 273 public static boolean isClassFieldDef(DetailAST node) { 274 return node.getType() == TokenTypes.VARIABLE_DEF && !isLocalVariableDef(node); 275 } 276 277 /** 278 * Checks whether ast node is in a specific scope. 279 * 280 * @param ast the node to check. 281 * @param scope a {@code Scope} value. 282 * @return true if the ast node is in the scope. 283 */ 284 public static boolean isInScope(DetailAST ast, Scope scope) { 285 final Scope surroundingScopeOfAstToken = getSurroundingScope(ast); 286 return surroundingScopeOfAstToken == scope; 287 } 288 289}