Fix fields in html and lint

- Do not mark the class as fully supported if a field is partially
  supported
- Register missing fields
- Update html doc to document missing fields
- Update lint with an option to print field

Bug: b/267449090
Change-Id: I687047c60544a4653e683fb768b891ea097cd152
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
index 45aa53f..c62d6b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -507,6 +507,15 @@
             .append(" Also supported with covariant return type.")
             .append(HTML_SPLIT);
       }
+      if (!classAnnotation.getUnsupportedFields().isEmpty()) {
+        commentBuilder
+            .append("Some fields (")
+            .append(classAnnotation.getUnsupportedFields().size())
+            .append(") present in Android ")
+            .append(MAX_TESTED_ANDROID_API_LEVEL)
+            .append(" are not supported.")
+            .append(HTML_SPLIT);
+      }
       if (!classAnnotation.getUnsupportedMethods().isEmpty()) {
         commentBuilder
             .append("Some methods (")
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index 63dec3c..5bfbe46 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -51,6 +51,9 @@
 
 public class GenerateLintFiles extends AbstractGenerateFiles {
 
+  // Only recent versions of studio support the format with fields.
+  private static final boolean FORMAT_WITH_FIELD = true;
+
   public static GenerateLintFiles createForTesting(
       Path specification, Set<Path> implementation, Path outputDirectory) throws Exception {
     return new GenerateLintFiles(specification, implementation, outputDirectory);
@@ -181,6 +184,15 @@
                             + method.getReference().proto.toDescriptorString());
                   }
                 });
+            if (FORMAT_WITH_FIELD) {
+              supportedClass.forEachFieldAndAnnotation(
+                  (field, fieldAnnotation) -> {
+                    if (fieldAnnotation == null || !fieldAnnotation.unsupportedInMinApiRange) {
+                      desugaredApisSignatures.add(
+                          classBinaryName + '#' + field.getReference().name);
+                    }
+                  });
+            }
           } else {
             desugaredApisSignatures.add(classBinaryName);
           }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
index 8d01c4b..307def9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.TriConsumer;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 import java.util.Collection;
@@ -121,8 +122,10 @@
         this.clazz = clazz;
       }
 
-      void forEachMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) {
-        biConsumer.accept(clazz, supportedMethods.values());
+      public void forEachFieldsAndMethods(
+          TriConsumer<DexClass, Collection<DexEncodedField>, Collection<DexEncodedMethod>>
+              triConsumer) {
+        triConsumer.accept(clazz, supportedFields.values(), supportedMethods.values());
       }
 
       void forEachMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
@@ -166,10 +169,6 @@
         fieldAnnotations.put(field, annotation.combine(prev));
       }
 
-      MethodAnnotation getMethodAnnotation(DexMethod method) {
-        return methodAnnotations.get(method);
-      }
-
       SupportedClass build() {
         return new SupportedClass(
             clazz,
@@ -196,10 +195,12 @@
       return builder.classAnnotation;
     }
 
-    void forEachClassAndMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) {
+    void forEachClassFieldsAndMethods(
+        TriConsumer<DexClass, Collection<DexEncodedField>, Collection<DexEncodedMethod>>
+            triConsumer) {
       supportedClassBuilders
           .values()
-          .forEach(classBuilder -> classBuilder.forEachMethods(biConsumer));
+          .forEach(classBuilder -> classBuilder.forEachFieldsAndMethods(triConsumer));
     }
 
     void forEachClassAndMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
@@ -254,10 +255,16 @@
       annotateMethod(method, annotation);
     }
 
