001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.checks.whitespace; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.CodePointUtil; 027import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 028 029/** 030 * <p> 031 * Checks that there is no whitespace before a token. 032 * More specifically, it checks that it is not preceded with whitespace, 033 * or (if linebreaks are allowed) all characters on the line before are 034 * whitespace. To allow linebreaks before a token, set property 035 * {@code allowLineBreaks} to {@code true}. No check occurs before semi-colons in empty 036 * for loop initializers or conditions. 037 * </p> 038 * <ul> 039 * <li> 040 * Property {@code allowLineBreaks} - Control whether whitespace is allowed 041 * if the token is at a linebreak. 042 * Type is {@code boolean}. 043 * Default value is {@code false}. 044 * </li> 045 * <li> 046 * Property {@code tokens} - tokens to check 047 * Type is {@code java.lang.String[]}. 048 * Validation type is {@code tokenSet}. 049 * Default value is: 050 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA"> 051 * COMMA</a>, 052 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#SEMI"> 053 * SEMI</a>, 054 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_INC"> 055 * POST_INC</a>, 056 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#POST_DEC"> 057 * POST_DEC</a>, 058 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ELLIPSIS"> 059 * ELLIPSIS</a>, 060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LABELED_STAT"> 061 * LABELED_STAT</a>. 062 * </li> 063 * </ul> 064 * <p> 065 * To configure the check: 066 * </p> 067 * <pre> 068 * <module name="NoWhitespaceBefore"/> 069 * </pre> 070 * <p>Example:</p> 071 * <pre> 072 * int foo; 073 * foo ++; // violation, whitespace before '++' is not allowed 074 * foo++; // OK 075 * for (int i = 0 ; i < 5; i++) {} // violation 076 * // ^ whitespace before ';' is not allowed 077 * for (int i = 0; i < 5; i++) {} // OK 078 * int[][] array = { { 1, 2 } 079 * , { 3, 4 } }; // violation, whitespace before ',' is not allowed 080 * int[][] array2 = { { 1, 2 }, 081 * { 3, 4 } }; // OK 082 * Lists.charactersOf("foo").listIterator() 083 * .forEachRemaining(System.out::print) 084 * ; // violation, whitespace before ';' is not allowed 085 * { 086 * label1 : // violation, whitespace before ':' is not allowed 087 * for (int i = 0; i < 10; i++) {} 088 * } 089 * 090 * { 091 * label2: // OK 092 * while (true) {} 093 * } 094 * </pre> 095 * <p>To configure the check to allow linebreaks before default tokens:</p> 096 * <pre> 097 * <module name="NoWhitespaceBefore"> 098 * <property name="allowLineBreaks" value="true"/> 099 * </module> 100 * </pre> 101 * <p>Example:</p> 102 * <pre> 103 * int[][] array = { { 1, 2 } 104 * , { 3, 4 } }; // OK, linebreak is allowed before ',' 105 * int[][] array2 = { { 1, 2 }, 106 * { 3, 4 } }; // OK, ideal code 107 * void ellipsisExample(String ...params) {}; // violation, whitespace before '...' is not allowed 108 * void ellipsisExample2(String 109 * ...params) {}; //OK, linebreak is allowed before '...' 110 * Lists.charactersOf("foo") 111 * .listIterator() 112 * .forEachRemaining(System.out::print); // OK 113 * </pre> 114 * <p> 115 * To Configure the check to restrict the use of whitespace before METHOD_REF and DOT tokens: 116 * </p> 117 * <pre> 118 * <module name="NoWhitespaceBefore"> 119 * <property name="tokens" value="METHOD_REF"/> 120 * <property name="tokens" value="DOT"/> 121 * </module> 122 * </pre> 123 * <p>Example:</p> 124 * <pre> 125 * Lists.charactersOf("foo").listIterator() 126 * .forEachRemaining(System.out::print); // violation, whitespace before '.' is not allowed 127 * Lists.charactersOf("foo").listIterator().forEachRemaining(System.out ::print); // violation, 128 * // whitespace before '::' is not allowed ^ 129 * Lists.charactersOf("foo").listIterator().forEachRemaining(System.out::print); // OK 130 * </pre> 131 * <p> 132 * To configure the check to allow linebreak before METHOD_REF and DOT tokens: 133 * </p> 134 * <pre> 135 * <module name="NoWhitespaceBefore"> 136 * <property name="tokens" value="METHOD_REF"/> 137 * <property name="tokens" value="DOT"/> 138 * <property name="allowLineBreaks" value="true"/> 139 * </module> 140 * </pre> 141 * <p>Example:</p> 142 * <pre> 143 * Lists .charactersOf("foo") //violation, whitespace before '.' is not allowed 144 * .listIterator() 145 * .forEachRemaining(System.out ::print); // violation, 146 * // ^ whitespace before '::' is not allowed 147 * Lists.charactersOf("foo") 148 * .listIterator() 149 * .forEachRemaining(System.out::print); // OK 150 * </pre> 151 * <p> 152 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 153 * </p> 154 * <p> 155 * Violation Message Keys: 156 * </p> 157 * <ul> 158 * <li> 159 * {@code ws.preceded} 160 * </li> 161 * </ul> 162 * 163 * @since 3.0 164 */ 165@StatelessCheck 166public class NoWhitespaceBeforeCheck 167 extends AbstractCheck { 168 169 /** 170 * A key is pointing to the warning message text in "messages.properties" 171 * file. 172 */ 173 public static final String MSG_KEY = "ws.preceded"; 174 175 /** Control whether whitespace is allowed if the token is at a linebreak. */ 176 private boolean allowLineBreaks; 177 178 @Override 179 public int[] getDefaultTokens() { 180 return new int[] { 181 TokenTypes.COMMA, 182 TokenTypes.SEMI, 183 TokenTypes.POST_INC, 184 TokenTypes.POST_DEC, 185 TokenTypes.ELLIPSIS, 186 TokenTypes.LABELED_STAT, 187 }; 188 } 189 190 @Override 191 public int[] getAcceptableTokens() { 192 return new int[] { 193 TokenTypes.COMMA, 194 TokenTypes.SEMI, 195 TokenTypes.POST_INC, 196 TokenTypes.POST_DEC, 197 TokenTypes.DOT, 198 TokenTypes.GENERIC_START, 199 TokenTypes.GENERIC_END, 200 TokenTypes.ELLIPSIS, 201 TokenTypes.LABELED_STAT, 202 TokenTypes.METHOD_REF, 203 }; 204 } 205 206 @Override 207 public int[] getRequiredTokens() { 208 return CommonUtil.EMPTY_INT_ARRAY; 209 } 210 211 @Override 212 public void visitToken(DetailAST ast) { 213 final int[] line = getLineCodePoints(ast.getLineNo() - 1); 214 final int columnNoBeforeToken = ast.getColumnNo() - 1; 215 final boolean isFirstToken = columnNoBeforeToken == -1; 216 217 if ((isFirstToken || CommonUtil.isCodePointWhitespace(line, columnNoBeforeToken)) 218 && !isInEmptyForInitializerOrCondition(ast)) { 219 final boolean isViolation = !allowLineBreaks 220 || !isFirstToken 221 && !CodePointUtil.hasWhitespaceBefore(columnNoBeforeToken, line); 222 223 if (isViolation) { 224 log(ast, MSG_KEY, ast.getText()); 225 } 226 } 227 } 228 229 /** 230 * Checks that semicolon is in empty for initializer or condition. 231 * 232 * @param semicolonAst DetailAST of semicolon. 233 * @return true if semicolon is in empty for initializer or condition. 234 */ 235 private static boolean isInEmptyForInitializerOrCondition(DetailAST semicolonAst) { 236 boolean result = false; 237 final DetailAST sibling = semicolonAst.getPreviousSibling(); 238 if (sibling != null 239 && (sibling.getType() == TokenTypes.FOR_INIT 240 || sibling.getType() == TokenTypes.FOR_CONDITION) 241 && !sibling.hasChildren()) { 242 result = true; 243 } 244 return result; 245 } 246 247 /** 248 * Setter to control whether whitespace is allowed if the token is at a linebreak. 249 * 250 * @param allowLineBreaks whether whitespace should be 251 * flagged at line breaks. 252 */ 253 public void setAllowLineBreaks(boolean allowLineBreaks) { 254 this.allowLineBreaks = allowLineBreaks; 255 } 256 257}