1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.indentation;
21
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
25
26 /**
27 * Handler for method definitions.
28 *
29 */
30 public class MethodDefHandler extends BlockParentHandler {
31
32 /**
33 * Construct an instance of this handler with the given indentation check,
34 * abstract syntax tree, and parent handler.
35 *
36 * @param indentCheck the indentation check
37 * @param ast the abstract syntax tree
38 * @param parent the parent handler
39 */
40 public MethodDefHandler(IndentationCheck indentCheck,
41 DetailAST ast, AbstractExpressionHandler parent) {
42 super(indentCheck, getHandlerName(ast), ast, parent);
43 }
44
45 @Override
46 protected DetailAST getTopLevelAst() {
47 // we check this stuff ourselves below
48 return null;
49 }
50
51 /**
52 * Checks modifiers for method definitions.
53 */
54 private void checkModifiers() {
55 final DetailAST modifier = getMainAst().findFirstToken(TokenTypes.MODIFIERS);
56 if (isOnStartOfLine(modifier)
57 && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
58 logError(modifier, "modifier", expandedTabsColumnNo(modifier));
59 }
60 }
61
62 /**
63 * Check the indentation level of the throws clause.
64 */
65 private void checkThrows() {
66 final DetailAST throwsAst = getMainAst().findFirstToken(TokenTypes.LITERAL_THROWS);
67
68 if (throwsAst != null) {
69 final LineWrappingHandler.LineWrappingOptions ignoreFirstLine;
70 if (isOnStartOfLine(throwsAst)) {
71 ignoreFirstLine = LineWrappingHandler.LineWrappingOptions.NONE;
72 }
73 else {
74 ignoreFirstLine = LineWrappingHandler.LineWrappingOptions.IGNORE_FIRST_LINE;
75 }
76 checkWrappingIndentation(throwsAst, throwsAst.getNextSibling(), getIndentCheck()
77 .getThrowsIndent(), getLineStart(getMethodDefLineStart(getMainAst())),
78 ignoreFirstLine);
79 }
80 }
81
82 /**
83 * Gets the start line of the method, excluding any annotations. This is required because the
84 * current {@link TokenTypes#METHOD_DEF} may not always be the start as seen in
85 * <a href="https://github.com/checkstyle/checkstyle/issues/3145">#3145</a>.
86 *
87 * @param mainAst
88 * The method definition ast.
89 * @return The start column position of the method.
90 */
91 private static int getMethodDefLineStart(DetailAST mainAst) {
92 // get first type position
93 int lineStart = mainAst.findFirstToken(TokenTypes.IDENT).getLineNo();
94
95 // check if there is a type before the indent
96 final DetailAST typeNode = mainAst.findFirstToken(TokenTypes.TYPE);
97 if (typeNode != null) {
98 lineStart = getFirstLine(typeNode);
99 }
100
101 // check if there is a modifier before the type
102 for (DetailAST node = mainAst.findFirstToken(TokenTypes.MODIFIERS).getFirstChild();
103 node != null;
104 node = node.getNextSibling()) {
105 // skip annotations as we check them else where as outside the method
106 if (node.getType() == TokenTypes.ANNOTATION) {
107 continue;
108 }
109
110 lineStart = Math.min(lineStart, node.getLineNo());
111 }
112
113 return lineStart;
114 }
115
116 @Override
117 public void checkIndentation() {
118 checkModifiers();
119 checkThrows();
120
121 if (getMethodDefParamRightParen(getMainAst()) != null) {
122 checkWrappingIndentation(getMainAst(), getMethodDefParamRightParen(getMainAst()));
123 }
124 // abstract method def -- no body
125 if (getLeftCurly() != null) {
126 super.checkIndentation();
127 }
128 }
129
130 @Override
131 protected boolean shouldCheckLeftParen(DetailAST leftParen) {
132 return !isMethodParenOnItsOwnLineWithMatchingRightParen(leftParen,
133 getMethodDefParamRightParen(getMainAst()));
134 }
135
136 @Override
137 protected boolean shouldCheckRightParen(DetailAST leftParen, DetailAST rightParen) {
138 return !isMethodParenOnItsOwnLineWithMatchingRightParen(leftParen, rightParen);
139 }
140
141 /**
142 * Checks if method definition parenthesis are wrapped on separate lines and aligned.
143 *
144 * @param leftParen left parenthesis of method definition
145 * @param rightParen right parenthesis of method definition
146 * @return true if both parenthesis start their own lines and are aligned
147 */
148 private boolean isMethodParenOnItsOwnLineWithMatchingRightParen(DetailAST leftParen,
149 DetailAST rightParen) {
150 return rightParen != null
151 && !TokenUtil.areOnSameLine(leftParen, rightParen)
152 && isOnStartOfLine(leftParen)
153 && isOnStartOfLine(rightParen)
154 && expandedTabsColumnNo(leftParen) == expandedTabsColumnNo(rightParen);
155 }
156
157 /**
158 * Returns right parenthesis of method definition parameter list.
159 *
160 * @param methodDefAst
161 * method definition ast node(TokenTypes.LITERAL_IF)
162 * @return right parenthesis of method definition parameter list.
163 */
164 private static DetailAST getMethodDefParamRightParen(DetailAST methodDefAst) {
165 return methodDefAst.findFirstToken(TokenTypes.RPAREN);
166 }
167
168 /**
169 * Creates a handler name for this class according to ast type.
170 *
171 * @param ast the abstract syntax tree.
172 * @return handler name for this class.
173 */
174 private static String getHandlerName(DetailAST ast) {
175
176 return switch (ast.getType()) {
177 case TokenTypes.CTOR_DEF -> "ctor def";
178 case TokenTypes.ANNOTATION_FIELD_DEF -> "annotation field def";
179 case TokenTypes.COMPACT_CTOR_DEF -> "compact ctor def";
180 default -> "method def";
181 };
182 }
183
184 }