Repository: checkstyle/checkstyle

Problem statement:
ClassFanOutComplexity: false positive on var
Taken from https://github.com/checkstyle/checkstyle/issues/6316#issuecomment-451522961

````
$ cat TestClass.java
public class TestClass {
    void method() {
var x = 1;
    }
}

$ cat TestConfig.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
          "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>

    <module name="TreeWalker">
<module name="ClassFanOutComplexity">
  <property name="max" value="0"/>
</module>
    </module>
</module>

$ java -jar checkstyle-8.15-all.jar -c TestConfig.xml TestClass.java
Starting audit...
[ERROR] TestClass.java:1:1: Class Fan-Out Complexity is 1 (max allowed is 0). [ClassFanOutComplexity]
Audit done.
Checkstyle ends with 1 errors.
````

`var` should not be affecting ClassFanOutComplexity by default.
With `var` changed to `Object`, no violation is produced.

https://checkstyle.org/config_metrics.html#ClassFanOutComplexity should ignore var,int type should be counted ... as it is not a class..


Hints:
@rnveach 
This check should ignore `var` completely?
if not, issue requires clarification - if right side is, for example, a method invocation
```
var myVar = godKnowsWhatThisMethodReturns();
```
I guess we cannot infer type here.

The only case when we know type is a String/int/char/etc. literal on right side.

@romani Please review.

> This check should ignore var completely?

My initial response is yes.

Based on the JLS,
https://docs.oracle.com/javase/specs/jls/se10/html/jls-14.html#jls-14.4.1
>  If LocalVariableType is var, then let T be the type of the initializer expression when treated as if it did not appear in an assignment context, and were thus a standalone expression (§15.2). The type of the local variable is the upward projection of T with respect to all synthetic type variables mentioned by T (§4.10.5). 

I would say the document is saying `var` behaves the same as `T` as if it was a template for a method/class.
> `<T> void method(T param) {}`

So our check should act the same too, but I checked this out with the check, and it flags template classes too.
@romani Do you agree that template classes shouldn't be flagged too?

````
$ cat TestClass.java
public class TestClass {
    <T> void method(T param) {
    }
}

$ cat TestConfig.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>

    <module name="TreeWalker">
<module name="ClassFanOutComplexity">
  <property name="max" value="0"/>
</module>
    </module>
</module>

$ java -jar checkstyle-8.20-all.jar -c TestConfig.xml TestClass.java
Starting audit...
[ERROR] TestClass.java:1:1: Class Fan-Out Complexity is 1 (max allowed is 0). [ClassFanOutComplexity]
Audit done.
Checkstyle ends with 1 errors.
````
>I guess we cannot infer type here.

Checkstyle can not infer types. Checkstyle works with string values. "var" for us is the same as Object.

>Do you agree that template classes shouldn't be flagged too?

It is ok, to be counted, as it some type and it is distinct. So  amount of types is  amount of tepmplate names.

Var is different for us, as amount of vars, it not amount of distinct types.
So it is better to ignore it completely.
@romani ClassFanOutComplexity and ClassDataAbstractionCoupling have same superclass and same list of ignored classes.
var can be added to this list and it looks like it will solve this issue, but there is side effect on ClassDataAbstractionCouplingCheck, it will ignore cases like:
```
Object a = new var();
``` 

where var is a class name. It is not forbidden in java9 and lower. Looks weird and breaks java naming convention, but still possible.
In my opinion, there are two options - add note to ClassDataAbstractionCouplingCheck or change existing design to add var only to ClassFanOutComplexity default excluded classes.
if we add `var` to excluded classes, it will force it to appear in documentation. `var` is not a class for users. It is internal checkstyle's nuance that we treat it same as type. Ideally we should update grammar to keep it part of tokens.

both such Checks should ignore `var` (in definition/left side) to my mind. 

Patch:
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/AbstractClassCouplingCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/AbstractClassCouplingCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/AbstractClassCouplingCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/AbstractClassCouplingCheck.java
@@ -55,6 +55,8 @@ public abstract class AbstractClassCouplingCheck extends AbstractCheck {
     /** Class names to ignore. */
     private static final Set<String> DEFAULT_EXCLUDED_CLASSES = Collections.unmodifiableSet(
         Arrays.stream(new String[] {
+            // reserved type name
+            "var",
             // primitives
             "boolean", "byte", "char", "double", "float", "int",
             "long", "short", "void",
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassDataAbstractionCouplingCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassDataAbstractionCouplingCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassDataAbstractionCouplingCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassDataAbstractionCouplingCheck.java
@@ -94,7 +94,7 @@
  * Object, Override, Queue, RuntimeException, SafeVarargs, SecurityException, Set, Short,
  * SortedMap, SortedSet, String, StringBuffer, StringBuilder, SuppressWarnings, Throwable,
  * TreeMap, TreeSet, UnsupportedOperationException, Void, boolean, byte, char, double,
- * float, int, long, short, void}.
+ * float, int, long, short, var, void}.
  * </li>
  * <li>
  * Property {@code excludeClassesRegexps} - Specify user-configured regular
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheck.java
@@ -61,7 +61,7 @@
  * Object, Override, Queue, RuntimeException, SafeVarargs, SecurityException, Set, Short,
  * SortedMap, SortedSet, String, StringBuffer, StringBuilder, SuppressWarnings, Throwable,
  * TreeMap, TreeSet, UnsupportedOperationException, Void, boolean, byte, char, double,
- * float, int, long, short, void}.
+ * float, int, long, short, var, void}.
  * </li>
  * <li>
  * Property {@code excludeClassesRegexps} - Specify user-configured regular
@@ -96,6 +96,10 @@
  *   Date date = new Date(); // Counted, 1
  *   Time time = new Time(); // Counted, 2
  *   Place place = new Place(); // Counted, 3
+ *   int value = 10; // int is ignored due to default excludedClasses property
+ *   void method() {
+ *     var result = "result"; // var is ignored due to default excludedClasses property
+ *   }
  * }
  * </pre>
  * <p>


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/metrics/ClassFanOutComplexityCheckTest.java
@@ -302,6 +302,16 @@ public void testClassFanOutComplexityRecords() throws Exception {
             getNonCompilablePath("InputClassFanOutComplexityRecords.java"), expected);
     }
 
+    @Test
+    public void testClassFanOutComplexityIgnoreVar() throws Exception {
+        final DefaultConfiguration checkConfig =
+            createModuleConfig(ClassFanOutComplexityCheck.class);
+        checkConfig.addAttribute("max", "0");
+        final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
+        verify(checkConfig,
+            getNonCompilablePath("InputClassFanOutComplexityVar.java"), expected);
+    }
+
     /**
      * We cannot reproduce situation when visitToken is called and leaveToken is not.
      * So, we have to use reflection to be sure that even in such situation
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/metrics/classfanoutcomplexity/InputClassFanOutComplexityVar.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/metrics/classfanoutcomplexity/InputClassFanOutComplexityVar.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/metrics/classfanoutcomplexity/InputClassFanOutComplexityVar.java
@@ -0,0 +1,19 @@
+//non-compiled with javac: Compilable with Java10
+package com.puppycrawl.tools.checkstyle.checks.metrics.classfanoutcomplexity;
+
+import javax.naming.NamingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/* Config:
+ * max  = 0
+ */
+class InputClassFanOutComplexityVar { // ok
+    void method() {
+        var x = 1;
+    }
+}