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 (type == TokenTypes.CLASS_DEF 074 || type == TokenTypes.INTERFACE_DEF 075 || type == TokenTypes.ANNOTATION_DEF 076 || type == TokenTypes.ENUM_DEF) { 077 final DetailAST mods = 078 token.findFirstToken(TokenTypes.MODIFIERS); 079 final Scope modScope = getScopeFromMods(mods); 080 if (returnValue == null || returnValue.isIn(modScope)) { 081 returnValue = modScope; 082 } 083 } 084 else if (type == TokenTypes.LITERAL_NEW) { 085 returnValue = Scope.ANONINNER; 086 // because Scope.ANONINNER is not in any other Scope 087 break; 088 } 089 } 090 091 return returnValue; 092 } 093 094 /** 095 * Returns whether a node is directly contained within a class block. 096 * 097 * @param node the node to check if directly contained within a class block. 098 * @return a {@code boolean} value 099 */ 100 public static boolean isInClassBlock(DetailAST node) { 101 return isInBlockOf(node, TokenTypes.CLASS_DEF); 102 } 103 104 /** 105 * Returns whether a node is directly contained within an interface block. 106 * 107 * @param node the node to check if directly contained within an interface block. 108 * @return a {@code boolean} value 109 */ 110 public static boolean isInInterfaceBlock(DetailAST node) { 111 return isInBlockOf(node, TokenTypes.INTERFACE_DEF); 112 } 113 114 /** 115 * Returns whether a node is directly contained within an annotation block. 116 * 117 * @param node the node to check if directly contained within an annotation block. 118 * @return a {@code boolean} value 119 */ 120 public static boolean isInAnnotationBlock(DetailAST node) { 121 return isInBlockOf(node, TokenTypes.ANNOTATION_DEF); 122 } 123 124 /** 125 * Returns whether a node is directly contained within a specified block. 126 * 127 * @param node the node to check if directly contained within a specified block. 128 * @param tokenType type of token. 129 * @return a {@code boolean} value 130 */ 131 private static boolean isInBlockOf(DetailAST node, int tokenType) { 132 boolean returnValue = false; 133 134 // Loop up looking for a containing interface block 135 for (DetailAST token = node.getParent(); 136 token != null && !returnValue; 137 token = token.getParent()) { 138 final int type = token.getType(); 139 if (type == tokenType) { 140 returnValue = true; 141 } 142 else if (type == TokenTypes.CLASS_DEF 143 || type == TokenTypes.ENUM_DEF 144 || type == TokenTypes.INTERFACE_DEF 145 || type == TokenTypes.ANNOTATION_DEF 146 || type == TokenTypes.LITERAL_NEW) { 147 break; 148 } 149 } 150 151 return returnValue; 152 } 153 154 /** 155 * Returns whether a node is directly contained within an interface or 156 * annotation block. 157 * 158 * @param node the node to check if directly contained within an interface 159 * or annotation block. 160 * @return a {@code boolean} value 161 */ 162 public static boolean isInInterfaceOrAnnotationBlock(DetailAST node) { 163 return isInInterfaceBlock(node) || isInAnnotationBlock(node); 164 } 165 166 /** 167 * Returns whether a node is directly contained within an enum block. 168 * 169 * @param node the node to check if directly contained within an enum block. 170 * @return a {@code boolean} value 171 */ 172 public static boolean isInEnumBlock(DetailAST node) { 173 boolean returnValue = false; 174 175 // Loop up looking for a containing interface block 176 for (DetailAST token = node.getParent(); 177 token != null && !returnValue; 178 token = token.getParent()) { 179 final int type = token.getType(); 180 if (type == TokenTypes.ENUM_DEF) { 181 returnValue = true; 182 } 183 else if (type == TokenTypes.INTERFACE_DEF 184 || type == TokenTypes.ANNOTATION_DEF 185 || type == TokenTypes.CLASS_DEF 186 || type == TokenTypes.LITERAL_NEW) { 187 break; 188 } 189 } 190 191 return returnValue; 192 } 193 194 /** 195 * Returns whether the scope of a node is restricted to a code block. 196 * A code block is a method or constructor body, an initializer block, or lambda body. 197 * 198 * @param node the node to check 199 * @return a {@code boolean} value 200 */ 201 public static boolean isInCodeBlock(DetailAST node) { 202 boolean returnValue = false; 203 204 // Loop up looking for a containing code block 205 for (DetailAST token = node.getParent(); 206 token != null; 207 token = token.getParent()) { 208 final int type = token.getType(); 209 if (type == TokenTypes.METHOD_DEF 210 || type == TokenTypes.CTOR_DEF 211 || type == TokenTypes.INSTANCE_INIT 212 || type == TokenTypes.STATIC_INIT 213 || type == TokenTypes.LAMBDA) { 214 returnValue = true; 215 break; 216 } 217 } 218 219 return returnValue; 220 } 221 222 /** 223 * Returns whether a node is contained in the outer most type block. 224 * 225 * @param node the node to check 226 * @return a {@code boolean} value 227 */ 228 public static boolean isOuterMostType(DetailAST node) { 229 boolean returnValue = true; 230 for (DetailAST parent = node.getParent(); 231 parent != null; 232 parent = parent.getParent()) { 233 if (parent.getType() == TokenTypes.CLASS_DEF 234 || parent.getType() == TokenTypes.INTERFACE_DEF 235 || parent.getType() == TokenTypes.ANNOTATION_DEF 236 || parent.getType() == TokenTypes.ENUM_DEF) { 237 returnValue = false; 238 break; 239 } 240 } 241 242 return returnValue; 243 } 244 245 /** 246 * Determines whether a node is a local variable definition. 247 * I.e. if it is declared in a code block, a for initializer, 248 * or a catch parameter. 249 * 250 * @param node the node to check. 251 * @return whether aAST is a local variable definition. 252 */ 253 public static boolean isLocalVariableDef(DetailAST node) { 254 boolean localVariableDef = false; 255 // variable declaration? 256 if (node.getType() == TokenTypes.VARIABLE_DEF) { 257 final DetailAST parent = node.getParent(); 258 final int type = parent.getType(); 259 localVariableDef = type == TokenTypes.SLIST 260 || type == TokenTypes.FOR_INIT 261 || type == TokenTypes.FOR_EACH_CLAUSE; 262 } 263 // catch parameter? 264 if (node.getType() == TokenTypes.PARAMETER_DEF) { 265 final DetailAST parent = node.getParent(); 266 localVariableDef = parent.getType() == TokenTypes.LITERAL_CATCH; 267 } 268 269 if (node.getType() == TokenTypes.RESOURCE) { 270 localVariableDef = true; 271 } 272 return localVariableDef; 273 } 274 275 /** 276 * Determines whether a node is a class field definition. 277 * I.e. if a variable is not declared in a code block, a for initializer, 278 * or a catch parameter. 279 * 280 * @param node the node to check. 281 * @return whether a node is a class field definition. 282 */ 283 public static boolean isClassFieldDef(DetailAST node) { 284 return node.getType() == TokenTypes.VARIABLE_DEF && !isLocalVariableDef(node); 285 } 286 287 /** 288 * Checks whether ast node is in a specific scope. 289 * 290 * @param ast the node to check. 291 * @param scope a {@code Scope} value. 292 * @return true if the ast node is in the scope. 293 */ 294 public static boolean isInScope(DetailAST ast, Scope scope) { 295 final Scope surroundingScopeOfAstToken = getSurroundingScope(ast); 296 return surroundingScopeOfAstToken == scope; 297 } 298 299}