typetools / checker-framework

Pluggable type-checking for Java
http://checkerframework.org/
Other
1.01k stars 352 forks source link

`ClassCastException` in `AnnotatedTypeFactory.getAnnotatedType()` #4690

Open msridhar opened 3 years ago

msridhar commented 3 years ago

I don't have a self-contained repro, but the issue should be easy to understand. We are seeing the following crash when running the Object Construction Checker:

Exception: java.lang.ClassCastException: org.checkerframework.framework.type.AnnotatedTypeMirror$AnnotatedIntersectionType cannot be cast to org.checkerframework.framework.type.AnnotatedTypeMirror$AnnotatedDeclaredType; java.lang.ClassCastException: org.checkerframework.framework.type.AnnotatedTypeMirror$AnnotatedIntersectionType cannot be cast to org.checkerframework.framework.type.AnnotatedTypeMirror$AnnotatedDeclaredType
    at org.checkerframework.framework.type.AnnotatedTypeFactory.getAnnotatedType(AnnotatedTypeFactory.java:2915)
    at org.checkerframework.checker.objectconstruction.ObjectConstructionAnnotatedTypeFactory.getMustCallValue(ObjectConstructionAnnotatedTypeFactory.java:155)     
...

It happens at this line:

https://github.com/typetools/checker-framework/blob/061fb741c13cb202dda82ef17ad61ca522a4736b/framework/src/main/java/org/checkerframework/framework/type/AnnotatedTypeFactory.java#L2916

And the related code has an interface declaration like this:

public interface Writer extends Closeable, CellSink, ShipperListener {}

So it seems that when calling getAnnotatedType(TypeElement) the result may not always be an AnnotatedDeclaredType.

Sorry again for not having a small test input to reproduce; we can put one together if need be.

/cc @kelloggm @Nargeshdb

msridhar commented 3 years ago

@smillst do you need a full repro for this one? or is the info above enough to reproduce?

smillst commented 3 years ago

@msridhar I'm going to need a full repro. I can't reproduce this. It's fine if it requires the Object Construction Checker.

msridhar commented 3 years ago

Ok, managed to get a somewhat simplified example. Here is the code:

import java.io.*;

class Test {

  enum BloomType {
    NONE,
    ROW,
    ROWCOL,
    ROWPREFIX_FIXED_LENGTH
  }

  static class Bytes {
    static int toInt(byte[] bytes) {
      return 0;
    }

    static String toStringBinary(byte[] bytes) {
      return "str";
    }
  }

  static class StoreFileWriter {
    BloomType bloomType;
    byte[] bloomParam = null;

    static boolean isTraceEnabled() {
      return true;
    }

    static void trace(String s) {}

    StoreFileWriter(BloomType bloomType, Object path) throws IOException {
      if (isTraceEnabled()) {
        trace(
            "Bloom filter type for "
                + path
                + ": "
                + this.bloomType
                + ", param: "
                + (bloomType == BloomType.ROWPREFIX_FIXED_LENGTH
                    ? Bytes.toInt(bloomParam)
                    : Bytes.toStringBinary(bloomParam))
                + ", "
                + getClass().getSimpleName());
      }
      switch (bloomType) {
        case ROW:
          break;
        case ROWCOL:
          break;
        case ROWPREFIX_FIXED_LENGTH:
          break;
        default:
          throw new IOException(
              "Invalid Bloom filter type: " + bloomType + " (ROW or ROWCOL or ROWPREFIX expected)");
      }
    }
  }
}

To reproduce the crash, build the framework from #4687 (at git SHA d8ddc9645117345a9018c50c65cfbc5836739f8e) and then run ./checker/bin/javac -processor resourceleak Test.java. If we can find a way to repro without using the Resource Leak Checker that would be best, as it is still under review and I can't guarantee it will keep exposing this issue as the code changes.

With this example, getAnnotatedType() gets called with a Symbol$ClassSymbol whose type field is a Type$IntersectionClassType, leading to the exception.

smillst commented 3 years ago

Thanks I can reproduce this now. I think we just have to remove AnnotatedTypeFactory#getAnnotatedType(javax.lang.model.element.TypeElement). Since TypeElement can represent non-declared types contrary to TypeElement's documentation. I'll do that.