-    MethodAnnotation getMethodAnnotation(DexMethod method) {
-      SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+    public Map<DexField, FieldAnnotation> getFieldAnnotations(DexClass clazz) {
+      SupportedClass.Builder classBuilder = supportedClassBuilders.get(clazz.getType());
       assert classBuilder != null;
-      return classBuilder.getMethodAnnotation(method);
+      return classBuilder.fieldAnnotations;
+    }
+
+    Map<DexMethod, MethodAnnotation> getMethodAnnotations(DexClass clazz) {
+      SupportedClass.Builder classBuilder = supportedClassBuilders.get(clazz.getType());
+      assert classBuilder != null;
+      return classBuilder.methodAnnotations;
     }
 
     SupportedClasses build() {
@@ -274,12 +281,18 @@
 
     private final boolean additionalMembersOnClass;
     private final boolean fullySupported;
-    // Methods in latest android.jar but unsupported.
+    // Members in latest android.jar but unsupported.
+    private final List<DexField> unsupportedFields;
     private final List<DexMethod> unsupportedMethods;
 
-    public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) {
+    public ClassAnnotation(
+        boolean fullySupported,
+        List<DexField> unsupportedFields,
+        List<DexMethod> unsupportedMethods) {
       this.additionalMembersOnClass = false;
       this.fullySupported = fullySupported;
+      unsupportedFields.sort(Comparator.naturalOrder());
+      this.unsupportedFields = unsupportedFields;
       unsupportedMethods.sort(Comparator.naturalOrder());
       this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods);
     }
@@ -287,6 +300,7 @@
     private ClassAnnotation() {
       this.additionalMembersOnClass = true;
       this.fullySupported = false;
+      this.unsupportedFields = ImmutableList.of();
       this.unsupportedMethods = ImmutableList.of();
     }
 
@@ -304,6 +318,10 @@
       return fullySupported;
     }
 
+    public List<DexField> getUnsupportedFields() {
+      return unsupportedFields;
+    }
+
     public List<DexMethod> getUnsupportedMethods() {
       return unsupportedMethods;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
index f286bae..dec96bf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -16,8 +16,11 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -82,36 +85,45 @@
 
   private void annotateClasses(
       SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
-    builder.forEachClassAndMethods(
-        (clazz, methods) -> {
+    builder.forEachClassFieldsAndMethods(
+        (clazz, fields, methods) -> {
           ClassAnnotation classAnnotation = builder.getClassAnnotation(clazz.type);
           if (classAnnotation != null && classAnnotation.isAdditionalMembersOnClass()) {
             return;
           }
           DexClass maxClass = appForMax.definitionFor(clazz.type);
-          List<DexMethod> missing = new ArrayList<>();
+          List<DexField> missingFields = new ArrayList<>();
+          List<DexMethod> missingMethods = new ArrayList<>();
           boolean fullySupported = true;
-          for (DexEncodedMethod method : maxClass.methods()) {
-            if (!(method.isPublic() || method.isProtectedMethod())) {
-              continue;
-            }
-            // If the method is in android.jar but not in the desugared library, or annotated, then
-            // the class is not marked as fully supported.
-            if (methods.stream().noneMatch(em -> em.getReference() == method.getReference())) {
-              missing.add(method.getReference());
-              fullySupported = false;
-            }
+          fullySupported &= analyzeMissingMembers(maxClass.fields(), fields, missingFields);
+          fullySupported &= analyzeMissingMembers(maxClass.methods(), methods, missingMethods);
+          fullySupported &= builder.getFieldAnnotations(clazz).isEmpty();
+          for (MethodAnnotation methodAnnotation : builder.getMethodAnnotations(clazz).values()) {
+            fullySupported &= !methodAnnotation.isCovariantReturnSupported();
           }
-          for (DexEncodedMethod method : clazz.methods()) {
-            MethodAnnotation methodAnnotation = builder.getMethodAnnotation(method.getReference());
-            if (methodAnnotation != null && !methodAnnotation.isCovariantReturnSupported()) {
-              fullySupported = false;
-            }
-          }
-          builder.annotateClass(clazz.type, new ClassAnnotation(fullySupported, missing));
+          builder.annotateClass(
+              clazz.type, new ClassAnnotation(fullySupported, missingFields, missingMethods));
         });
   }
 
+  private <EM extends DexEncodedMember<EM, M>, M extends DexMember<EM, M>>
+      boolean analyzeMissingMembers(
+          Iterable<EM> maxClassMembers, Collection<EM> referenceMembers, List<M> missingMembers) {
+    boolean fullySupported = true;
+    for (EM member : maxClassMembers) {
+      if (!(member.getAccessFlags().isPublic() || member.getAccessFlags().isProtected())) {
+        continue;
+      }
+      // If the field is in android.jar but not in the desugared library, then
+      // the class is not marked as fully supported.
+      if (referenceMembers.stream().noneMatch(em -> em.getReference() == member.getReference())) {
+        missingMembers.add(member.getReference());
+        fullySupported = false;
+      }
+    }
+    return fullySupported;
+  }
+
   private void annotatePartialDesugaringMembers(
       SupportedClasses.Builder builder, Path specification) throws IOException {
     for (int api = AndroidApiLevel.K.getLevel();