From a82ecf6b468600079a0601f51b4d7ff718ae2e79 Mon Sep 17 00:00:00 2001 From: Andrei Selkin Date: Fri, 18 Sep 2015 00:48:43 +0300 Subject: [PATCH] Issue #2175: Add support of lambdas which parameter types are omitted for HiddenFieldCheck --- .../checks/coding/HiddenFieldCheck.java | 26 +- .../checks/coding/HiddenFieldCheckTest.java | 28 +++ .../coding/InputHiddenFieldLambdas.java | 227 ++++++++++++++++++ 3 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/coding/InputHiddenFieldLambdas.java diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheck.java index f94191598..58a76831d 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheck.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheck.java @@ -174,6 +174,7 @@ public class HiddenFieldCheck TokenTypes.CLASS_DEF, TokenTypes.ENUM_DEF, TokenTypes.ENUM_CONSTANT_DEF, + TokenTypes.LAMBDA, }; } @@ -199,12 +200,35 @@ public class HiddenFieldCheck case TokenTypes.PARAMETER_DEF: processVariable(ast); break; - + case TokenTypes.LAMBDA: + processLambda(ast); + break; default: visitOtherTokens(ast, type); } } + /** + * Process a lambda token. + * Checks whether a lambda parameter shadows a field. + * Note, that when parameter of lambda expression is untyped, + * ANTLR parses the parameter as an identifier. + * @param ast the lambda token. + */ + private void processLambda(DetailAST ast) { + final DetailAST firstChild = ast.getFirstChild(); + if (firstChild.getType() == TokenTypes.IDENT) { + final String untypedLambdaParameterName = firstChild.getText(); + if (isStaticOrInstanceField(firstChild, untypedLambdaParameterName)) { + log(firstChild, MSG_KEY, untypedLambdaParameterName); + } + } + else { + // Type of lambda parameter is not omitted. + processVariable(ast); + } + } + /** * Called to process tokens other than {@link TokenTypes#VARIABLE_DEF} * and {@link TokenTypes#PARAMETER_DEF}. diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheckTest.java index 84091b8ef..5357539bb 100644 --- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheckTest.java +++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/HiddenFieldCheckTest.java @@ -21,6 +21,8 @@ package com.puppycrawl.tools.checkstyle.checks.coding; import static com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck.MSG_KEY; +import java.io.File; + import org.junit.Assert; import org.junit.Test; @@ -30,6 +32,32 @@ import com.puppycrawl.tools.checkstyle.DefaultConfiguration; public class HiddenFieldCheckTest extends BaseCheckTestSupport { + @Test + public void testStaticVisibilityFromLambdas() throws Exception { + final DefaultConfiguration checkConfig = + createCheckConfig(HiddenFieldCheck.class); + final String[] expected = { + "16:34: " + getCheckMessage(MSG_KEY, "value"), + "48:31: " + getCheckMessage(MSG_KEY, "languageCode"), + "57:35: " + getCheckMessage(MSG_KEY, "number"), + "70:35: " + getCheckMessage(MSG_KEY, "id"), + "98:33: " + getCheckMessage(MSG_KEY, "note"), + "123:57: " + getCheckMessage(MSG_KEY, "stringValue"), + "123:78: " + getCheckMessage(MSG_KEY, "intValue"), + "134:74: " + getCheckMessage(MSG_KEY, "doubleValue"), + "146:51: " + getCheckMessage(MSG_KEY, "firstString"), + "146:64: " + getCheckMessage(MSG_KEY, "secondString"), + "162:49: " + getCheckMessage(MSG_KEY, "first"), + "188:62: " + getCheckMessage(MSG_KEY, "mPi"), + "204:27: " + getCheckMessage(MSG_KEY, "justSomeList"), + "204:61: " + getCheckMessage(MSG_KEY, "justSomeMap"), + "216:55: " + getCheckMessage(MSG_KEY, "someObject"), + "224:52: " + getCheckMessage(MSG_KEY, "someObject"), + }; + verify(checkConfig, new File("src/test/resources-noncompilable/com/puppycrawl/tools/" + + "checkstyle/coding/InputHiddenFieldLambdas.java").getCanonicalPath(), expected); + } + @Test public void testStaticVisibilityFromAnonymousClasses() throws Exception { final DefaultConfiguration checkConfig = diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/coding/InputHiddenFieldLambdas.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/coding/InputHiddenFieldLambdas.java new file mode 100644 index 000000000..f89c858d6 --- /dev/null +++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/coding/InputHiddenFieldLambdas.java @@ -0,0 +1,227 @@ +package com.puppycrawl.tools.checkstyle.checks.coding; + +import java.lang.Integer; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class InputHiddenFieldLambdas { + /** + * Example 1: lambda parameter 'value' on line 16 + * hides a field 'value' on line 14. + */ + List numbers = Arrays.asList(1, 2, 3, 4, 5, 6); + Integer value = new Integer(1); + { + numbers.forEach((Integer value) -> System.out.println(value)); // 1 violation + } + + /** + * Example 2: lambda parameter 'name' on line 27 + * does not hide a field 'name' on line 25, because + * field 'name' can not be referenced from a static context. + */ + static List firstNames = Arrays.asList("Andrei", "Michal", "Roman", "Vladislav"); + String name = new String(); + static { + firstNames.forEach((String name) -> System.out.println(name)); + } + + /** + * Example 3: lambda parameter 'brand' on line 38 (which type is omitted) + * does not hide a field 'brand' on line 36, because + * field 'brand' can not be referenced from a static context. + */ + static List carBrands = Arrays.asList("BMW", "Mazda", "Volkswagen"); + String brand = new String(); + static { + carBrands.forEach(brand -> System.out.println(brand)); + } + + /** + * Example 4: lambda parameter 'languageCode' on line 48 + * hides a field 'languageCode' on line 46. + */ + static List languageCodes = Arrays.asList("de", "ja", "fr", "pt"); + static String languageCode = new String(); + { + languageCodes.forEach(languageCode -> System.out.println(languageCode)); // 1 violation + } + + /** + * Example 5: lambda parameter 'number' on line 57 + * hides a field 'number' on line 55. + */ + int number = 1; + Optional foo1(int i) { + return Optional.of(5).map(number -> { // violation + if (number == 1) return true; + else if (number == 2) return true; + else return false; + }); + } + + /** + * Example 6: lambda parameter 'id' on line 70 + * hides a field 'id' on line 68. + */ + static long id = 1; + Optional foo2(int i) { + return Optional.of(5).map(id -> { // violation + if (id == 1) return true; + else if (id == 2) return true; + else return false; + }); + } + + /** + * Example 7: lambda parameter 'age' on line 84 + * does not hide a field 'age' on line 82, + * because field 'age' can not be referenced from a static context. + */ + int age = 21; + static Optional foo3(int i) { + return Optional.of(5).map(age -> { + if (age == 1) return true; + else if (age == 2) return true; + else return false; + }); + } + + /** + * Example 8: lambda parameter 'note' on line 98 + * hides a field 'note' on line 95. + */ + static String note = new String(); + private void foo4() { + List acceptableNotes = Arrays.asList("C", "D", "E", "F", "G", "A", "B"); + acceptableNotes.forEach(note -> System.out.println(note)); // 1 violation + } + + /** + * Example 9: lambda parameter 'letter' on line 109 + * does not hide a field 'letter' on line 106, because + * field 'letter' can not be referenced from a static context. + */ + String letter = new String("a"); + private static void foo5() { + List acceptableAlphabet = Arrays.asList("a", "b", "c"); + acceptableAlphabet.forEach(letter -> System.out.println(letter)); + } + + @FunctionalInterface + interface Function { + public B apply (A a, B b); + } + + /** + * Example 10: typed parameters - two hide fields + */ + String stringValue = "248.3"; + int intValue = 2; + { + Function multiAdder = (String stringValue, Integer intValue) -> { // 2 violations + return Integer.parseInt(stringValue) + intValue; + }; + System.out.println(multiAdder.apply ("22.4", 2)); + } + + /** + * Example 11: typed parameters - one hide field + */ + Double doubleValue = 8.5; + { + Function adder = (Integer integerValue, Double doubleValue) -> { // 1 violation + return integerValue + doubleValue; + }; + System.out.println(adder.apply(2, 2.2)); + } + + /** + * Example 11: untyped parameters - two hide fields + */ + String firstString = "Hello,"; + String secondString = " World!"; + { + Function stringConcat = (firstString, secondString) -> { // 2 violations + return firstString + secondString; + }; + System.out.println(stringConcat.apply("A", "B")); + } + + @FunctionalInterface + interface SomeFunction { + public Two apply(One one, Two two); + } + + /** + * Example 11: untyped parameters - one hide field + */ + Integer first = 1; + { + Function turnToZ = (first, second) -> 'z'; // 1 violation + } + + @FunctionalInterface + public interface Foo { + public String apply(); + } + + /** + * Example 12: case when no parameters are given + */ + { + Foo foo = () -> ""; + } + @FunctionalInterface + interface FunctionWithOneParameter { + public One apply(One one); + } + + /** + * Example 13: internal lambda hides a field + */ + Double mPi = Math.PI; + List simpleNumbers = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); + { + simpleNumbers.forEach(digit -> { + FunctionWithOneParameter strangeAdder = (mPi -> mPi+= digit); // 1 violation + }); + } + + @FunctionalInterface + interface FunctionWithComplexGenerics { + public Two foo(One one, Two two); + } + + /** + * Example 14: lambda typed with complex generics + */ + List justSomeList; + Map justSomeMap; + { + FunctionWithComplexGenerics, Map> someWierdFunc = + (List justSomeList, Map justSomeMap) -> { // 2 violations + System.out.println(justSomeList); + System.out.println(justSomeMap); + return new HashMap<>(); + }; + } + + /** + * Example 15: lambda stored in field (with typed parameter) + * hides other field + */ + Object someObject = new Object(); + FunctionWithOneParameter objectToString = (Object someObject) -> { // 1 violation + return someObject.toString(); + }; + + /** + * Example 16: lambda stored in field (with untyped parameter) + * hides other field + */ + FunctionWithOneParameter otherObjectToString = someObject -> { // 1 violation + return someObject.toString(); + }; +}