001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.indentation; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023 024/** 025 * Handler for lambda expressions. 026 * 027 */ 028public class LambdaHandler extends AbstractExpressionHandler { 029 /** 030 * Checks whether the lambda is correctly indented, this variable get its value from checking 031 * the lambda handler's indentation, and it is being used in aligning the lambda's children. 032 * A true value depicts lambda is correctly aligned without giving any errors. 033 * This is updated to false where there is any Indentation error log. 034 */ 035 private boolean isLambdaCorrectlyIndented = true; 036 037 /** 038 * Construct an instance of this handler with the given indentation check, 039 * abstract syntax tree, and parent handler. 040 * 041 * @param indentCheck the indentation check 042 * @param ast the abstract syntax tree 043 * @param parent the parent handler 044 */ 045 public LambdaHandler(IndentationCheck indentCheck, 046 DetailAST ast, AbstractExpressionHandler parent) { 047 super(indentCheck, "lambda", ast, parent); 048 } 049 050 @Override 051 public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) { 052 IndentLevel childIndent = getIndent(); 053 if (isLambdaCorrectlyIndented) { 054 childIndent = IndentLevel.addAcceptable(childIndent, getLineStart(getMainAst()), 055 getLineStart(getMainAst().getFirstChild())); 056 } 057 058 return childIndent; 059 } 060 061 /** 062 * {@inheritDoc}. 063 * 064 * @noinspection MethodWithMultipleReturnPoints 065 * @noinspectionreason MethodWithMultipleReturnPoints - indentation is complex and 066 * tightly coupled, thus making this method difficult to refactor 067 */ 068 @Override 069 protected IndentLevel getIndentImpl() { 070 if (getParent() instanceof MethodCallHandler) { 071 return getParent().getSuggestedChildIndent(this); 072 } 073 074 DetailAST parent = getMainAst().getParent(); 075 if (getParent() instanceof NewHandler) { 076 parent = parent.getParent(); 077 } 078 079 // Use the start of the parent's line as the reference indentation level. 080 IndentLevel level = new IndentLevel(getLineStart(parent)); 081 082 // If the start of the lambda is the first element on the line; 083 // assume line wrapping with respect to its parent. 084 final DetailAST firstChild = getMainAst().getFirstChild(); 085 if (getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) { 086 level = new IndentLevel(level, getIndentCheck().getLineWrappingIndentation()); 087 } 088 089 return level; 090 } 091 092 @Override 093 public void checkIndentation() { 094 final DetailAST mainAst = getMainAst(); 095 final DetailAST firstChild = mainAst.getFirstChild(); 096 097 // If the "->" has no children, it is a switch 098 // rule lambda (i.e. 'case ONE -> 1;') 099 final boolean isSwitchRuleLambda = firstChild == null; 100 101 if (!isSwitchRuleLambda 102 && getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) { 103 final int firstChildColumnNo = expandedTabsColumnNo(firstChild); 104 final IndentLevel level = getIndent(); 105 106 if (isNonAcceptableIndent(firstChildColumnNo, level)) { 107 isLambdaCorrectlyIndented = false; 108 logError(firstChild, "arguments", firstChildColumnNo, level); 109 } 110 } 111 112 // If the "->" is the first element on the line, assume line wrapping. 113 final int mainAstColumnNo = expandedTabsColumnNo(mainAst); 114 final boolean isLineWrappedLambda = mainAstColumnNo == getLineStart(mainAst); 115 if (isLineWrappedLambda) { 116 checkLineWrappedLambda(isSwitchRuleLambda, mainAstColumnNo); 117 } 118 } 119 120 /** 121 * Checks that given indent is acceptable or not. 122 * 123 * @param astColumnNo indent value to check 124 * @param level indent level 125 * @return true if indent is not acceptable 126 */ 127 private boolean isNonAcceptableIndent(int astColumnNo, IndentLevel level) { 128 return astColumnNo < level.getFirstIndentLevel() 129 || getIndentCheck().isForceStrictCondition() 130 && !level.isAcceptable(astColumnNo); 131 } 132 133 /** 134 * This method checks a line wrapped lambda, whether it is a lambda 135 * expression or switch rule lambda. 136 * 137 * @param isSwitchRuleLambda if mainAst is a switch rule lambda 138 * @param mainAstColumnNo the column number of the lambda we are checking 139 */ 140 private void checkLineWrappedLambda(final boolean isSwitchRuleLambda, 141 final int mainAstColumnNo) { 142 final IndentLevel level; 143 final DetailAST mainAst = getMainAst(); 144 145 if (isSwitchRuleLambda) { 146 // We check the indentation of the case literal or default literal 147 // on the previous line and use that to determine the correct 148 // indentation for the line wrapped "->" 149 final DetailAST previousSibling = mainAst.getPreviousSibling(); 150 final int previousLineStart = getLineStart(previousSibling); 151 152 level = new IndentLevel(new IndentLevel(previousLineStart), 153 getIndentCheck().getLineWrappingIndentation()); 154 } 155 else { 156 level = new IndentLevel(getIndent(), 157 getIndentCheck().getLineWrappingIndentation()); 158 } 159 160 if (isNonAcceptableIndent(mainAstColumnNo, level)) { 161 isLambdaCorrectlyIndented = false; 162 logError(mainAst, "", mainAstColumnNo, level); 163 } 164 } 165}