checker-framework icon indicating copy to clipboard operation
checker-framework copied to clipboard

RLC - [[checkOwningField() did not find a reason!]]

Open iamsanjaymalakar opened this issue 10 months ago • 0 comments

While running CF RLC with inference on the NJR benchmark, I encountered several leaks where CF reported:

[[checkOwningField() did not find a reason!]]

One specific example from the NJR benchmark project url1f1de5fc71_cooijmanstim_hobo_tgz-pJ8-hobo_TestingEnviromentJ8 produced the following leak warning:

src/hobo/TestingEnviroment.java:10: warning: (required.method.not.called) $$ 4 $$ method writeFile $$ field fileHandler $$ hobo.FileHandler $$ [[checkOwningField() did not find a reason!]] $$ ( 163, 187 ) $$ @MustCall method writeFile may not have been invoked on field fileHandler or any of its aliases.
	FileHandler fileHandler;
	            ^
  The type of object is: hobo.FileHandler.
  Reason for going out of scope: [[checkOwningField() did not find a reason!]]

Initially, I suspected this issue might be related to inconsistencies within the inference algorithm. Upon deeper analysis, I identified two clear patterns in the affected cases:

Pattern 1: Inheritance with Owning Fields

When a class inherits @MustCall obligations from its superclass and introduces an additional owning field, inference does not properly generate new inheritable @MustCall annotations for the subclass's owning field.

Example:

import java.io.*;
import org.checkerframework.checker.calledmethods.qual.*;
import org.checkerframework.checker.mustcall.qual.*;

public class NewTest extends InputStream {
  @Owning private RandomAccessFile stream;

  public NewTest(String fileName) throws FileNotFoundException {
    stream = new RandomAccessFile(new File(fileName), "r");
  }

  @EnsuresCalledMethods(
      value = {"this.stream"},
      methods = {"close"})
  public void releaseStream() throws IOException {
    stream.close();
  }

  @Override
  public int read() throws IOException {
    return stream.read();
  }
}

Pattern 2: Resources Closed Inside Constructors

Another problematic case occurs when the owning resource's required finalizer method is invoked directly within the constructor:

Example:

@InheritableMustCall({<init>})
public class TestingEnviroment {

        @Owning FileHandler fileHandler;
	
        @EnsuresCalledMethods(value = { "this.fileHandler" }, methods = { "writeFile" })
	public TestingEnviroment() {
		fileHandler = new FileHandler("src/fileOutput/test.txt");
		fileHandler.writeFile(winCounter[0]+" : "+winCounter[1]);
	}
	
	public static void main(String[] args) {
		new TestingEnviroment();
	}
}

In this scenario, the inference algorithm assigns an @InheritableMustCall({<init>}) annotation, despite the resource already having its required method (writeFile) invoked within the constructor. I think this inference is wrong in this case where the resource is released directly in the constructor.

iamsanjaymalakar avatar Mar 12 '25 07:03 iamsanjaymalakar