View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  
11  /**
12   * Checks that constructors do not contain any method calls.
13   *
14   * <p>A constructor must only assign fields from constructor parameters
15   * or from newly created objects, and may delegate to another constructor
16   * via {@code this(...)} or {@code super(...)}. Calling any method
17   * (static or instance) from inside a constructor is forbidden,
18   * including as the right-hand side of a field assignment
19   * (e.g. {@code this.bar = Foo.createBar()}), as an argument to a
20   * delegating constructor call, or as a nested argument to a {@code new}
21   * expression.
22   *
23   * <p>Method calls nested inside lambda bodies or anonymous class bodies
24   * are not considered constructor code, because they are not executed
25   * at construction time: only the lambda object or the anonymous class
26   * instance is created. Such subtrees are skipped.
27   *
28   * @since 0.24
29   */
30  public final class ConstructorsCodeFreeCheck extends AbstractCheck {
31  
32      @Override
33      public int[] getDefaultTokens() {
34          return new int[] {TokenTypes.CTOR_DEF};
35      }
36  
37      @Override
38      public int[] getAcceptableTokens() {
39          return this.getDefaultTokens();
40      }
41  
42      @Override
43      public int[] getRequiredTokens() {
44          return this.getDefaultTokens();
45      }
46  
47      @Override
48      public void visitToken(final DetailAST ast) {
49          final DetailAST body = ast.findFirstToken(TokenTypes.SLIST);
50          if (body != null) {
51              this.reportCalls(body);
52          }
53      }
54  
55      /**
56       * Reports every method call found anywhere in the given subtree,
57       * except those nested inside lambda bodies or anonymous class bodies.
58       * @param node Root of the subtree to scan
59       */
60      private void reportCalls(final DetailAST node) {
61          for (DetailAST child = node.getFirstChild();
62              child != null; child = child.getNextSibling()) {
63              final int type = child.getType();
64              if (type == TokenTypes.LAMBDA || type == TokenTypes.OBJBLOCK) {
65                  continue;
66              }
67              if (type == TokenTypes.METHOD_CALL) {
68                  this.log(
69                      child.getLineNo(),
70                      "Constructor must not contain method calls"
71                  );
72              }
73              this.reportCalls(child);
74          }
75      }
76  }