NoCloneCheck.java

1
////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2018 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.coding;
21
22
import com.puppycrawl.tools.checkstyle.StatelessCheck;
23
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24
import com.puppycrawl.tools.checkstyle.api.DetailAST;
25
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26
27
/**
28
 * <p>
29
 * Checks that the clone method is not overridden from the
30
 * Object class.
31
 * </p>
32
 *
33
 * <p>Rationale: The clone method relies on strange/hard to follow rules that
34
 * do not work it all situations.  Consequently, it is difficult to
35
 * override correctly.  Below are some of the rules/reasons why the clone
36
 * method should be avoided.
37
 *
38
 * <ul>
39
 * <li>
40
 * Classes supporting the clone method should implement the Cloneable
41
 * interface but the Cloneable interface does not include the clone method.
42
 * As a result, it doesn't enforce the method override.
43
 * </li>
44
 * <li>
45
 * The Cloneable interface forces the Object's clone method to work
46
 * correctly. Without implementing it, the Object's clone method will
47
 * throw a CloneNotSupportedException.
48
 * </li>
49
 * <li>
50
 * Non-final classes must return the object returned from a call to
51
 * super.clone().
52
 * </li>
53
 * <li>
54
 * Final classes can use a constructor to create a clone which is different
55
 * from non-final classes.
56
 * </li>
57
 * <li>
58
 * If a super class implements the clone method incorrectly all subclasses
59
 * calling super.clone() are doomed to failure.
60
 * </li>
61
 * <li>
62
 * If a class has references to mutable objects then those object
63
 * references must be replaced with copies in the clone method
64
 * after calling super.clone().
65
 * </li>
66
 * <li>
67
 * The clone method does not work correctly with final mutable object
68
 * references because final references cannot be reassigned.
69
 * </li>
70
 * <li>
71
 * If a super class overrides the clone method then all subclasses must
72
 * provide a correct clone implementation.
73
 * </li>
74
 * </ul>
75
 *
76
 * <p>Two alternatives to the clone method, in some cases, is a copy constructor
77
 * or a static factory method to return copies of an object. Both of these
78
 * approaches are simpler and do not conflict with final fields. They do not
79
 * force the calling client to handle a CloneNotSupportedException.  They also
80
 * are typed therefore no casting is necessary. Finally, they are more
81
 * flexible since they can take interface types rather than concrete classes.
82
 *
83
 * <p>Sometimes a copy constructor or static factory is not an acceptable
84
 * alternative to the clone method.  The example below highlights the
85
 * limitation of a copy constructor (or static factory). Assume
86
 * Square is a subclass for Shape.
87
 *
88
 * <pre>
89
 * Shape s1 = new Square();
90
 * System.out.println(s1 instanceof Square); //true
91
 * </pre>
92
 * ...assume at this point the code knows nothing of s1 being a Square
93
 *    that's the beauty of polymorphism but the code wants to copy
94
 *    the Square which is declared as a Shape, its super type...
95
 *
96
 * <pre>
97
 * Shape s2 = new Shape(s1); //using the copy constructor
98
 * System.out.println(s2 instanceof Square); //false
99
 * </pre>
100
 * The working solution (without knowing about all subclasses and doing many
101
 * casts) is to do the following (assuming correct clone implementation).
102
 *
103
 * <pre>
104
 * Shape s2 = s1.clone();
105
 * System.out.println(s2 instanceof Square); //true
106
 * </pre>
107
 * Just keep in mind if this type of polymorphic cloning is required
108
 * then a properly implemented clone method may be the best choice.
109
 *
110
 * <p>Much of this information was taken from Effective Java:
111
 * Programming Language Guide First Edition by Joshua Bloch
112
 * pages 45-52.  Give Bloch credit for writing an excellent book.
113
 * </p>
114
 *
115
 * <p>This check is almost exactly the same as the {@link NoFinalizerCheck}
116
 *
117
 * @author Travis Schneeberger
118
 * @see Object#clone()
119
 */
120
@StatelessCheck
121
public class NoCloneCheck extends AbstractCheck {
122
123
    /**
124
     * A key is pointing to the warning message text in "messages.properties"
125
     * file.
126
     */
127
    public static final String MSG_KEY = "avoid.clone.method";
128
129
    @Override
130
    public int[] getDefaultTokens() {
131 1 1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return getRequiredTokens();
132
    }
133
134
    @Override
135
    public int[] getAcceptableTokens() {
136 1 1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return getRequiredTokens();
137
    }
138
139
    @Override
140
    public int[] getRequiredTokens() {
141 1 1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return new int[] {TokenTypes.METHOD_DEF};
142
    }
143
144
    @Override
145
    public void visitToken(DetailAST aAST) {
146
        final DetailAST mid = aAST.findFirstToken(TokenTypes.IDENT);
147
        final String name = mid.getText();
148
149 1 1. visitToken : negated conditional → KILLED
        if ("clone".equals(name)) {
150
            final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS);
151
            final boolean hasEmptyParamList =
152 1 1. visitToken : negated conditional → KILLED
                params.findFirstToken(TokenTypes.PARAMETER_DEF) == null;
153
154 1 1. visitToken : negated conditional → KILLED
            if (hasEmptyParamList) {
155 1 1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::log → KILLED
                log(aAST.getLineNo(), MSG_KEY);
156
            }
157
        }
158
    }
159
160
}

Mutations

131

1.1
Location : getDefaultTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

136

1.1
Location : getAcceptableTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

141

1.1
Location : getRequiredTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

149

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testHasClone(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
negated conditional → KILLED

152

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testHasClone(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
negated conditional → KILLED

154

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testHasClone(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
negated conditional → KILLED

155

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest.testHasClone(com.puppycrawl.tools.checkstyle.checks.coding.NoCloneCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/NoCloneCheck::log → KILLED

Active mutators

Tests examined


Report generated by PIT 1.3.1