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