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