├── script.save ├── script.save.1 ├── object-construction-checker ├── tests │ ├── socket │ │ ├── GradleReport1.java │ │ ├── PaperExample.java │ │ ├── MCANotOwningField.java │ │ ├── UnconnectedSocketAlias.java │ │ ├── SocketNullOverwrite.java │ │ ├── MCAWithThis.java │ │ ├── ZookeeperReport6.java │ │ ├── SimpleSocketExample.java │ │ ├── COInSubtype.java │ │ ├── IsClosed.java │ │ ├── MCAOwningField.java │ │ ├── SocketContainer2.java │ │ ├── OptionalSocket.java │ │ ├── SocketContainer3.java │ │ ├── HBaseReport1.java │ │ ├── TryWithResourcesSimple.java │ │ ├── EnhancedFor.java │ │ ├── MustCallAliasSocketException.java │ │ ├── SocketContainer.java │ │ ├── HdfsReport3.java │ │ ├── GetChannelOnLocks.java │ │ ├── ConnectingServerSockets.java │ │ ├── ReassignmentWithMCA.java │ │ ├── BindChannel.java │ │ └── ConnectingSockets.java │ ├── basic │ │ ├── Parens.java │ │ ├── SimpleInference.java │ │ ├── SimpleInferenceMerge.java │ │ ├── Issue20.java │ │ ├── Xor.java │ │ ├── Generics.java │ │ ├── Not.java │ │ ├── Postconditions.java │ │ ├── UnparseablePredicate.java │ │ └── SimpleFluentInference.java │ ├── mustcall │ │ ├── CommonModuleCrash.java │ │ ├── WrapperStream.java │ │ ├── MustCloseIntoObject.java │ │ ├── NIOFile.java │ │ ├── MustCallNullStore.java │ │ ├── MustCallAliasPassthrough.java │ │ ├── TwoConstructorsCloseable.java │ │ ├── InheritanceStream.java │ │ ├── MustCallAliasPassthroughThis.java │ │ ├── Enclosing.java │ │ ├── MustCallAliasPassthroughWrong1.java │ │ ├── StringFromObject.java │ │ ├── WrapperStreamPoly.java │ │ ├── MustCallAliasImplWrong2.java │ │ ├── MustCallAliasImpl.java │ │ ├── CreatesObligationInnerClass.java │ │ ├── MustCallAliasImplWrong1.java │ │ ├── MustCallAliasPassthroughWrong4.java │ │ ├── ManualMustCallEmptyOnConstructor.java │ │ ├── ACExceptionalExitPointTest.java │ │ ├── MustCallAliasPassthroughLocal.java │ │ ├── MustCallAliasLayeredStreams.java │ │ ├── MustCallAliasOwningField.java │ │ ├── CreatesObligationSimpler.java │ │ ├── MustCallAliasPassthroughWrong2.java │ │ ├── MustCallAliasPassthroughWrong3.java │ │ ├── SelfAssign.java │ │ ├── CreatesObligationOverride.java │ │ ├── RequiresCalledMethodsTest.java │ │ ├── DoubleIf.java │ │ ├── MustCallAliasExamples.java │ │ ├── COAnonymousClass.java │ │ ├── ZookeeperByteBufferInputStream.java │ │ └── MustCallAliasPassthroughChain.java │ ├── autovalue │ │ ├── Parcel.java │ │ ├── UseAutoValueBuilder.java │ │ ├── Parcelable.java │ │ ├── NonAVBuilder.java │ │ ├── GuavaImmutablePropBuilder.java │ │ ├── NonBuildName.java │ │ ├── BuilderGetter.java │ │ ├── GuavaImmutable.java │ │ ├── CallWithinBuilder.java │ │ ├── IsPreserved.java │ │ ├── Validation.java │ │ ├── FooParcelable.java │ │ ├── SetInsideBuildWithCM.java │ │ ├── SetInsideBuild.java │ │ ├── Inheritance.java │ │ ├── GetAndIs.java │ │ ├── AnimalNoSet.java │ │ └── GetAnimal.java │ ├── disableframeworks │ │ ├── UseAutoValueBuilder.java │ │ └── README.md │ ├── guice │ │ ├── src │ │ │ └── main │ │ │ │ └── java │ │ │ │ ├── Main.java │ │ │ │ ├── OtherModule.java │ │ │ │ └── Module.java │ │ └── pom.xml │ ├── README.md │ ├── lombok │ │ ├── UseLombokBuilder.java │ │ ├── OldInherited.java │ │ ├── LombokDefaultAssignments.java │ │ └── DefaultedName.java │ ├── cve │ │ ├── RequestCreatedInCall.java │ │ ├── OnlyOwnersFalsePositive.java │ │ ├── SimpleFalsePositive.java │ │ ├── WithOwnersFilter.java │ │ ├── Cve2.java │ │ ├── SpecialNames.java │ │ └── MorePreciseFilters.java │ ├── noaccumulationframes │ │ ├── CreatesObligationSimpler.java │ │ ├── SocketContainer.java │ │ ├── ConnectingServerSockets.java │ │ └── ConnectingSockets.java │ ├── noresourcealias │ │ └── MustCallAliasPassthroughLocal.java │ └── nolightweightownership │ │ └── ACOwning.java ├── gradle.properties ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── javax.annotation.processing.Processor │ │ ├── java │ │ │ └── org │ │ │ │ └── checkerframework │ │ │ │ └── checker │ │ │ │ └── objectconstruction │ │ │ │ ├── Socket.astub │ │ │ │ ├── Stream.astub │ │ │ │ ├── NotOwning.astub │ │ │ │ ├── Reflection.astub │ │ │ │ ├── NoObligationGenerics.astub │ │ │ │ └── NoObligationStreams.astub │ │ └── main.iml │ └── test │ │ ├── test.iml │ │ └── java │ │ └── tests │ │ ├── EC2Test.java │ │ ├── MustCallTest.java │ │ ├── NoResourceAliasesTest.java │ │ ├── CountTest.java │ │ ├── MustCallOnlyJDKTest.java │ │ ├── NoAccumulationFramesTest.java │ │ ├── SocketTest.java │ │ ├── NoLightweightOwnershipTest.java │ │ ├── BasicTest.java │ │ └── LombokTest.java └── stubs │ └── GenerateDataKey.astub ├── test-lib-java ├── src │ └── main │ │ └── java │ │ └── testlib │ │ ├── lombok │ │ ├── lombok.config │ │ └── Foo.java │ │ └── autovalue │ │ └── AVTest.java └── build.gradle ├── fse-2021 ├── run.sh ├── REQUIREMENTS.md ├── STATUS.md ├── INSTALL.md └── LICENSE.md ├── must-call-checker ├── gradle.properties ├── tests │ ├── mustcall │ │ ├── StreamBool.java │ │ ├── SimpleStreamExample.java │ │ ├── StringSort.java │ │ ├── FieldInitializationWithGeneric.java │ │ ├── SimpleException.java │ │ ├── EditLogInputStream.java │ │ ├── ToStringOnSocket.java │ │ ├── SystemInOut.java │ │ ├── BinaryInputArchive.java │ │ ├── NullableTransfer.java │ │ ├── MustCallAliasImpl.java │ │ ├── MapWrap.java │ │ ├── OwningParams.java │ │ ├── MustCallAliasImplNoOwning.java │ │ ├── FileDescriptors.java │ │ ├── InferTypeArgs.java │ │ ├── MyDataInputStream.java │ │ ├── CommandResponse.java │ │ ├── SocketBufferedReader.java │ │ ├── TryWithResourcesCrash.java │ │ ├── ListOfMustCall.java │ │ ├── BorrowOnReturn.java │ │ ├── PolyTests.java │ │ ├── ClassForNameInit.java │ │ ├── TryWithResourcesSimple.java │ │ └── Subtype0.java │ └── nolightweightownership │ │ ├── OwningParams.java │ │ └── BorrowOnReturn.java └── src │ ├── main │ └── java │ │ └── org │ │ └── checkerframework │ │ └── checker │ │ └── mustcall │ │ ├── NoObligationGenerics.astub │ │ ├── NoObligationStreams.astub │ │ ├── Reflection.astub │ │ ├── MustCallNoAccumulationFramesChecker.java │ │ ├── MustCallTypeValidator.java │ │ └── SocketAccumulationFrames.astub │ └── test │ └── java │ └── tests │ ├── MustCallTest.java │ └── NoLightweightOwnershipTest.java ├── must-call-qual ├── gradle.properties ├── src │ └── main │ │ └── java │ │ └── org │ │ └── checkerframework │ │ └── checker │ │ └── mustcall │ │ └── qual │ │ ├── PolyMustCall.java │ │ ├── InheritableMustCall.java │ │ ├── MustCallUnknown.java │ │ └── MustCall.java └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── object-construction-qual ├── gradle.properties ├── src │ └── main │ │ └── java │ │ └── org │ │ └── checkerframework │ │ └── checker │ │ └── objectconstruction │ │ └── qual │ │ ├── Owning.java │ │ ├── NotOwning.java │ │ ├── EnsuresCalledMethodsVarArgs.java │ │ └── RequiresCalledMethods.java └── build.gradle ├── experimental-machinery ├── ablation │ ├── run-always-call-on-plume-util.sh │ ├── run-always-call-on-hadoop.sh │ ├── run-always-call-on-zookeeper.sh │ ├── run-always-call-on-hbase.sh │ ├── warnings-without-custom-types.sh │ ├── errors-without-custom-types.sh │ ├── plume-util-ablation.sh │ ├── hbase-ablation.sh │ ├── hadoop-ablation.sh │ └── zookeeper-ablation.sh └── case-studies │ ├── resource-counts.sh │ ├── anno-counter.sh │ └── how-to-count-added-lines.txt ├── .gitignore ├── .github ├── workflows │ └── gradle.yml └── dependabot.yml ├── README-developers.md ├── settings.gradle ├── gradle.properties ├── RELEASING.md └── LICENSE /script.save: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /script.save.1: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/GradleReport1.java: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-lib-java/src/main/java/testlib/lombok/lombok.config: -------------------------------------------------------------------------------- 1 | lombok.addLombokGeneratedAnnotation = true -------------------------------------------------------------------------------- /fse-2021/run.sh: -------------------------------------------------------------------------------- 1 | docker rmi --force resource_leak 2 | docker build --no-cache -t resource_leak . 3 | docker run resource_leak 4 | -------------------------------------------------------------------------------- /must-call-checker/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Must Call Checker 2 | POM_ARTIFACT_ID=must-call-checker 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /must-call-qual/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Must Call Type Qualifiers 2 | POM_ARTIFACT_ID=must-call-qual 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kelloggm/object-construction-checker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /object-construction-checker/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Object Construction Checker 2 | POM_ARTIFACT_ID=object-construction-checker 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /object-construction-qual/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Object Construction Type Qualifiers 2 | POM_ARTIFACT_ID=object-construction-qual 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /object-construction-checker/src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | org.checkerframework.checker.objectconstruction.ObjectConstructionChecker -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/Socket.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/Socket.astub -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/Stream.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/Stream.astub -------------------------------------------------------------------------------- /experimental-machinery/ablation/run-always-call-on-plume-util.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd plume-util 4 | # do a clean build since we always want full output from tool 5 | ./gradlew clean compileJava 6 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Parens.java: -------------------------------------------------------------------------------- 1 | public class Parens { 2 | public synchronized void incrementPushed(long[] pushed, int operationType) { 3 | // ++(pushed[operationType]); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/NotOwning.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/NotOwning.astub -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/Reflection.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/Reflection.astub -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/NoObligationGenerics.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/NoObligationGenerics.astub -------------------------------------------------------------------------------- /object-construction-checker/src/main/java/org/checkerframework/checker/objectconstruction/NoObligationStreams.astub: -------------------------------------------------------------------------------- 1 | ../../../../../../../../must-call-checker/src/main/java/org/checkerframework/checker/mustcall/NoObligationStreams.astub -------------------------------------------------------------------------------- /test-lib-java/src/main/java/testlib/lombok/Foo.java: -------------------------------------------------------------------------------- 1 | package testlib.lombok; 2 | 3 | import lombok.Builder; 4 | import lombok.NonNull; 5 | 6 | @Builder 7 | public class Foo { 8 | @NonNull 9 | String requiredProperty; 10 | } 11 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/CommonModuleCrash.java: -------------------------------------------------------------------------------- 1 | import java.net.*; 2 | 3 | class CommonModuleCrash { 4 | Socket bar = new Socket(); 5 | static void baz(Socket s) { } 6 | static { 7 | baz(new Socket()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/StreamBool.java: -------------------------------------------------------------------------------- 1 | // A test case for a false positive in hfds. 2 | 3 | import java.io.InputStream; 4 | 5 | class StreamBool { 6 | InputStream stream; 7 | 8 | boolean isActive() { 9 | return stream != null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 15 11:12:32 PDT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/SimpleStreamExample.java: -------------------------------------------------------------------------------- 1 | // Based on a false positive in Zookeeper 2 | 3 | import java.util.*; 4 | 5 | class SimpleStreamExample { 6 | static void test(List s) { 7 | s.stream().filter(str -> str == null); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/Parcel.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | 3 | /** 4 | * stub to avoid bringing in Android dependence 5 | */ 6 | public final class Parcel { 7 | 8 | public String readString() { return ""; } 9 | 10 | public void writeString(String val) {} 11 | 12 | } 13 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/StringSort.java: -------------------------------------------------------------------------------- 1 | // Another false positive I found in Zookeeper. 2 | 3 | import java.util.*; 4 | 5 | class StringSort { 6 | public static void sort() { 7 | List myList = new ArrayList(); 8 | Collections.sort(myList); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /object-construction-checker/tests/disableframeworks/UseAutoValueBuilder.java: -------------------------------------------------------------------------------- 1 | import testlib.autovalue.AVTest; 2 | 3 | class UseAutoValueBuilder { 4 | 5 | void test() { 6 | AVTest v = AVTest.builder().build(); 7 | 8 | AVTest v2 = AVTest.builder().setName("name").build(); 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /experimental-machinery/case-studies/resource-counts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # A simple script for finding the output from the -AcountMustCall option to the checker. 4 | # The input should be a single file containing the output of running the checker before 5 | # any output is filtered. 6 | 7 | grep "obligation(s)" $1 8 | -------------------------------------------------------------------------------- /fse-2021/REQUIREMENTS.md: -------------------------------------------------------------------------------- 1 | To use the artifact via the docker image, all that's required is Docker. 2 | We tested the artifact using version 3.3.3 of Docker Desktop on Mac OS X. 3 | 4 | If you wish to install the artifact itself outside Docker (following the 5 | "extending our work" instructions in the INSTALLATION.md file), you will also 6 | need a Java 11 JDK. -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/WrapperStream.java: -------------------------------------------------------------------------------- 1 | // A simple test that must-call as a type annotation fixes the simplest version 2 | // of the wrapper stream problem. 3 | 4 | import java.io.*; 5 | 6 | class WrapperStream { 7 | void test(byte[] buf) { 8 | InputStream is = new ByteArrayInputStream(buf); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/FieldInitializationWithGeneric.java: -------------------------------------------------------------------------------- 1 | // based on a false positive I found in Zookeeper 2 | 3 | import java.util.*; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | class FieldInitializationWithGeneric { 7 | private Set activeObservers = Collections.newSetFromMap(new ConcurrentHashMap()); 8 | } 9 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/UseAutoValueBuilder.java: -------------------------------------------------------------------------------- 1 | import testlib.autovalue.AVTest; 2 | 3 | class UseAutoValueBuilder { 4 | 5 | void test() { 6 | // :: error: finalizer.invocation.invalid 7 | AVTest v = AVTest.builder().build(); 8 | 9 | AVTest v2 = AVTest.builder().setName("name").build(); 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/guice/src/main/java/Main.java: -------------------------------------------------------------------------------- 1 | import com.google.inject.Injector; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) { 6 | final Injector injector = com.google.inject.Guice.createInjector(new Module()); 7 | final Main guiceObject = injector.getInstance(Main.class); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /object-construction-checker/tests/disableframeworks/README.md: -------------------------------------------------------------------------------- 1 | These are test cases for when we disable the framework supports for AutoValue and Lombok, therefore 2 | the checker annotation won't be automatically inserted into the code. 3 | These two test cases are adapted from _autovalue/UseAutoValueBuilder.java_ and _lombok/LombokBuilderExample.java_ 4 | but removed all `// :: error:` comments. -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCloseIntoObject.java: -------------------------------------------------------------------------------- 1 | // Test that assigning a "must close" value into a class without a mustCall annotation 2 | // still results in an error. 3 | 4 | import java.net.Socket; 5 | 6 | class MustCloseIntoObject { 7 | void test() throws Exception { 8 | // :: error: required.method.not.called 9 | Object o = new Socket("", 0); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /object-construction-checker/tests/guice/src/main/java/OtherModule.java: -------------------------------------------------------------------------------- 1 | import com.google.inject.AbstractModule; 2 | import com.google.inject.Provides; 3 | 4 | public class OtherModule extends AbstractModule { 5 | 6 | @Override 7 | protected void configure() { 8 | // do nothing 9 | } 10 | 11 | @Provides 12 | public Float providesFloat() { 13 | return 1.0f; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-lib-java/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java-library" 3 | } 4 | 5 | dependencies { 6 | compileOnly 'org.projectlombok:lombok:1.18.20' 7 | annotationProcessor 'org.projectlombok:lombok:1.18.18' 8 | 9 | implementation 'com.google.auto.value:auto-value-annotations:1.6.5' 10 | annotationProcessor 'com.google.auto.value:auto-value:1.6.5' 11 | 12 | } 13 | 14 | sourceCompatibility = 1.8 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | # Ignore Gradle build output directory 5 | build 6 | 7 | # Ignore IntelliJ and emacs files 8 | .idea 9 | *~ 10 | 11 | # Ignore the out directory created when running tests 12 | out 13 | 14 | # Ignore all .DS_Store files. 15 | .DS_Store 16 | 17 | # I think this directory gets created when running builds from IntelliJ 18 | test-lib-java/src/main/generated/ 19 | -------------------------------------------------------------------------------- /object-construction-checker/tests/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the various test suites for the object construction checker. 2 | 3 | Each test suite is meant to reflect a particular application domain. The test suites are: 4 | * "basic": generic tests to make sure the typechecker is working as intended. These don't reflect an application domain. 5 | 6 | To run the guice example use `mvn compile` followed by `mvn exec:java -Dexec.mainClass="Main"`. -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/Parcelable.java: -------------------------------------------------------------------------------- 1 | package android.os; 2 | 3 | /** 4 | * stub to avoid bringing in Android dependence 5 | */ 6 | public interface Parcelable { 7 | public interface Creator { 8 | 9 | public T createFromParcel(Parcel source); 10 | 11 | public T[] newArray(int size); 12 | } 13 | 14 | public int describeContents(); 15 | 16 | public void writeToParcel(Parcel dest, int flags); 17 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/lombok/UseLombokBuilder.java: -------------------------------------------------------------------------------- 1 | // tests that using a lombok builder from an external jar incorrectly 2 | // still produces an error 3 | 4 | import testlib.lombok.Foo; 5 | 6 | class UseLombokBuilder { 7 | void test() { 8 | // :: error: finalizer.invocation.invalid 9 | Foo foo = Foo.builder().build(); 10 | 11 | Foo foo2 = Foo.builder().requiredProperty("foo!").build(); 12 | } 13 | } -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/SimpleException.java: -------------------------------------------------------------------------------- 1 | // A test that throwing and catching exceptions doesn't cause false positives. 2 | 3 | class SimpleException { 4 | void thrower() throws Exception { 5 | throw new RuntimeException("some exception"); 6 | } 7 | 8 | void test() { 9 | try { 10 | thrower(); 11 | } catch (Exception e) { 12 | e.printStackTrace(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/EditLogInputStream.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | import java.io.*; 3 | abstract class EditLogInputStream implements Closeable { 4 | public abstract boolean isLocalLog(); 5 | } 6 | interface JournalSet extends Closeable{ 7 | static final Comparator 8 | LOCAL_LOG_PREFERENCE_COMPARATOR = Comparator 9 | .comparing(EditLogInputStream::isLocalLog) 10 | .reversed(); 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | gradle: 7 | strategy: 8 | matrix: 9 | jdk: [8, 11] 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up JDK 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: ${{ matrix.jdk }} 18 | - name: Build with Gradle 19 | run: ./gradlew build 20 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/PaperExample.java: -------------------------------------------------------------------------------- 1 | import java.net.Socket; 2 | 3 | class PaperExample { 4 | void test(String myHost, int myPort) throws Exception { 5 | Socket s = null; 6 | try { 7 | s = new Socket(myHost, myPort); /* 1 */ 8 | } catch (Exception e) { } 9 | finally { 10 | if (s != null) { /* 2 */ 11 | s.close(); 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/run-always-call-on-hadoop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "x${JAVA8_HOME}" = "x" ]; then 4 | echo "Please set JAVA8_HOME to run the checker on hadoop. hadoop requires Java 8." 5 | exit 1 6 | else 7 | java_home_old="${JAVA_HOME}" 8 | export JAVA_HOME="${JAVA8_HOME}" 9 | fi 10 | 11 | cd hadoop 12 | mvn --projects hadoop-hdfs-project/hadoop-hdfs --also-make clean compile -DskipTests 13 | 14 | export JAVA_HOME="${java_home_old}" 15 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/run-always-call-on-zookeeper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "x${JAVA8_HOME}" = "x" ]; then 4 | echo "Please set JAVA8_HOME to run the checker on ZooKeeper. ZooKeeper requires Java 8." 5 | exit 1 6 | else 7 | java_home_old="${JAVA_HOME}" 8 | export JAVA_HOME="${JAVA8_HOME}" 9 | fi 10 | 11 | cd zookeeper || exit 1 12 | mvn -B --projects zookeeper-server --also-make clean install -DskipTests 13 | export JAVA_HOME="${java_home_old}" 14 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/NIOFile.java: -------------------------------------------------------------------------------- 1 | // Test case that certain APIs in java.nio.file don't actually need to be closed. 2 | 3 | import java.nio.file.WatchService; 4 | import java.nio.file.FileSystem; 5 | import java.nio.file.Path; 6 | import java.io.IOException; 7 | 8 | class NIOFile { 9 | void test(Path dirPath) throws IOException { 10 | FileSystem fs = dirPath.getFileSystem(); 11 | WatchService watchService = fs.newWatchService(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/MCANotOwningField.java: -------------------------------------------------------------------------------- 1 | // A test case that a MustCallAlias value with a non-owning field with a must-call obligation 2 | // does not lead to a false positive. 3 | 4 | import java.net.Socket; 5 | 6 | class MCANotOwningField { 7 | 8 | final Socket s; 9 | 10 | MCANotOwningField(Socket s) throws Exception { 11 | this.s = s; 12 | } 13 | 14 | void simple() throws Exception { 15 | s.getInputStream(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/UnconnectedSocketAlias.java: -------------------------------------------------------------------------------- 1 | // A test case for an interaction between CO and aliasing that could 2 | // lead to a soundness bug if handled wrong. 3 | 4 | import java.net.*; 5 | 6 | class UnconnectedSocketAlias { 7 | void test(SocketAddress sa) throws Exception { 8 | // :: error: required.method.not.called 9 | Socket s = new Socket(); 10 | Socket t = s; 11 | t.close(); 12 | s.connect(sa); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /object-construction-checker/src/main/main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/test.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/SocketNullOverwrite.java: -------------------------------------------------------------------------------- 1 | // Taken from ACSocketTest and put here to ease debugging. 2 | 3 | import java.net.Socket; 4 | import java.io.IOException; 5 | 6 | class SocketNullOverwrite { 7 | void replaceVarWithNull(String address, int port) { 8 | try { 9 | // :: error: required.method.not.called 10 | Socket s = new Socket(address, port); 11 | s = null; 12 | } catch (IOException e) { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README-developers.md: -------------------------------------------------------------------------------- 1 | To build a version of the Object Construction Checker, and install it locally: 2 | 3 | ``` 4 | git clone https://github.com/kelloggm/object-construction-checker.git 5 | (cd object-construction-checker && ./gradlew install) 6 | ``` 7 | 8 | To make Gradle use it, add to your `build.gradle` file: 9 | 10 | ``` 11 | repositories { 12 | mavenLocal() 13 | } 14 | ``` 15 | 16 | Then, follow the instructions in the other READMEs, using version `0.1.14-SNAPSHOT` of the Object Construction Checker artifacts. 17 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/NonAVBuilder.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | @AutoValue 6 | abstract class NonAVBuilder { 7 | abstract String name(); 8 | 9 | public Builder toBuilder() { 10 | return new Builder(this); 11 | } 12 | 13 | // NOT an AutoValue builder 14 | static final class Builder { 15 | 16 | Builder(NonAVBuilder b) {} 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/MCAWithThis.java: -------------------------------------------------------------------------------- 1 | // A test that when a must-call class with MustCallAlias methods 2 | // is extended, those methods can be used without false positives. 3 | 4 | import java.net.Socket; 5 | 6 | class MCAWithThis extends Socket { 7 | public MCAWithThis() { 8 | super(); 9 | } 10 | 11 | public void test() throws Exception { 12 | this.getInputStream(); 13 | } 14 | 15 | public void test2() throws Exception { 16 | getInputStream(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /must-call-checker/tests/nolightweightownership/OwningParams.java: -------------------------------------------------------------------------------- 1 | // Tests that parameters (including receiver parameters) marked as @Owning are still checked. 2 | // Modified for -AnoLightweightOwnership to do the opposite lol 3 | 4 | import org.checkerframework.checker.objectconstruction.qual.Owning; 5 | import org.checkerframework.checker.mustcall.qual.MustCall; 6 | 7 | class OwningParams { 8 | static void o1(@Owning OwningParams o) { } 9 | 10 | void test(@Owning @MustCall({"a"}) OwningParams o) { 11 | o1(o); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/ToStringOnSocket.java: -------------------------------------------------------------------------------- 1 | // A test for a false positive I found in Zookeeper. Sockets are must-close, but the 2 | // result of calling toString on them shouldn't be! 3 | 4 | import java.net.Socket; 5 | 6 | class ToStringOnSocket { 7 | void log(String string) { 8 | System.out.println(string); 9 | } 10 | 11 | void test(Socket socket) { 12 | log("bad socket: " + socket); 13 | } 14 | 15 | void test2(Socket socket) { 16 | log("bad socket: " + socket.toString()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test-lib-java/src/main/java/testlib/autovalue/AVTest.java: -------------------------------------------------------------------------------- 1 | package testlib.autovalue; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | @AutoValue 6 | public abstract class AVTest { 7 | 8 | public abstract String name(); 9 | 10 | public static Builder builder() { 11 | return new AutoValue_AVTest.Builder(); 12 | } 13 | 14 | @AutoValue.Builder 15 | public abstract static class Builder { 16 | 17 | public abstract Builder setName(String value); 18 | 19 | public abstract AVTest build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/SystemInOut.java: -------------------------------------------------------------------------------- 1 | // A test that the checker doesn't ask you to close System.in, System.out, or System.err. 2 | 3 | import java.util.Scanner; 4 | import java.io.*; 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class SystemInOut { 9 | void test() { 10 | @MustCall({}) InputStream in = System.in; 11 | @MustCall({}) OutputStream out = System.out; 12 | @MustCall({}) OutputStream err = System.err; 13 | @MustCall({}) Scanner sysIn = new Scanner(System.in); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/NoObligationGenerics.astub: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.mustcall.qual.*; 2 | 3 | package java.lang.management; 4 | 5 | class ManagementFactory { 6 | // Declaring as 7 | // T 8 | // didn't work; I'm not sure why. 9 | public static @MustCall({}) T newPlatformMXBeanProxy(MBeanServerConnection connection, 10 | String mxbeanName, 11 | Class mxbeanInterface); 12 | } 13 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/ZookeeperReport6.java: -------------------------------------------------------------------------------- 1 | // Based on a Zookeeper false positive that requires unconnected socket support. 2 | 3 | import java.nio.channels.SocketChannel; 4 | import java.io.IOException; 5 | 6 | class ZookeeperReport6 { 7 | SocketChannel createSock() throws IOException { 8 | SocketChannel sock; 9 | sock = SocketChannel.open(); 10 | sock.configureBlocking(false); 11 | sock.socket().setSoLinger(false, -1); 12 | sock.socket().setTcpNoDelay(true); 13 | return sock; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallNullStore.java: -------------------------------------------------------------------------------- 1 | import java.nio.Buffer; 2 | import java.nio.ByteBuffer; 3 | 4 | // input exposing a bug where store after the return is null 5 | class MustCallNullStore { 6 | 7 | int inflateDirect(ByteBuffer src, ByteBuffer dst) throws java.io.IOException { 8 | 9 | ByteBuffer presliced = dst; 10 | if (dst.position() > 0) { 11 | dst = dst.slice(); 12 | } 13 | 14 | int n = 0; 15 | try { 16 | presliced.position(presliced.position() + n); 17 | } finally { 18 | } 19 | return n; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/run-always-call-on-hbase.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "x${JAVA8_HOME}" = "x" ]; then 4 | echo "Please set JAVA8_HOME to run the checker on hbase. hbase requires Java 8." 5 | exit 1 6 | else 7 | java_home_old="${JAVA_HOME}" 8 | export JAVA_HOME="${JAVA8_HOME}" 9 | fi 10 | 11 | cd hbase 12 | # the install task must be run (as opposed to compile), as hbase 13 | # requires that sub-project jars be installed in the local maven repo 14 | mvn --projects hbase-server --also-make clean install -DskipTests 15 | 16 | export JAVA_HOME="${java_home_old}" 17 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthrough.java: -------------------------------------------------------------------------------- 1 | // A test that a class can extend another class with an MCA constructor, 2 | // and have its own constructor be MCA as well. 3 | 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import org.checkerframework.checker.objectconstruction.qual.*; 7 | import java.io.*; 8 | 9 | class MustCallAliasPassthrough extends FilterInputStream { 10 | @MustCallAlias MustCallAliasPassthrough(@MustCallAlias InputStream is) { 11 | super(is); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/BinaryInputArchive.java: -------------------------------------------------------------------------------- 1 | // Test case based on a false positive reported by Narges that was 2 | // caused by not respecting ownership transfer rules for constructor params. 3 | 4 | import java.io.*; 5 | class BinaryInputArchive{ 6 | 7 | private DataInput in; 8 | 9 | public BinaryInputArchive(DataInput in) { 10 | this.in = in; 11 | } 12 | 13 | public static BinaryInputArchive getArchive(InputStream strm) { 14 | return new BinaryInputArchive( 15 | new DataInputStream( 16 | strm)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/TwoConstructorsCloseable.java: -------------------------------------------------------------------------------- 1 | // A test case for false positives that I encountered in Zookeeper. 2 | 3 | import java.io.Closeable; 4 | 5 | public class TwoConstructorsCloseable implements Closeable { 6 | public TwoConstructorsCloseable(Object obj) { 7 | 8 | } 9 | 10 | public TwoConstructorsCloseable() { 11 | this(null); 12 | } 13 | 14 | public void close() { 15 | 16 | } 17 | 18 | class Derivative extends TwoConstructorsCloseable { 19 | Derivative() { 20 | super(null); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/5.0/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'object-construction-checker' 11 | 12 | include ':must-call-checker' 13 | include ':must-call-qual' 14 | include ':object-construction-checker' 15 | include ':object-construction-qual' 16 | include ':test-lib-java' 17 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/SimpleSocketExample.java: -------------------------------------------------------------------------------- 1 | // A simple socket example for debugging. 2 | 3 | import java.net.Socket; 4 | import java.io.IOException; 5 | 6 | class SimpleSocketExample { 7 | void basicTest(String address, int port){ 8 | try 9 | { 10 | // :: error: required.method.not.called 11 | Socket socket2 = new Socket(address, port); 12 | Socket specialSocket = new Socket(address, port); 13 | specialSocket.close(); 14 | } 15 | catch(IOException i) 16 | { 17 | 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /fse-2021/STATUS.md: -------------------------------------------------------------------------------- 1 | Artifacts evaluated - Reusable: This replication package allows other 2 | researchers to repeat our experiments and to edit the source code. The Resource 3 | Leak Checker has been incorporated into the [Checker 4 | Framework](https://checkerframework.org) as of version 3.15.0, and it will be 5 | maintained and enhanced going forward, enabling future researchers to build on 6 | our work. 7 | 8 | Artifacts evaluated - available: our tool, case studies, and scripts 9 | are all available open-source. We've archived our reproduction package 10 | [here](https://zenodo.org/record/4902322#.YLrTMC1h1TY). 11 | -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/NoObligationStreams.astub: -------------------------------------------------------------------------------- 1 | package java.io; 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | 5 | @InheritableMustCall({}) 6 | class ByteArrayInputStream { } 7 | 8 | @InheritableMustCall({}) 9 | class ByteArrayOutputStream { } 10 | 11 | @InheritableMustCall({}) 12 | class StringReader { } 13 | 14 | @InheritableMustCall({}) 15 | class StringWriter { } 16 | 17 | package java.lang; 18 | 19 | class System { 20 | @MustCall({}) InputSteam in; 21 | @MustCall({}) OutputStream out; 22 | @MustCall({}) OutputStream err; 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/InheritanceStream.java: -------------------------------------------------------------------------------- 1 | // A test that checks that an empty @MustCall annotation in a stub on a subclass overrides 2 | // a non-empty one on a superclass (that is being inherited). 3 | 4 | import java.io.*; 5 | 6 | class InheritanceStream { 7 | void testBAIS(byte[] buf) { 8 | new ByteArrayInputStream(buf); 9 | } 10 | 11 | void testBAOS() { 12 | new ByteArrayOutputStream(); 13 | } 14 | 15 | void testSR(String buf) { 16 | new StringReader(buf); 17 | } 18 | 19 | void testSW() { 20 | new StringWriter(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/SimpleInference.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | /* The simplest inference test case Martin could think of */ 4 | class SimpleInference { 5 | void build(@CalledMethods({"a"}) SimpleInference this) { } 6 | 7 | void a() { } 8 | 9 | static void doStuffCorrect() { 10 | SimpleInference s = new SimpleInference(); 11 | s.a(); 12 | s.build(); 13 | } 14 | 15 | static void doStuffWrong() { 16 | SimpleInference s = new SimpleInference(); 17 | // :: error: finalizer.invocation.invalid 18 | s.build(); 19 | } 20 | } -------------------------------------------------------------------------------- /object-construction-qual/src/main/java/org/checkerframework/checker/objectconstruction/qual/Owning.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.objectconstruction.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotation indicating ownership should be transferred to the parameter or field, for the purposes 10 | * of MustCall checking. 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 14 | public @interface Owning {} 15 | -------------------------------------------------------------------------------- /object-construction-qual/src/main/java/org/checkerframework/checker/objectconstruction/qual/NotOwning.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.objectconstruction.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotation indicating ownership should not be transferred to the parameter or field, for the 10 | * purposes of MustCall checking. 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) 14 | public @interface NotOwning {} 15 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughThis.java: -------------------------------------------------------------------------------- 1 | // A test that a class can have multiple MCA constructors. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | import java.io.*; 7 | 8 | class MustCallAliasPassthroughThis extends FilterInputStream { 9 | @MustCallAlias MustCallAliasPassthroughThis(@MustCallAlias InputStream is) { 10 | super(is); 11 | } 12 | 13 | @MustCallAlias MustCallAliasPassthroughThis(@MustCallAlias InputStream is, int x) { 14 | this(is); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/Enclosing.java: -------------------------------------------------------------------------------- 1 | // test case for https://github.com/kelloggm/object-construction-checker/issues/366 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.objectconstruction.qual.*; 5 | 6 | class Enclosing { 7 | @MustCall("a") 8 | static class Foo { 9 | void a() {} 10 | } 11 | static class Nested { 12 | // :: error: required.method.not.called 13 | @Owning Foo foo; 14 | @CreatesObligation("this") 15 | void initFoo() { 16 | if (this.foo == null) { 17 | this.foo = new Foo(); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/NullableTransfer.java: -------------------------------------------------------------------------------- 1 | // A test that the must-call type of an object tested against null 2 | // is always empty. 3 | 4 | import java.io.*; 5 | 6 | import org.checkerframework.checker.objectconstruction.qual.Owning; 7 | import org.checkerframework.checker.mustcall.qual.MustCall; 8 | 9 | class NullableTransfer { 10 | 11 | void test(@Owning InputStream is) { 12 | if (is == null) { 13 | @MustCall({}) InputStream is2 = is; 14 | } else { 15 | // :: error: assignment.type.incompatible 16 | @MustCall({}) InputStream is3 = is; 17 | @MustCall("close") InputStream is4 = is; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/MustCallAliasImpl.java: -------------------------------------------------------------------------------- 1 | // A simple test that the extra obligations that MustCallAlias imposes are 2 | // respected. 3 | 4 | // @skip-test until the checks are implemented 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | public class MustCallAliasImpl implements Closeable { 11 | 12 | @Owning final Closeable foo; 13 | 14 | public @MustCallAlias MustCallAliasImpl(@MustCallAlias Closeable foo) { 15 | this.foo = foo; 16 | } 17 | 18 | @Override 19 | public void close() throws IOException { 20 | this.foo.close(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /must-call-qual/src/main/java/org/checkerframework/checker/mustcall/qual/PolyMustCall.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall.qual; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import org.checkerframework.framework.qual.PolymorphicQualifier; 9 | 10 | /** The polymorphic qualifier for the MustCall type system. */ 11 | @Documented 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) 14 | @PolymorphicQualifier(MustCallUnknown.class) 15 | public @interface PolyMustCall {} 16 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | // enable parallel builds 2 | org.gradle.parallel=true 3 | 4 | GROUP=net.sridharan.objectconstruction 5 | VERSION_NAME=0.1.14-SNAPSHOT 6 | 7 | POM_DESCRIPTION=Object Construction Checker 8 | 9 | 10 | POM_URL=https://github.com/kelloggm/object-construction-checker 11 | POM_SCM_URL=https://github.com/kelloggm/object-construction-checker 12 | POM_SCM_CONNECTION=scm:git:git://github.com/kelloggm/object-construction-checker.git 13 | POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/kelloggm/object-construction-checker.git 14 | 15 | POM_LICENCE_NAME=MIT License 16 | POM_LICENCE_URL=https://opensource.org/licenses/MIT 17 | POM_LICENCE_DIST=repo 18 | 19 | POM_DEVELOPER_ID=msridhar 20 | POM_DEVELOPER_NAME=Manu Sridharan 21 | -------------------------------------------------------------------------------- /object-construction-checker/tests/guice/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | test 8 | test 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.google.inject 14 | guice 15 | 4.1.0 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /must-call-checker/src/test/java/tests/MustCallTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 6 | import org.junit.runners.Parameterized.Parameters; 7 | 8 | public class MustCallTest extends CheckerFrameworkPerDirectoryTest { 9 | public MustCallTest(List testFiles) { 10 | super( 11 | testFiles, 12 | org.checkerframework.checker.mustcall.MustCallChecker.class, 13 | "mustcall", 14 | "-Anomsgtext", 15 | // "-AstubDebug"); 16 | "-nowarn"); 17 | } 18 | 19 | @Parameters 20 | public static String[] getTestDirs() { 21 | return new String[] {"mustcall"}; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /must-call-qual/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | // Apply the java plugin to add support for Java 3 | id 'java-library' 4 | 5 | } 6 | 7 | sourceCompatibility = 1.8 8 | 9 | dependencies { 10 | // re-export these dependencies so clients don't need to explicitly import them 11 | api "org.checkerframework:checker-qual:${versions.checkerFramework}" 12 | } 13 | 14 | tasks.withType(JavaCompile).all { 15 | options.compilerArgs.add("-Xlint:all") 16 | } 17 | 18 | // run google java format 19 | spotless { 20 | // uncomment this line to temporarily disable spotless (i.e. when debugging) 21 | // enforceCheck = false 22 | java { 23 | googleJavaFormat() 24 | } 25 | } 26 | 27 | apply from: rootProject.file("gradle-mvn-push.gradle") 28 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughWrong1.java: -------------------------------------------------------------------------------- 1 | // A test that a class can extend another class with an MCA constructor, 2 | // and have its own constructor be MCA as well. 3 | // This version just throws away the input rather than passing it to the super constructor. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | class MustCallAliasPassthroughWrong1 extends FilterInputStream { 11 | // :: error: required.method.not.called 12 | @MustCallAlias MustCallAliasPassthroughWrong1(@MustCallAlias InputStream is) { 13 | super(null); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/COInSubtype.java: -------------------------------------------------------------------------------- 1 | // A test for a bad interaction between CO and subtyping 2 | // that could happen if CO was unsound. 3 | 4 | import org.checkerframework.checker.objectconstruction.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class COInSubtype { 9 | static class Foo { 10 | 11 | @CreatesObligation("this") 12 | void resetFoo() { } 13 | } 14 | 15 | @MustCall("a") 16 | static class Bar extends Foo { 17 | void a() { } 18 | } 19 | 20 | static void test() { 21 | // :: error: required.method.not.called 22 | @MustCall("a") Foo f = new Bar(); 23 | f.resetFoo(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/StringFromObject.java: -------------------------------------------------------------------------------- 1 | // Tests that converting to a String from an object doesn't create 2 | // phantom must call obligations. Taken from some code in Zookeeper that 3 | // caused a false positive. 4 | 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | 8 | class StringFromObject { 9 | boolean test(Map map) { 10 | boolean isHierarchical = false; 11 | for (Entry entry : map.entrySet()) { 12 | String key = entry.getKey().toString().trim(); 13 | if (key.startsWith("group") || key.startsWith("weight")) { 14 | isHierarchical = true; 15 | break; 16 | } 17 | } 18 | return isHierarchical; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/WrapperStreamPoly.java: -------------------------------------------------------------------------------- 1 | // A simple test that must-call as a type annotation makes it so that so-called 2 | // "polymorphic" streams like DataInputStream and DataOutputStream are treated as 3 | // their constituent stream. 4 | 5 | import java.io.*; 6 | import org.checkerframework.checker.objectconstruction.qual.Owning; 7 | 8 | 9 | class WrapperStreamPoly { 10 | void test_no_close_needed(@Owning ByteArrayInputStream b) { 11 | // b doesn't need to be closed, so neither does this stream. 12 | DataInputStream d = new DataInputStream(b); 13 | } 14 | 15 | // :: error: required.method.not.called 16 | void test_close_needed(@Owning InputStream b) { 17 | DataInputStream d = new DataInputStream(b); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/MapWrap.java: -------------------------------------------------------------------------------- 1 | // A test for a class that wraps a map. I found a similar example in Zookeeper that causes false 2 | // positives. 3 | 4 | import java.util.HashMap; 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class MapWrap { 9 | HashMap impl = new HashMap(); 10 | 11 | String remove(E e) { 12 | // remove should permit any object: its signature is remove(Object key), *not* remove(E key) 13 | String old = impl.remove(e); 14 | return old; 15 | } 16 | 17 | String remove2(@MustCall({}) E e) { 18 | // remove should permit any object: its signature is remove(Object key), *not* remove(E key) 19 | String old = impl.remove(e); 20 | return old; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/OwningParams.java: -------------------------------------------------------------------------------- 1 | // Tests that parameters (including receiver parameters) marked as @Owning are still checked. 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.Owning; 4 | import org.checkerframework.checker.mustcall.qual.MustCall; 5 | 6 | class OwningParams { 7 | static void o1(@Owning OwningParams o) { } 8 | 9 | void o2(@Owning OwningParams this) { } 10 | 11 | void test(@Owning @MustCall({"a"}) OwningParams o, @Owning OwningParams p) { 12 | // :: error: argument.type.incompatible 13 | o1(o); 14 | // TODO: this error doesn't show up! See MustCallVisitor#skipReceiverSubtypeCheck 15 | // error: method.invocation.invalid 16 | o.o2(); 17 | o1(p); 18 | p.o2(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/RequestCreatedInCall.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.model.Filter; 4 | import com.amazonaws.services.ec2.AmazonEC2; 5 | 6 | import java.util.*; 7 | 8 | // A test to ensure that requests that are created in the call to describeImages work correctly. 9 | class RequestCreatedInCall { 10 | void test(AmazonEC2 ec2) { 11 | List filters = new ArrayList<>(); 12 | filters.add(new Filter().withName("foo").withValues("bar")); 13 | DescribeImagesResult describeImagesResult = 14 | ec2.describeImages(new DescribeImagesRequest().withOwners("martin").withFilters(filters)); 15 | } 16 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/IsClosed.java: -------------------------------------------------------------------------------- 1 | // A test that the EnsuresCalledMethodsIf annotations in 2 | // the Socket stub files are respected. 3 | 4 | import org.checkerframework.checker.objectconstruction.qual.Owning; 5 | import java.net.*; 6 | import java.io.*; 7 | 8 | class IsClosed { 9 | void test_socket(@Owning Socket sock) { 10 | if (!sock.isClosed()) { 11 | try { 12 | sock.close(); 13 | } catch (IOException io) { 14 | 15 | } 16 | } 17 | } 18 | 19 | void test_server_socket(@Owning ServerSocket sock) { 20 | if (!sock.isClosed()) { 21 | try { 22 | sock.close(); 23 | } catch (IOException io) { 24 | 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /object-construction-qual/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | // Apply the java plugin to add support for Java 3 | id 'java-library' 4 | 5 | } 6 | 7 | sourceCompatibility = 1.8 8 | 9 | dependencies { 10 | // re-export these dependencies so clients don't need to explicitly import them 11 | api "org.checkerframework:checker-qual:${versions.checkerFramework}" 12 | api project(':must-call-qual') 13 | } 14 | 15 | tasks.withType(JavaCompile).all { 16 | options.compilerArgs.add("-Xlint:all") 17 | } 18 | 19 | // run google java format 20 | spotless { 21 | // uncomment this line to temporarily disable spotless (i.e. when debugging) 22 | // enforceCheck = false 23 | java { 24 | googleJavaFormat() 25 | } 26 | } 27 | 28 | apply from: rootProject.file("gradle-mvn-push.gradle") 29 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/OnlyOwnersFalsePositive.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.AmazonEC2; 4 | 5 | import java.util.Collections; 6 | 7 | // Tests that just setting with/setOwners is permitted, since there are legitimate reasons to do that. 8 | // Originally, we required with/setFilters && with/setOwners. 9 | class OnlyOwnersFalsePositive { 10 | void test(AmazonEC2 ec2Client) { 11 | DescribeImagesRequest describeImagesRequest = new DescribeImagesRequest(); 12 | describeImagesRequest.setOwners(Collections.singleton("self")); 13 | DescribeImagesResult describeImagesResult = ec2Client.describeImages(describeImagesRequest); 14 | } 15 | } -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/Reflection.astub: -------------------------------------------------------------------------------- 1 | // Contains assumptions about reflection. In particular, this 2 | // makes the results of reflection the default (i.e. bottom) type 3 | // in the Must Call type system. This choice is technically unsound: 4 | // if e.g. the java.net.Socket constructor is invoked via reflection, 5 | // then our checker will fail to track it. For usability, we found it 6 | // much more useful to ignore these warnings than to issue them. 7 | // If you wish to see warnings related to reflection, remove this stub file 8 | // when running the checker. 9 | 10 | package java.lang.reflect; 11 | 12 | import org.checkerframework.checker.mustcall.qual.MustCall; 13 | 14 | class Constructor { 15 | @MustCall({}) T newInstance(Object... initArgs); 16 | } 17 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/MustCallAliasImplNoOwning.java: -------------------------------------------------------------------------------- 1 | // A simple test that the extra obligations that MustCallAlias imposes are 2 | // respected. Identical to MustCallAliasImpl.java except the @Owning annotation 3 | // on foo has been removed, making this unverifiable. 4 | 5 | // @skip-test until the checks are implemented 6 | 7 | import org.checkerframework.checker.mustcall.qual.*; 8 | import java.io.*; 9 | 10 | public class MustCallAliasImplNoOwning implements Closeable { 11 | 12 | final Closeable foo; 13 | 14 | // :: error: mustcall.alias.invalid 15 | public @MustCallAlias MustCallAliasImplNoOwning(@MustCallAlias Closeable foo) { 16 | this.foo = foo; 17 | } 18 | 19 | @Override 20 | public void close() throws IOException { 21 | this.foo.close(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/EC2Test.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized; 8 | 9 | public class EC2Test extends CheckerFrameworkPerDirectoryTest { 10 | public EC2Test(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "cve", 15 | "-Anomsgtext", 16 | "-Astubs=stubs", 17 | "-AuseValueChecker", 18 | "-nowarn"); 19 | } 20 | 21 | @Parameterized.Parameters 22 | public static String[] getTestDirs() { 23 | return new String[] {"cve"}; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasImplWrong2.java: -------------------------------------------------------------------------------- 1 | // A simple test that the extra obligations that MustCallAlias imposes are 2 | // respected. This version gets it wrong by assigning the MCA param to a non-owning 3 | // field. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | public class MustCallAliasImplWrong2 implements Closeable { 11 | 12 | final /*@Owning*/ Closeable foo; 13 | 14 | // :: error: required.method.not.called 15 | public @MustCallAlias MustCallAliasImplWrong2(@MustCallAlias Closeable foo) { 16 | this.foo = foo; 17 | } 18 | 19 | @Override 20 | public void close() { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/MustCallTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | public class MustCallTest extends CheckerFrameworkPerDirectoryTest { 10 | public MustCallTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "mustcall", 15 | "-Anomsgtext", 16 | "-AcheckMustCall", 17 | "-AcountMustCall", 18 | "-nowarn"); 19 | } 20 | 21 | @Parameters 22 | public static String[] getTestDirs() { 23 | return new String[] {"mustcall"}; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /must-call-checker/src/test/java/tests/NoLightweightOwnershipTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 6 | import org.junit.runners.Parameterized.Parameters; 7 | 8 | public class NoLightweightOwnershipTest extends CheckerFrameworkPerDirectoryTest { 9 | public NoLightweightOwnershipTest(List testFiles) { 10 | super( 11 | testFiles, 12 | org.checkerframework.checker.mustcall.MustCallChecker.class, 13 | "nolightweightownership", 14 | "-Anomsgtext", 15 | "-AnoLightweightOwnership", 16 | // "-AstubDebug"); 17 | "-nowarn"); 18 | } 19 | 20 | @Parameters 21 | public static String[] getTestDirs() { 22 | return new String[] {"nolightweightownership"}; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasImpl.java: -------------------------------------------------------------------------------- 1 | // A simple test that the extra obligations that MustCallAlias imposes are 2 | // respected. 3 | 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import org.checkerframework.checker.objectconstruction.qual.*; 7 | import java.io.*; 8 | 9 | public class MustCallAliasImpl implements Closeable { 10 | 11 | final @Owning Closeable foo; 12 | 13 | // I got this error here: (type.invalid.annotations.on.use) 14 | public @MustCallAlias MustCallAliasImpl(@MustCallAlias Closeable foo) { 15 | this.foo = foo; 16 | } 17 | 18 | @Override 19 | @EnsuresCalledMethods(value = {"this.foo"}, methods = {"close"}) 20 | public void close() throws IOException { 21 | this.foo.close(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/MCAOwningField.java: -------------------------------------------------------------------------------- 1 | // A test case for a common pattern in Zookeeper: something is must-call-alias 2 | // with an owning field, and therefore a false positive was issued. 3 | 4 | import java.net.Socket; 5 | 6 | import org.checkerframework.checker.objectconstruction.qual.Owning; 7 | import org.checkerframework.checker.mustcall.qual.MustCall; 8 | import org.checkerframework.checker.calledmethods.qual.*; 9 | 10 | @MustCall("stop") 11 | class MCAOwningField { 12 | 13 | @Owning final Socket s; 14 | 15 | MCAOwningField() throws Exception { 16 | s = new Socket(); 17 | } 18 | 19 | void simple() throws Exception { 20 | s.getInputStream(); 21 | } 22 | 23 | @EnsuresCalledMethods(value="s", methods="close") 24 | void stop() throws Exception { 25 | s.close(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/FileDescriptors.java: -------------------------------------------------------------------------------- 1 | // A test for some issues related to the getFD() method in RandomAccessFile. 2 | 3 | import java.io.*; 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | 7 | class FileDescriptors { 8 | void test(@Owning RandomAccessFile r) throws Exception { 9 | @MustCall("close") FileDescriptor fd = r.getFD(); 10 | // :: error: assignment.type.incompatible 11 | @MustCall({}) FileDescriptor fd2 = r.getFD(); 12 | } 13 | 14 | void test2(@Owning RandomAccessFile r) throws Exception { 15 | @MustCall("close") FileInputStream f = new FileInputStream(r.getFD()); 16 | // :: error: assignment.type.incompatible 17 | @MustCall({}) FileInputStream f2 = new FileInputStream(r.getFD()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/InferTypeArgs.java: -------------------------------------------------------------------------------- 1 | // A test case from the CF's all-systems tests that fails with the MC checker unless an 2 | // implicit upper bound is made explicit. 3 | 4 | class CFAbstractValue> {} 5 | 6 | class CFAbstractAnalysis> {} 7 | 8 | class GenericAnnotatedTypeFactory< 9 | Value extends CFAbstractValue, FlowAnalysis extends CFAbstractAnalysis> { 10 | 11 | protected FlowAnalysis createFlowAnalysis() { 12 | FlowAnalysis result = invokeConstructorFor(); 13 | return result; 14 | } 15 | 16 | // The difference between this version of this test and the all-systems version is the "extends Object" on 17 | // the next line. 18 | public static T invokeConstructorFor() { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/CreatesObligationInnerClass.java: -------------------------------------------------------------------------------- 1 | // Test case for https://github.com/kelloggm/object-construction-checker/issues/368 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | 5 | class CreatesObligationInnerClass { 6 | static class Foo { 7 | 8 | @CreatesObligation("this") 9 | void resetFoo() { } 10 | 11 | /** 12 | * non-static inner class 13 | */ 14 | class Bar { 15 | @CreatesObligation 16 | void bar() { 17 | // :: error: reset.not.owning 18 | resetFoo(); 19 | } 20 | } 21 | 22 | void callBar() { 23 | Bar b = new Bar(); 24 | // If this call to bar() were permitted with no errors, this would be unsound. 25 | b.bar(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/GuavaImmutablePropBuilder.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | 7 | @AutoValue 8 | abstract class GuavaImmutablePropBuilder { 9 | 10 | public abstract ImmutableList names(); 11 | 12 | static Builder builder() { 13 | return new AutoValue_GuavaImmutablePropBuilder.Builder(); 14 | } 15 | 16 | @AutoValue.Builder 17 | abstract static class Builder { 18 | 19 | abstract ImmutableList.Builder namesBuilder(); 20 | 21 | abstract GuavaImmutablePropBuilder build(); 22 | 23 | } 24 | 25 | public static void buildSomething() { 26 | // don't need to explicitly set the names 27 | builder().build(); 28 | } 29 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/NonBuildName.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | 6 | @AutoValue 7 | abstract class NonBuildName { 8 | 9 | public abstract String name(); 10 | 11 | static Builder builder() { 12 | return new AutoValue_NonBuildName.Builder(); 13 | } 14 | 15 | @AutoValue.Builder 16 | abstract static class Builder { 17 | 18 | abstract Builder setName(String name); 19 | 20 | abstract NonBuildName makeIt(); 21 | 22 | } 23 | 24 | static void correct() { 25 | Builder b = builder(); 26 | b.setName("Phil"); 27 | b.makeIt(); 28 | } 29 | 30 | static void wrong() { 31 | Builder b = builder(); 32 | // :: error: finalizer.invocation.invalid 33 | b.makeIt(); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/NoResourceAliasesTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | public class NoResourceAliasesTest extends CheckerFrameworkPerDirectoryTest { 10 | public NoResourceAliasesTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "noresourcealias", 15 | "-Anomsgtext", 16 | "-AcheckMustCall", 17 | "-AnoResourceAliases", 18 | "-nowarn"); 19 | } 20 | 21 | @Parameters 22 | public static String[] getTestDirs() { 23 | return new String[] {"noresourcealias"}; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/warnings-without-custom-types.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # a simple script to post-process a run of the must-call checker on hadoop or hbase 4 | # to remove 1) irrelevant lines not related to errors, 2) required.method.not.called 5 | # errors on custom types, 3) errors in hadoop-common, which isn't typechecked 6 | 7 | # input: a file containing the output of compiling one of the above projects 8 | 9 | out=$1 10 | 11 | cat "${out}" | grep "WARNING" | grep -e "objectconstruction:" -e "mustcall:" -e "mustcallnoaccumulationframes:" | grep -v "The type of object is: org." | grep -v "mustcall:inconsistent.mustcall.subtype" | grep -v "The type of object is: testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "counter", 15 | "-Anomsgtext", 16 | // "-Astubs=stubs", 17 | "-AuseValueChecker", 18 | "-AcheckMustCall", 19 | "-AcountMustCall", 20 | "-nowarn"); 21 | } 22 | 23 | @Parameterized.Parameters 24 | public static String[] getTestDirs() { 25 | return new String[] {"counter"}; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/MustCallOnlyJDKTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | public class MustCallOnlyJDKTest extends CheckerFrameworkPerDirectoryTest { 10 | public MustCallOnlyJDKTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "mustcall", 15 | "-Anomsgtext", 16 | "-AcheckMustCall", 17 | "-AcountMustCall", 18 | "-AonlyUses=^java\\.", 19 | "-nowarn"); 20 | } 21 | 22 | @Parameters 23 | public static String[] getTestDirs() { 24 | return new String[] {"mustcall-onlyjdk"}; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/NoAccumulationFramesTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | public class NoAccumulationFramesTest extends CheckerFrameworkPerDirectoryTest { 10 | public NoAccumulationFramesTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "noaccumulationframes", 15 | "-Anomsgtext", 16 | "-AcheckMustCall", 17 | "-AnoAccumulationFrames", 18 | "-nowarn"); 19 | } 20 | 21 | @Parameters 22 | public static String[] getTestDirs() { 23 | return new String[] {"noaccumulationframes"}; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/SocketTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized; 8 | 9 | public class SocketTest extends CheckerFrameworkPerDirectoryTest { 10 | public SocketTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "socket", 15 | "-Anomsgtext", 16 | // "-Astubs=stubs", 17 | "-AuseValueChecker", 18 | "-AcheckMustCall", 19 | "-AcountMustCall", 20 | "-nowarn"); 21 | } 22 | 23 | @Parameterized.Parameters 24 | public static String[] getTestDirs() { 25 | return new String[] {"socket"}; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/SimpleFalsePositive.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.model.Filter; 4 | import com.amazonaws.services.ec2.AmazonEC2; 5 | 6 | import java.util.*; 7 | 8 | // A simple (potential) false positive case with mutliple filters. 9 | class SimpleFalsePositive { 10 | void test(AmazonEC2 ec2Client, String namePrefix) { 11 | DescribeImagesRequest request = new DescribeImagesRequest() 12 | .withOwners("martin") 13 | .withFilters(Arrays.asList( 14 | new Filter("platform", Arrays.asList("windows")), 15 | new Filter("name", Arrays.asList(String.format("%s*", namePrefix))))); 16 | DescribeImagesResult result = ec2Client.describeImages(request); 17 | } 18 | } -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/NoLightweightOwnershipTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | public class NoLightweightOwnershipTest extends CheckerFrameworkPerDirectoryTest { 10 | public NoLightweightOwnershipTest(List testFiles) { 11 | super( 12 | testFiles, 13 | ObjectConstructionChecker.class, 14 | "nolightweightownership", 15 | "-Anomsgtext", 16 | "-AcheckMustCall", 17 | "-AnoLightweightOwnership", 18 | "-nowarn"); 19 | } 20 | 21 | @Parameters 22 | public static String[] getTestDirs() { 23 | return new String[] {"nolightweightownership"}; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: org.sosy-lab:java-smt 10 | versions: 11 | - "> 3.2.0" 12 | - dependency-name: com.google.errorprone:javac 13 | versions: 14 | - 1.8.0-u20 15 | - dependency-name: com.diffplug.spotless 16 | versions: 17 | - 5.12.1 18 | - dependency-name: com.amazonaws:aws-java-sdk-bom 19 | versions: 20 | - 1.11.1000 21 | - dependency-name: org.checkerframework:checker-qual 22 | versions: 23 | - 3.10.0 24 | - 3.11.0 25 | - 3.9.1 26 | - dependency-name: org.checkerframework:framework-test 27 | versions: 28 | - 3.10.0 29 | - 3.11.0 30 | - 3.9.1 31 | - dependency-name: org.checkerframework:checker 32 | versions: 33 | - 3.10.0 34 | - 3.11.0 35 | - 3.9.1 36 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/MyDataInputStream.java: -------------------------------------------------------------------------------- 1 | import java.io.DataInputStream; 2 | import java.io.InputStream; 3 | import java.nio.ByteBuffer; 4 | import java.io.IOException; 5 | 6 | public class MyDataInputStream extends DataInputStream { 7 | public MyDataInputStream(InputStream in) { 8 | super(in); 9 | } 10 | 11 | public void readFully(long position, ByteBuffer buf) throws IOException { 12 | if (in instanceof ByteBufferPositionedReadable) { 13 | ((ByteBufferPositionedReadable) in).readFully(position, buf); 14 | } else { 15 | throw new UnsupportedOperationException("Byte-buffer pread " + 16 | "unsupported by " + in.getClass().getCanonicalName()); 17 | } 18 | } 19 | 20 | interface ByteBufferPositionedReadable { 21 | void readFully(long position, ByteBuffer buf) throws IOException; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasImplWrong1.java: -------------------------------------------------------------------------------- 1 | // A simple test that the extra obligations that MustCallAlias imposes are 2 | // respected. This version gets it wrong by not assigning the MCA param 3 | // to a field. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | public class MustCallAliasImplWrong1 implements Closeable { 11 | 12 | final @Owning Closeable foo; 13 | 14 | // :: error: required.method.not.called 15 | public @MustCallAlias MustCallAliasImplWrong1(@MustCallAlias Closeable foo) { 16 | this.foo = null; 17 | } 18 | 19 | @Override 20 | @EnsuresCalledMethods(value = {"this.foo"}, methods = {"close"}) 21 | public void close() throws IOException { 22 | this.foo.close(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/CommandResponse.java: -------------------------------------------------------------------------------- 1 | // Based on a false positive in Zookeeper. 2 | 3 | import java.util.Map; 4 | 5 | class CommandResponse { 6 | Map data; 7 | 8 | public void putAll(Map m) { 9 | // This is a false positive. The fix is to change the declaration to match what's below. 10 | // The cause is that implicit upper bounds are defaulted to top, to match the intuition 11 | // that e.g. List actually means List. In this case, that 12 | // causes an incompatibility with putAll, whose type requires @Bottom Object as the second 13 | // type parameter, because of the type of the data field. 14 | // :: error: argument.type.incompatible 15 | data.putAll(m); 16 | } 17 | 18 | public void putAll2(Map m) { 19 | data.putAll(m); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/BuilderGetter.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | 6 | @AutoValue 7 | abstract class BuilderGetter { 8 | 9 | public abstract String name(); 10 | 11 | static Builder builder() { 12 | return new AutoValue_BuilderGetter.Builder(); 13 | } 14 | 15 | @AutoValue.Builder 16 | abstract static class Builder { 17 | 18 | abstract Builder setName(String name); 19 | 20 | abstract String name(); 21 | 22 | abstract BuilderGetter build(); 23 | 24 | } 25 | 26 | static void correct() { 27 | Builder b = builder(); 28 | b.setName("Phil"); 29 | b.build(); 30 | } 31 | 32 | static void wrong() { 33 | Builder b = builder(); 34 | b.name(); 35 | // :: error: finalizer.invocation.invalid 36 | b.build(); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughWrong4.java: -------------------------------------------------------------------------------- 1 | // A test that a class can extend another class with an MCA constructor, 2 | // and have its own constructor be MCA as well. 3 | // This version just closes the MCA parameter, which isn't wrong so much as weird but I wanted a test for it. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | class MustCallAliasPassthroughWrong4 extends FilterInputStream { 11 | // I mean I guess this return type is technically okay - it's too conservative (@Owning on the 12 | // param would be better) but I see no reason not to verify it. 13 | @MustCallAlias MustCallAliasPassthroughWrong4(@MustCallAlias InputStream is) throws Exception { 14 | super(null); 15 | is.close(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/MustCallNoAccumulationFramesChecker.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall; 2 | 3 | import org.checkerframework.framework.qual.StubFiles; 4 | import org.checkerframework.framework.source.SupportedOptions; 5 | 6 | /** 7 | * This copy of the MustCall Checker is identical, except that it does not load the stub files at 8 | * initialization that treat unconnected sockets as @MustCall({}). See 9 | * SocketAccumulationFrames.astub. 10 | * 11 | *

The only difference is the contents of the @StubFiles annotation, below. 12 | */ 13 | @StubFiles({ 14 | "Socket.astub", 15 | "NotOwning.astub", 16 | "Stream.astub", 17 | "NoObligationGenerics.astub", 18 | "NoObligationStreams.astub", 19 | "Reflection.astub", 20 | }) 21 | @SupportedOptions({MustCallChecker.NO_ACCUMULATION_FRAMES}) 22 | public class MustCallNoAccumulationFramesChecker extends MustCallChecker {} 23 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/GuavaImmutable.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | 7 | @AutoValue 8 | abstract class GuavaImmutable { 9 | 10 | public abstract ImmutableList names(); 11 | 12 | static Builder builder() { 13 | return new AutoValue_GuavaImmutable.Builder(); 14 | } 15 | 16 | @AutoValue.Builder 17 | abstract static class Builder { 18 | 19 | abstract Builder names(ImmutableList value); 20 | 21 | abstract GuavaImmutable build(); 22 | 23 | } 24 | 25 | public static void buildSomethingWrong() { 26 | // :: error: finalizer.invocation.invalid 27 | builder().build(); 28 | } 29 | 30 | public static void buildSomethingRight() { 31 | builder().names(ImmutableList.of()).build(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/SocketContainer2.java: -------------------------------------------------------------------------------- 1 | // A simple class that has a Socket as an owning field. 2 | // This test exists to check that we gracefully handle assignments to it. 3 | 4 | import java.net.*; 5 | import java.io.*; 6 | 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import org.checkerframework.checker.calledmethods.qual.*; 9 | import org.checkerframework.checker.mustcall.qual.*; 10 | 11 | @MustCall("close") 12 | class SocketContainer2 { 13 | 14 | @Owning Socket sock = new Socket(); 15 | 16 | public SocketContainer2(String host, int port) throws Exception { 17 | // It's not okay to assign to a field with an initializer! 18 | // :: error: required.method.not.called 19 | sock = new Socket(host, port); 20 | } 21 | 22 | @EnsuresCalledMethods(value="this.sock", methods="close") 23 | public void close() throws IOException { 24 | sock.close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/OptionalSocket.java: -------------------------------------------------------------------------------- 1 | // A test case for some false positives that came up in Zookeeper. 2 | // These are sort-of a must call alias situation, but it's not as clear. 3 | // I suspect our MCA implementation won't actually handle these cases, in 4 | // which case it's fine with me to skip this test for now. - Martin 5 | // @skip-test 6 | 7 | import java.util.*; 8 | import java.net.*; 9 | import java.io.*; 10 | import org.checkerframework.checker.objectconstruction.qual.*; 11 | 12 | class OptionalSocket { 13 | void test_close_get_null(@Owning Optional sock) throws IOException { 14 | // TODO can't pass this 15 | if (sock.get() != null) { 16 | // TODO can't pass this 17 | sock.get().close(); 18 | } 19 | } 20 | 21 | void test_close_get(@Owning Optional sock) throws IOException { 22 | // TODO can't pass this 23 | sock.get().close(); 24 | } 25 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/SocketContainer3.java: -------------------------------------------------------------------------------- 1 | // A simple class that has a Socket as an owning field. 2 | // This test exists to check that we gracefully handle assignments. 3 | 4 | import java.net.*; 5 | import java.io.*; 6 | 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import org.checkerframework.checker.calledmethods.qual.*; 9 | import org.checkerframework.checker.mustcall.qual.*; 10 | 11 | @MustCall("close") 12 | class SocketContainer3 { 13 | @Owning Socket sock = null; 14 | 15 | public SocketContainer3(String host, int port) throws Exception { 16 | // Even if the field appears to always be initialized to null, 17 | // so why isn't this safe? 18 | // :: error: required.method.not.called 19 | sock = new Socket(host, port); 20 | } 21 | 22 | @EnsuresCalledMethods(value="this.sock", methods="close") 23 | public void close() throws IOException { 24 | sock.close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/ManualMustCallEmptyOnConstructor.java: -------------------------------------------------------------------------------- 1 | // test for https://github.com/kelloggm/object-construction-checker/issues/326 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.common.returnsreceiver.qual.*; 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | import java.io.InputStream; 9 | 10 | class ManualMustCallEmptyOnConstructor { 11 | 12 | // Test that writing @MustCall({}) on a constructor results in an error 13 | @MustCall("a") 14 | static class Foo { 15 | final @Owning InputStream is; 16 | 17 | // :: error: inconsistent.constructor.type 18 | @MustCall({}) Foo(@Owning InputStream is) { 19 | this.is = is; 20 | } 21 | 22 | @EnsuresCalledMethods(value="this.is", methods="close") 23 | void a() throws Exception { 24 | is.close(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /must-call-qual/src/main/java/org/checkerframework/checker/mustcall/qual/InheritableMustCall.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * This annotation is an alias for {@link MustCall} that applies to the type on which it is written 11 | * and all of its subtypes. It is useful to avoid the need to annotate each subtype with an {@link 12 | * MustCall} annotation. This annotation may only be written on a class declaration. 13 | */ 14 | @Inherited 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target({ElementType.TYPE}) 17 | public @interface InheritableMustCall { 18 | /** 19 | * Methods that must be called, on any expression whose type is annotated. 20 | * 21 | * @return methods that must be called 22 | */ 23 | public String[] value() default {}; 24 | } 25 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/SocketBufferedReader.java: -------------------------------------------------------------------------------- 1 | // a test for missing mustcall propagation that might have caused a false positive? 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.io.PrintStream; 6 | import java.net.*; 7 | import org.checkerframework.checker.mustcall.qual.*; 8 | 9 | class SocketBufferedReader { 10 | void test(String address, int port) 11 | { 12 | try 13 | { 14 | Socket socket = new Socket( address, 80 ); 15 | PrintStream out = new PrintStream( socket.getOutputStream() ); 16 | BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); 17 | @MustCall("close") BufferedReader reader = in; 18 | // :: error: assignment.type.incompatible 19 | @MustCall({}) BufferedReader reader2 = in; 20 | in.close(); 21 | } 22 | catch( Exception e ) 23 | { 24 | e.printStackTrace(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/ACExceptionalExitPointTest.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.mustcall.qual.*; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.common.returnsreceiver.qual.*; 4 | 5 | class ACExceptionalExitPointTest { 6 | 7 | @MustCall("a") 8 | class Foo { 9 | void a() {} 10 | @This Foo b() { 11 | return this; 12 | } 13 | void c() {} 14 | } 15 | 16 | Foo makeFoo(){ 17 | return new Foo(); 18 | } 19 | 20 | @CalledMethods({"a"}) Foo makeFoo2(){ 21 | Foo f = new Foo(); 22 | f.a(); 23 | return f; 24 | } 25 | 26 | void exceptionalExitWrong() throws Exception { 27 | // :: error: required.method.not.called 28 | Foo fw = makeFoo(); 29 | throw new Exception(); 30 | } 31 | 32 | void exceptionalExitCorrect() throws Exception { 33 | Foo fw = new Foo(); 34 | fw.a(); 35 | throw new Exception(); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /object-construction-qual/src/main/java/org/checkerframework/checker/objectconstruction/qual/EnsuresCalledMethodsVarArgs.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.objectconstruction.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Target; 5 | 6 | /** 7 | * Indicates that the method, if it terminates successfully, always invokes the given methods on all 8 | * of the arguments passed in the varargs position. 9 | * 10 | *

Consider the following method: 11 | * 12 | *

13 |  * @EnsuresCalledMethodsVarArgs("m")
14 |  * public void callMOnAll(S s, T t...) { ... }
15 |  * 
16 | * 17 | *

This method guarantees that {@code m()} is always called on every {@code T} object passed in 18 | * the {@code t} varargs argument before the method returns. 19 | */ 20 | @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 21 | public @interface EnsuresCalledMethodsVarArgs { 22 | 23 | /** @return the methods guaranteed to be invoked on the varargs parameters */ 24 | String[] value(); 25 | } 26 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing 2 | ========= 3 | 4 | Note that these steps require pushing directly to the master branch on GitHub. Be sure that other developers are not pushing changes concurrently. 5 | 6 | 1. Change the version in `gradle.properties` to a non-SNAPSHOT version. E.g., if the `VERSION_NAME` in `gradle.properties` is `X.Y.Z-SNAPSHOT`, change it to `X.Y.Z`. Also change all mentions of the current version in `README.md` to refer to version `X.Y.Z` instead. 7 | 2. `export NEWVER=X.Y.Z`, where `X.Y.Z` is the new version from step 1. 8 | 3. `git commit -am "Prepare for release $NEWVER."` 9 | 4. `git tag -a v"$NEWVER" -m "Version $NEWVER"` 10 | 5. `./gradlew --no-parallel clean uploadArchives` 11 | 6. Update the `gradle.properties` to the next SNAPSHOT version, `X.Y.Z+1-SNAPSHOT`. 12 | 7. Update `README-developers.md` to refer to `X.Y.Z+1-SNAPSHOT`. 13 | 8. `git commit -am "Prepare next development version."` 14 | 9. `git push && git push --tags` 15 | 10. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. 16 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/CallWithinBuilder.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | import java.util.Collection; 6 | 7 | import com.google.common.collect.ImmutableList; 8 | 9 | @AutoValue 10 | abstract class CallWithinBuilder { 11 | 12 | public abstract ImmutableList names(); 13 | 14 | static Builder builder() { 15 | return new AutoValue_CallWithinBuilder.Builder(); 16 | } 17 | 18 | @AutoValue.Builder 19 | abstract static class Builder { 20 | 21 | abstract ImmutableList.Builder namesBuilder(); 22 | 23 | public Builder addName(String name) { 24 | namesBuilder().add(name); 25 | return this; 26 | } 27 | 28 | public Builder addNames(Collection names) { 29 | for (String n: names) { 30 | addName(n); 31 | } 32 | return this; 33 | } 34 | 35 | abstract CallWithinBuilder build(); 36 | 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /must-call-qual/src/main/java/org/checkerframework/checker/mustcall/qual/MustCallUnknown.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import org.checkerframework.framework.qual.SubtypeOf; 8 | 9 | /** 10 | * The top qualifier in the Must Call type hierarchy. It represents a type that might have an 11 | * obligation to call any set (even an infinite set!) of methods, and therefore represents every 12 | * object. This type should rarely be written by programmers, because the Must Call type system 13 | * should be used to track specific MustCall obligations using {@link MustCall}; the Object 14 | * Construction Checker cannot verify that the property represented by this annotation is enforced. 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) 18 | @SubtypeOf({}) 19 | public @interface MustCallUnknown {} 20 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughLocal.java: -------------------------------------------------------------------------------- 1 | // A test that passing a local to an MCA super constructor is allowed. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | import java.io.*; 7 | 8 | class MustCallAliasPassthroughLocal extends FilterInputStream { 9 | MustCallAliasPassthroughLocal(File f) throws Exception { 10 | // This is safe - this MCA constructor of FilterInputStream means that the result of this 11 | // constructor - i.e. the caller - is taking ownership of this newly-created output stream. 12 | super(new FileInputStream(f)); 13 | } 14 | 15 | static void test(File f) throws Exception { 16 | // :: error: required.method.not.called 17 | new MustCallAliasPassthroughLocal(f); 18 | } 19 | 20 | static void test_ok(File f) throws Exception { 21 | new MustCallAliasPassthroughLocal(f).close(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasLayeredStreams.java: -------------------------------------------------------------------------------- 1 | // Test case based on an MCA situation in Zookeeper. 2 | import org.checkerframework.checker.objectconstruction.qual.*; 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import java.io.*; 6 | 7 | class MustCallAliasLayeredStreams { 8 | InputStream cache; 9 | public InputStream createInputStream(String filename) throws FileNotFoundException { 10 | if (cache == null) { 11 | // The real version of this uses a mix of JDK and custom streams, so it makes more sense... 12 | // TODO we shouldn't report a warning here and the code is okay because the cache is non-owning, 13 | // and the caller of createInputStream is the owner of all of these streams. 14 | // :: error: required.method.not.called 15 | cache = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(filename)))); 16 | } 17 | return cache; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/SimpleInferenceMerge.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | /* The simplest inference test case Martin could think of */ 4 | class SimpleInferenceMerge { 5 | void build(@CalledMethods({"a", "b"}) SimpleInferenceMerge this) { } 6 | 7 | void a() { } 8 | 9 | void b() {} 10 | 11 | void c() {} 12 | 13 | static void doStuffCorrectMerge(boolean b) { 14 | SimpleInferenceMerge s = new SimpleInferenceMerge(); 15 | if (b) { 16 | s.a(); 17 | s.b(); 18 | } else { 19 | s.b(); 20 | s.a(); 21 | s.c(); 22 | } 23 | s.build(); 24 | } 25 | 26 | static void doStuffWrongMerge(boolean b) { 27 | SimpleInferenceMerge s = new SimpleInferenceMerge(); 28 | if (b) { 29 | s.a(); 30 | s.b(); 31 | } else { 32 | s.b(); 33 | s.c(); 34 | } 35 | // :: error: finalizer.invocation.invalid 36 | s.build(); 37 | } 38 | } -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/TryWithResourcesCrash.java: -------------------------------------------------------------------------------- 1 | // A test case for a crash that Narges encountered while checking hfds. 2 | 3 | import java.io.DataOutputStream; 4 | import java.io.OutputStream; 5 | import java.io.Closeable; 6 | import java.io.IOException; 7 | 8 | class TryWithResourcesCrash { 9 | void test(FileSystem fs, byte[] bytes, String path) throws IOException { 10 | try (FSDataOutputStream out = fs.createFile(path).overwrite(true).build()) { 11 | out.write(bytes); 12 | } 13 | } 14 | 15 | class FSDataOutputStream extends DataOutputStream { 16 | FSDataOutputStream(OutputStream os) { 17 | super(os); 18 | } 19 | } 20 | 21 | abstract class FSDataOutputStreamBuilder> { 22 | abstract S build(); 23 | abstract B overwrite(boolean b); 24 | } 25 | 26 | abstract class FileSystem implements Closeable { 27 | abstract FSDataOutputStreamBuilder createFile (String s); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasOwningField.java: -------------------------------------------------------------------------------- 1 | // Based on a MustCallAlias scenario in Zookeeper. 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import java.io.*; 7 | 8 | public @MustCall("shutdown") class MustCallAliasOwningField { 9 | 10 | private final @Owning BufferedInputStream input; 11 | 12 | public MustCallAliasOwningField(@Owning BufferedInputStream input, boolean b) { 13 | this.input = input; 14 | if (b) { 15 | DataInputStream d = new DataInputStream(input); 16 | authenticate(d); 17 | } 18 | } 19 | 20 | @EnsuresCalledMethods(value="this.input", methods="close") 21 | public void shutdown() throws IOException { 22 | input.close(); 23 | } 24 | 25 | public static void authenticate(InputStream is) { 26 | 27 | } 28 | 29 | public void wrapField() { 30 | DataInputStream dis = new DataInputStream(input); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/HBaseReport1.java: -------------------------------------------------------------------------------- 1 | // Based on a HBase false positive 2 | // In this example fstream is @MCA with out and close is called on all 3 | // exit paths but we randomly report a warning for this test case 4 | // @skip-test 5 | 6 | import java.io.*; 7 | import java.io.FilterInputStream; 8 | import java.util.*; 9 | 10 | class HBaseReport1{ 11 | 12 | public static void test(String fileName) { 13 | FileWriter fstream; 14 | try { 15 | // :: error: required.method.not.called 16 | fstream = new FileWriter(fileName); 17 | } catch (IOException e) { 18 | return; 19 | } 20 | 21 | BufferedWriter out = new BufferedWriter(fstream); 22 | 23 | try { 24 | try { 25 | out.write(fileName + "\n"); 26 | } finally { 27 | try { 28 | out.close(); 29 | } finally { 30 | fstream.close(); 31 | } 32 | } 33 | } catch (IOException e) { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/CreatesObligationSimpler.java: -------------------------------------------------------------------------------- 1 | // A simpler test that @CreatesObligation works as intended wrt the Object Construction Checker. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | 7 | @MustCall("a") 8 | class CreatesObligationSimpler { 9 | 10 | @CreatesObligation 11 | void reset() { } 12 | 13 | @CreatesObligation("this") 14 | void resetThis() { } 15 | 16 | void a() { } 17 | 18 | static @MustCall({}) CreatesObligationSimpler makeNoMC() { 19 | return null; 20 | } 21 | 22 | static void test1() { 23 | // :: error: required.method.not.called 24 | CreatesObligationSimpler cos = makeNoMC(); 25 | @MustCall({}) CreatesObligationSimpler a = cos; 26 | cos.reset(); 27 | // :: error: assignment.type.incompatible 28 | @CalledMethods({"reset"}) CreatesObligationSimpler b = cos; 29 | @CalledMethods({}) CreatesObligationSimpler c = cos; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughWrong2.java: -------------------------------------------------------------------------------- 1 | // A test that a class can extend another class with an MCA constructor, 2 | // and have its own constructor be MCA as well. 3 | // This version passes the MCA param to another method instead of the passthrough constructor. 4 | // This is actually okay - the stream does get closed, if it needs to be closed - though the 5 | // MCA annotation on the return type is super misleading and will lead to FPs. It would be better 6 | // to annotate code like this with @Owning on the constructor. 7 | 8 | import org.checkerframework.checker.mustcall.qual.*; 9 | import org.checkerframework.checker.calledmethods.qual.*; 10 | import org.checkerframework.checker.objectconstruction.qual.*; 11 | import java.io.*; 12 | 13 | class MustCallAliasPassthroughWrong2 extends FilterInputStream { 14 | @MustCallAlias MustCallAliasPassthroughWrong2(@MustCallAlias InputStream is) throws Exception { 15 | super(null); 16 | closeIS(is); 17 | } 18 | 19 | void closeIS(@Owning InputStream is) throws Exception { 20 | is.close(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/IsPreserved.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | @AutoValue 6 | abstract class IsPreserved { 7 | 8 | abstract String name(); 9 | 10 | abstract String getAddress(); 11 | 12 | abstract boolean isPresent(); 13 | 14 | static Builder builder() { 15 | return new AutoValue_IsPreserved.Builder(); 16 | } 17 | 18 | @AutoValue.Builder 19 | abstract static class Builder { 20 | 21 | abstract Builder name(String val); 22 | 23 | abstract Builder getAddress(String val); 24 | 25 | abstract Builder isPresent(boolean value); 26 | 27 | abstract IsPreserved build(); 28 | } 29 | 30 | public static void buildSomethingRight() { 31 | Builder b = builder(); 32 | b.name("Frank"); 33 | b.getAddress("something"); 34 | b.isPresent(true); 35 | b.build(); 36 | } 37 | 38 | public static void buildSomethingRightFluent() { 39 | builder().name("Bill").getAddress("something").isPresent(false).build(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/Validation.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | 6 | @AutoValue 7 | abstract class Validation { 8 | 9 | public abstract String name(); 10 | 11 | static Builder builder() { 12 | return new AutoValue_Validation.Builder(); 13 | } 14 | 15 | @AutoValue.Builder 16 | abstract static class Builder { 17 | 18 | abstract Builder setName(String name); 19 | 20 | abstract Validation autoBuild(); 21 | 22 | public Validation build(@CalledMethods("setName") Builder this) { 23 | Validation v = autoBuild(); 24 | if (v.name().length() < 5) { 25 | throw new RuntimeException("name too short!"); 26 | } 27 | return v; 28 | } 29 | 30 | } 31 | 32 | static void correct() { 33 | Builder b = builder(); 34 | b.setName("Phil"); 35 | b.build(); 36 | } 37 | 38 | static void wrong() { 39 | Builder b = builder(); 40 | // :: error: finalizer.invocation.invalid 41 | b.build(); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/ListOfMustCall.java: -------------------------------------------------------------------------------- 1 | // A test that checks that parameterized classes in the JDK don't cause false positives 2 | // when they are used with an @MustCall-annotated class. 3 | 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | 6 | import java.util.*; 7 | 8 | @MustCall("a") 9 | class ListOfMustCall { 10 | static void test(ListOfMustCall lm) { 11 | List l = new ArrayList<>(); 12 | // add(E e) takes an object of the type argument's type 13 | l.add(lm); 14 | // remove(Object e) takes an object 15 | l.remove(lm); 16 | } 17 | 18 | static void test2(ListOfMustCall lm) { 19 | List<@MustCall("a") ListOfMustCall> l = new ArrayList<>(); 20 | l.add(lm); 21 | l.remove(lm); 22 | } 23 | 24 | static void test3(ListOfMustCall lm) { 25 | List l = new ArrayList<>(); 26 | l.remove(lm); 27 | } 28 | 29 | static void test4(ListOfMustCall lm) { 30 | List l = new ArrayList<>(); 31 | l.remove(lm); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Martin Kellogg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/FooParcelable.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import android.os.Parcelable; 3 | 4 | /** 5 | * Test for support of AutoValue Parcel extension. This test currently passes, but only because we ignore cases 6 | * where we cannot find a matching setter for a method we think corresponds to an AutoValue property. 7 | * See https://github.com/kelloggm/object-construction-checker/issues/110 8 | */ 9 | @AutoValue 10 | abstract class FooParcelable implements Parcelable { 11 | abstract String name(); 12 | 13 | static Builder builder() { 14 | return new AutoValue_FooParcelable.Builder(); 15 | } 16 | 17 | @AutoValue.Builder 18 | abstract static class Builder { 19 | 20 | abstract Builder setName(String value); 21 | 22 | abstract FooParcelable build(); 23 | } 24 | 25 | public static void buildSomethingWrong() { 26 | Builder b = builder(); 27 | // :: error: finalizer.invocation.invalid 28 | b.build(); 29 | } 30 | 31 | public static void buildSomethingRight() { 32 | Builder b = builder(); 33 | b.setName("Frank"); 34 | b.build(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughWrong3.java: -------------------------------------------------------------------------------- 1 | // This is a test for what happens when there's a missing MCA return type. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | import java.io.*; 7 | 8 | class MustCallAliasPassthroughWrong3 { 9 | // Both of these verify - it's okay to leave off the MCA param, because the return type is 10 | // owning - but the first one leads to imprecision at call sites. 11 | static InputStream missingMCA(@MustCallAlias InputStream is) { 12 | return is; 13 | } 14 | 15 | static @MustCallAlias InputStream withMCA(@MustCallAlias InputStream is) { 16 | return is; 17 | } 18 | 19 | // :: error: required.method.not.called 20 | void use_bad(@Owning InputStream is) throws Exception { 21 | InputStream is2 = missingMCA(is); 22 | is2.close(); 23 | } 24 | 25 | void use_good(@Owning InputStream is) throws Exception { 26 | InputStream is2 = withMCA(is); 27 | is2.close(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/TryWithResourcesSimple.java: -------------------------------------------------------------------------------- 1 | // A simple test that a try-with-resources socket doesn't issue a false positive. 2 | 3 | import java.net.Socket; 4 | import java.io.*; 5 | import java.util.*; 6 | import java.nio.channels.*; 7 | 8 | class TryWithResourcesSimple { 9 | static void test(String address, int port) { 10 | try (Socket socket = new Socket(address, port)) { 11 | 12 | } catch (Exception e) { 13 | 14 | } 15 | } 16 | 17 | public boolean isPreUpgradableLayout(File oldF) throws IOException { 18 | 19 | if (!oldF.exists()) { 20 | return false; 21 | } 22 | // check the layout version inside the storage file 23 | // Lock and Read old storage file 24 | try (RandomAccessFile oldFile = new RandomAccessFile(oldF, "rws"); 25 | FileLock oldLock = oldFile.getChannel().tryLock()) { 26 | if (null == oldLock) { 27 | throw new OverlappingFileLockException(); 28 | } 29 | oldFile.seek(0); 30 | int oldVersion = oldFile.readInt(); 31 | return false; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/SetInsideBuildWithCM.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.objectconstruction.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | @AutoValue 6 | abstract class SetInsideBuildWithCM { 7 | public abstract String name(); 8 | public abstract int size(); 9 | static Builder builder() { 10 | return new AutoValue_SetInsideBuildWithCM.Builder(); 11 | } 12 | @AutoValue.Builder 13 | abstract static class Builder { 14 | abstract Builder setName(String name); 15 | abstract Builder setSize(int value); 16 | abstract SetInsideBuildWithCM autoBuild(); 17 | public SetInsideBuildWithCM build() { 18 | return this.autoBuild(); 19 | } 20 | } 21 | 22 | public static void buildSomethingCorrect() { 23 | Builder b = builder(); 24 | b.setName("Frank"); 25 | b.setSize(2); 26 | b.build(); 27 | } 28 | public static void buildSomethingWrong() { 29 | Builder b = builder(); 30 | // :: error: finalizer.invocation.invalid 31 | b.build(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/SelfAssign.java: -------------------------------------------------------------------------------- 1 | // test assignments of the same variable to itself 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | import java.io.*; 7 | 8 | class SelfAssign { 9 | 10 | static void test0() throws IOException { 11 | InputStream selfAssignIn0 = new FileInputStream("file.txt"); 12 | try { 13 | selfAssignIn0 = selfAssignIn0; 14 | } finally { 15 | selfAssignIn0.close(); 16 | } 17 | 18 | } 19 | 20 | static void test1(boolean b) throws IOException { 21 | InputStream selfAssignIn = new FileInputStream("file.txt"); 22 | try { 23 | selfAssignIn = selfAssignIn.markSupported()? selfAssignIn: new BufferedInputStream(selfAssignIn); 24 | } finally { 25 | selfAssignIn.close(); 26 | } 27 | } 28 | 29 | static void test2(boolean b) throws IOException { 30 | InputStream in = new FileInputStream("file.txt"); 31 | try { 32 | in = new BufferedInputStream(in); 33 | } finally { 34 | in.close(); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/SetInsideBuild.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | @AutoValue 6 | abstract class SetInsideBuild { 7 | public abstract String name(); 8 | public abstract int size(); 9 | static Builder builder() { 10 | return new AutoValue_SetInsideBuild.Builder(); 11 | } 12 | @AutoValue.Builder 13 | abstract static class Builder { 14 | abstract Builder setName(String name); 15 | abstract Builder setSize(int value); 16 | abstract SetInsideBuild autoBuild(); 17 | public SetInsideBuild build(@CalledMethods({"setName"}) Builder this) { 18 | 19 | return this.setSize(4).autoBuild(); 20 | } 21 | } 22 | 23 | public static void buildSomethingWrong() { 24 | Builder b = builder(); 25 | // :: error: finalizer.invocation.invalid 26 | b.build(); 27 | } 28 | 29 | 30 | public static void buildSomethingCorrect() { 31 | Builder b = builder(); 32 | b.setName("Frank"); 33 | b.build(); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/noaccumulationframes/CreatesObligationSimpler.java: -------------------------------------------------------------------------------- 1 | // A simpler test that @CreatesObligation works as intended wrt the Object Construction Checker. 2 | 3 | // This test has been modified to expect that CreatesObligation is feature-flagged to off. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | 9 | @MustCall("a") 10 | class CreatesObligationSimpler { 11 | 12 | @CreatesObligation 13 | void reset() { } 14 | 15 | @CreatesObligation("this") 16 | void resetThis() { } 17 | 18 | void a() { } 19 | 20 | static @MustCall({}) CreatesObligationSimpler makeNoMC() { 21 | // :: error: return.type.incompatible 22 | return new CreatesObligationSimpler(); 23 | } 24 | 25 | static void test1() { 26 | CreatesObligationSimpler cos = makeNoMC(); 27 | @MustCall({}) CreatesObligationSimpler a = cos; 28 | cos.reset(); 29 | @CalledMethods({"reset"}) CreatesObligationSimpler b = cos; 30 | @CalledMethods({}) CreatesObligationSimpler c = cos; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/EnhancedFor.java: -------------------------------------------------------------------------------- 1 | // Based on some false positives I found in ZK. 2 | 3 | import java.util.List; 4 | import java.net.Socket; 5 | import java.io.IOException; 6 | 7 | import org.checkerframework.checker.objectconstruction.qual.Owning; 8 | 9 | class EnhancedFor { 10 | void test(List list) { 11 | for (Socket s : list) { 12 | try { 13 | s.close(); 14 | } catch (IOException i) { } 15 | } 16 | } 17 | 18 | void test2(List list) { 19 | for (int i = 0; i < list.size(); i++) { 20 | Socket s = list.get(i); 21 | try { 22 | s.close(); 23 | } catch (IOException io) { } 24 | } 25 | } 26 | 27 | void test3(List list) { 28 | // This error is issued because `s` is a local variable, and 29 | // the foreach loop under the hood assigns the result of a call 30 | // to Iterator#next into it (which is owning by default, because it's 31 | // a method return type). 32 | // :: error: required.method.not.called 33 | for (Socket s : list) { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Issue20.java: -------------------------------------------------------------------------------- 1 | // test case for issue 20: https://github.com/kelloggm/object-construction-checker/issues/20 2 | 3 | import java.util.Map; 4 | 5 | public class Issue20 { 6 | 7 | private boolean enableProtoAnnotations; 8 | 9 | @SuppressWarnings({"unchecked"}) 10 | private T getProtoExtension( 11 | E element, GeneratedExtension extension) { 12 | // Use this method as the chokepoint for all field annotations processing, so we can 13 | // toggle on/off annotations processing in one place. 14 | if (!enableProtoAnnotations) { 15 | return null; 16 | } 17 | return (T) element.getOptionFields().get(extension.getDescriptor()); 18 | } 19 | 20 | // stubs of relevant classes 21 | private class Message {} 22 | private class ProtoElement { 23 | public Map getOptionFields() { return null; } 24 | } 25 | private class FieldDescriptor {} 26 | private class GeneratedExtension { 27 | public FieldDescriptor getDescriptor() { 28 | return null; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/CreatesObligationOverride.java: -------------------------------------------------------------------------------- 1 | // A test case that a create-obligation method cannot be called via dynamic dispatch 2 | // without resetting the obligation. 3 | 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | 6 | @MustCall("a") 7 | class CreatesObligationOverride { 8 | @CreatesObligation 9 | @Override 10 | // :: error: creates.obligation.override.invalid 11 | public String toString() { return "this method could re-assign a field or do something else it shouldn't"; } 12 | 13 | public void a() { } 14 | 15 | public static void test_no_cast() { 16 | // :: error: required.method.not.called 17 | CreatesObligationOverride co = new CreatesObligationOverride(); 18 | co.a(); 19 | co.toString(); 20 | } 21 | 22 | public static void test_cast() { 23 | // it would be ideal if the checker issued an error directly here, but the best we can do is 24 | // issue the error above when the offending version of toString() is defined 25 | CreatesObligationOverride co = new CreatesObligationOverride(); 26 | co.a(); 27 | Object o = co; 28 | o.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /object-construction-qual/src/main/java/org/checkerframework/checker/objectconstruction/qual/RequiresCalledMethods.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.objectconstruction.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Target; 5 | import org.checkerframework.checker.calledmethods.qual.CalledMethods; 6 | import org.checkerframework.framework.qual.PreconditionAnnotation; 7 | import org.checkerframework.framework.qual.QualifierArgument; 8 | 9 | @PreconditionAnnotation(qualifier = CalledMethods.class) 10 | @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 11 | public @interface RequiresCalledMethods { 12 | /** 13 | * The Java expressions to which the qualifier applies. 14 | * 15 | * @return the Java expressions to which the qualifier applies 16 | * @see org.checkerframework.framework.qual.EnsuresQualifier 17 | */ 18 | // Preconditions must use "value" as the name (conditional preconditions use "expression"). 19 | String[] value(); 20 | 21 | /** 22 | * The methods guaranteed to be invoked on the expressions. 23 | * 24 | * @return the methods guaranteed to be invoked on the expressions 25 | */ 26 | @QualifierArgument("value") 27 | String[] methods(); 28 | } 29 | -------------------------------------------------------------------------------- /fse-2021/INSTALL.md: -------------------------------------------------------------------------------- 1 | #### Reproducing our results 2 | 3 | To reproduce our results, you can use our docker image. Install Docker 4 | and follow the instructions at the beginning of README.md. 5 | 6 | #### Kicking the tires 7 | 8 | See the "Kicking the tires" section of file README.md. 9 | 10 | #### Extending our work 11 | 12 | The `object-construction-checker` directory contains the source code for 13 | the tool. 14 | 15 | Its test suite (a set of simple Java programs with expected errors) appears 16 | in the the `object-construction-checker/tests/mustcall` and 17 | `object-construction-checker/tests/socket` subdirectories. To run the 18 | tests, run `./gradlew build` from the root directory. You can add new tests 19 | by placing them in one of these directories, if you want to see how the 20 | tool works on small examples. 21 | 22 | This artifact contains a version of our tool that reproduces the numbers in 23 | the paper. Going forward, the tool will be distributed with the Checker 24 | Framework (https://checkerframework.org) and called the "Resource Leak 25 | Checker". Future researchers who wish to build on or compare against our 26 | tool should use that version, which will incorporate bug fixes and 27 | precision improvements. 28 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/MustCallAliasSocketException.java: -------------------------------------------------------------------------------- 1 | // Based on a false positive in Zookeeper's SaslQuorumAuthLearner. 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | 6 | class MustCallAliasSocketException { 7 | 8 | public boolean quorumRequireSasl; 9 | 10 | // This socket isn't owning, so we shouldn't warn on it. 11 | public void authenticate(Socket sock, String hostName) throws IOException { 12 | if (!quorumRequireSasl) { 13 | // I kept this block in the test case because it demonstrates 14 | // that sock is definitely non-owning. 15 | System.out.println("Skipping SASL authentication as"); 16 | return; 17 | } 18 | try { 19 | DataOutputStream dout = new DataOutputStream(sock.getOutputStream()); 20 | // Before MCA was implemented, the call to getInputStream() below triggered 21 | // a false positive warning that dout had not been closed. 22 | DataInputStream din = new DataInputStream(sock.getInputStream()); 23 | // ~30 lines omitted... 24 | } finally { 25 | // do some other things that are definitely not closing sock 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/errors-without-custom-types.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # input: a file containing the output of ./run-always-call-on-zookeeper.sh 4 | 5 | # output: the input, without 6 | # 1) irrelevant lines not related to errors, and 7 | # 2) required.method.not.called errors on custom types (those starting with "org."). 8 | 9 | # PROBLEM: This script strips off information after the first line of an error message. For example, the full output may contain 10 | # [ERROR] /home/mernst/research/types/cf-case-studies/zookeeper-fork-kelloggm-branch-with-annotations-verified-2/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java:[1214,29] error: [objectconstruction:contracts.postcondition.not.satisfied] postcondition of finish is not satisfied. 11 | # found : no information about this.sock 12 | # required: this.sock is @CalledMethods("close") 13 | # of which only the first line is retained in the output of this script. 14 | 15 | zookeeper_out=$1 16 | 17 | # shellcheck disable=SC2002 18 | cat "${zookeeper_out}" | grep "ERROR" | grep -e "objectconstruction:" -e "mustcall:" -e "mustcallnoaccumulationframes:" | grep -v "The type of object is: org." | grep -v "mustcall:inconsistent.mustcall.subtype" | sort -u 19 | -------------------------------------------------------------------------------- /object-construction-checker/tests/noresourcealias/MustCallAliasPassthroughLocal.java: -------------------------------------------------------------------------------- 1 | // A test that passing a local to an MCA super constructor is allowed. 2 | // This version has been modified to expect errors, as if running under 3 | // -AnoResourceAliases - so @MustCallAlias annotations are ignored. 4 | 5 | import org.checkerframework.checker.mustcall.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import org.checkerframework.checker.objectconstruction.qual.*; 8 | import java.io.*; 9 | 10 | class MustCallAliasPassthroughLocal extends FilterInputStream { 11 | MustCallAliasPassthroughLocal(File f) throws Exception { 12 | // This is safe - this MCA constructor of FilterInputStream means that the result of this 13 | // constructor - i.e. the caller - is taking ownership of this newly-created output stream. 14 | // :: error: required.method.not.called 15 | super(new FileInputStream(f)); 16 | } 17 | 18 | static void test(File f) throws Exception { 19 | // :: error: required.method.not.called 20 | new MustCallAliasPassthroughLocal(f); 21 | } 22 | 23 | static void test_ok(File f) throws Exception { 24 | new MustCallAliasPassthroughLocal(f).close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/Inheritance.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.common.returnsreceiver.qual.This; 4 | 5 | class Inheritance { 6 | static interface Props { 7 | String name(); 8 | String somethingElse(); 9 | abstract class Builder> { 10 | public abstract @This B name(String value); 11 | } 12 | } 13 | 14 | @AutoValue 15 | abstract static class PropHolder implements Props { 16 | abstract int size(); 17 | @Override 18 | public String somethingElse() { return "hi"; } 19 | static Builder builder() { 20 | return new AutoValue_Inheritance_PropHolder.Builder(); 21 | } 22 | @AutoValue.Builder 23 | public abstract static class Builder extends Props.Builder { 24 | public abstract Builder size(int value); 25 | public abstract PropHolder build(); 26 | } 27 | } 28 | 29 | static void correct() { 30 | PropHolder.Builder b = PropHolder.builder(); 31 | b.name("Manu").size(1).build(); 32 | } 33 | 34 | static void wrong() { 35 | PropHolder.Builder b = PropHolder.builder(); 36 | // :: error: finalizer.invocation.invalid 37 | b.size(1).build(); 38 | } 39 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/GetAndIs.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | @AutoValue 6 | abstract class GetAndIs { 7 | abstract String get(); 8 | 9 | abstract boolean is(); 10 | 11 | static Builder builder() { 12 | return new AutoValue_GetAndIs.Builder(); 13 | } 14 | 15 | @AutoValue.Builder 16 | abstract static class Builder { 17 | 18 | abstract Builder setGet(String value); 19 | 20 | abstract Builder setIs(boolean value); 21 | 22 | abstract GetAndIs build(); 23 | } 24 | 25 | public static void buildSomethingWrong() { 26 | Builder b = builder(); 27 | b.setGet("Frank"); 28 | // :: error: finalizer.invocation.invalid 29 | b.build(); 30 | } 31 | 32 | public static void buildSomethingRight() { 33 | Builder b = builder(); 34 | b.setGet("Frank"); 35 | b.setIs(false); 36 | b.build(); 37 | } 38 | 39 | public static void buildSomethingWrongFluent() { 40 | // :: error: finalizer.invocation.invalid 41 | builder().setGet("Frank").build(); 42 | } 43 | 44 | public static void buildSomethingRightFluent() { 45 | builder().setGet("Jim").setIs(true).build(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/SocketContainer.java: -------------------------------------------------------------------------------- 1 | // A simple class that has a Socket as an owning field. 2 | // This test exists to check that we gracefully handle assignments 1) 3 | // in the constructor and 2) to null. 4 | 5 | import java.net.*; 6 | import java.io.*; 7 | 8 | import org.checkerframework.checker.objectconstruction.qual.*; 9 | import org.checkerframework.checker.calledmethods.qual.*; 10 | import org.checkerframework.checker.mustcall.qual.*; 11 | 12 | @MustCall("close") 13 | class SocketContainer { 14 | @Owning Socket sock; 15 | 16 | public SocketContainer(String host, int port) throws Exception { 17 | // It should be okay to assign to uninitialized owning fields in the constructor. 18 | // But it isn't! Why? 19 | // :: error: required.method.not.called 20 | sock = new Socket(host, port); 21 | // It's definitely not okay to do it twice! 22 | // :: error: required.method.not.called 23 | sock = new Socket(host, port); 24 | } 25 | 26 | @EnsuresCalledMethods(value="this.sock", methods="close") 27 | public void close() throws IOException { 28 | sock.close(); 29 | // It's okay to assign a field to null after its obligations have been fulfilled, 30 | // without inducing a reset. 31 | sock = null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/WithOwnersFilter.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.model.Filter; 4 | import com.amazonaws.services.ec2.AmazonEC2; 5 | 6 | public class WithOwnersFilter { 7 | private static final String IMG_NAME = "some_linux_img"; 8 | 9 | public static void correct1(AmazonEC2 client) { 10 | DescribeImagesResult result = client.describeImages(new DescribeImagesRequest() 11 | .withFilters(new Filter("name").withValues(IMG_NAME)) 12 | .withFilters(new Filter("owner").withValues("my_aws_acct"))); 13 | } 14 | 15 | public static void correct2(AmazonEC2 client) { 16 | DescribeImagesRequest request = new DescribeImagesRequest(); 17 | request.withFilters(new Filter("name").withValues(IMG_NAME)); 18 | request.withFilters(new Filter("owner").withValues("my_aws_acct")); 19 | client.describeImages(request); 20 | } 21 | 22 | public static void correct3(AmazonEC2 client) { 23 | DescribeImagesRequest request = new DescribeImagesRequest(); 24 | request.withFilters(new Filter("name").withValues(IMG_NAME), new Filter("owner").withValues("my_aws_acct")); 25 | client.describeImages(request); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /experimental-machinery/case-studies/anno-counter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A simple script to count annotations in the case studies. 4 | # The argument is the name of the project. 5 | # TODO: why did we uses this instead of AnnotationStatistics? IIRC we had some trouble getting 6 | # it to play nicely with Maven? I do remember double-checking the output of this very carefully. 7 | 8 | project=$1 9 | 10 | echo "@Owning:" 11 | grep -EoniR --include \*.java "@Owning" "${project}" | wc -l 12 | 13 | echo "@NotOwning (counted with @Owning):" 14 | grep -EoniR --include \*.java "@NotOwning" "${project}" | wc -l 15 | 16 | echo "@EnsuresCalledMethods:" # This will also count @EnsuresCalledMethodsIf. That's by design. 17 | grep -EoniR --include \*.java "@EnsuresCalledMethods" "${project}" | wc -l 18 | 19 | echo "@MustCall:" 20 | grep -EoniR --include \*.java "@MustCall\(" "${project}" | wc -l 21 | 22 | echo "@InheritableMustCall (counted with @MustCall):" 23 | grep -EoniR --include \*.java "@InheritableMustCall" "${project}" | wc -l 24 | 25 | echo "@MustCallAlias:" 26 | grep -EoniR --include \*.java "@MustCallAlias" "${project}" | wc -l 27 | 28 | echo "@PolyMustCall (counted with @MustCallAlias):" 29 | grep -EoniR --include \*.java "@PolyMustCall" "${project}" | wc -l 30 | 31 | echo "@CreatesObligation:" 32 | grep -EoniR --include \*.java "@CreatesObligation" "${project}" | wc -l 33 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/HdfsReport3.java: -------------------------------------------------------------------------------- 1 | // Based on a false positive in hdfs 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.objectconstruction.qual.*; 5 | import org.checkerframework.common.returnsreceiver.qual.*; 6 | import org.checkerframework.checker.calledmethods.qual.*; 7 | import java.net.*; 8 | import java.io.*; 9 | import java.io.FilterInputStream; 10 | import java.util.*; 11 | import javax.net.ssl.*; 12 | import java.nio.*; 13 | import java.nio.file.*; 14 | import java.security.*; 15 | import java.security.DigestInputStream; 16 | import java.security.MessageDigest; 17 | import java.nio.channels.ServerSocketChannel; 18 | import java.lang.StringBuilder; 19 | 20 | class HdfsReport3 21 | { 22 | private StringBuffer nonObligationTest(int id) { 23 | final StringWriter out = new StringWriter(); 24 | dumpTreeRecursively(new PrintWriter(out, true), new StringBuilder(), 25 | id); 26 | return out.getBuffer(); 27 | } 28 | 29 | public void dumpTreeRecursively(PrintWriter out, StringBuilder prefix, 30 | int snapshotId) { 31 | } 32 | 33 | // StringBuilder doesn't implement closeable 34 | final private StringBuilder sb = new StringBuilder(); 35 | final private Formatter formatter = new Formatter(sb); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /object-construction-checker/tests/guice/src/main/java/Module.java: -------------------------------------------------------------------------------- 1 | import com.google.inject.AbstractModule; 2 | import com.google.inject.Provides; 3 | 4 | public class Module extends AbstractModule { 5 | 6 | @Override 7 | protected void configure() { 8 | install(new OtherModule()); // the "install" can be used to get additional @Provides from other classes 9 | } 10 | 11 | /* 12 | * This is the problematic method: the method requires a String and a Double to be provided, but the module 13 | * only provides a String (the Double is commented out). If the method has an argument type for which we do 14 | * not have a @Provides, it fails at runtime. 15 | * Note that there are other ways of getting Provided parameters such as "@Named" 16 | */ 17 | @Provides 18 | public Main providesGuiceObject(final String thisExists, 19 | final Double thisDoesntExist, 20 | final Float comesFromElsewhere) { 21 | return new Main(); 22 | } 23 | 24 | @Provides 25 | private String privedesSomeString() { 26 | return "Hello World"; 27 | } 28 | 29 | /* 30 | If this provider is commented in, there is no runtime error anymore. 31 | */ 32 | // @Provides 33 | // private Double privedesSomeDouble() { 34 | // return 1.0; 35 | // } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/GetChannelOnLocks.java: -------------------------------------------------------------------------------- 1 | // Based on a false positive in hdfs 2 | // A test that shows we are handling the sequence of method invocations correctly 3 | 4 | import java.io.IOException; 5 | import java.nio.channels.FileLock; 6 | 7 | class GetChannelOnLocks { 8 | public boolean isLockSupported(FileLock lock, FileLock lock1, FileLock lock2) throws IOException { 9 | FileLock firstLock = null; 10 | FileLock secondLock = null; 11 | try { 12 | firstLock = lock1; 13 | if(firstLock == null) { 14 | return true; 15 | } 16 | secondLock = lock2; 17 | if(secondLock == null) { 18 | return true; 19 | } 20 | } finally { 21 | if(firstLock != null && firstLock != lock) { 22 | firstLock.release(); 23 | firstLock.channel().close(); 24 | } 25 | if(secondLock != null) { 26 | secondLock.release(); 27 | secondLock.channel().close(); 28 | } 29 | } 30 | return false; 31 | } 32 | 33 | public void isLockSupported2(FileLock lock, FileLock firstLock) throws IOException { 34 | if(firstLock != null && firstLock != lock) { 35 | firstLock.release(); 36 | firstLock.channel().close(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/BorrowOnReturn.java: -------------------------------------------------------------------------------- 1 | // tests that the MustCall checker respects the OCC's Owning and NotOwning annotations on return values. 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | 6 | class BorrowOnReturn { 7 | @MustCall("a") class Foo { 8 | void a() { } 9 | } 10 | 11 | @Owning Object getOwnedFoo() { 12 | // :: error: return.type.incompatible 13 | return new Foo(); 14 | } 15 | 16 | Object getNoAnnoFoo() { 17 | // Treat as owning, so warn 18 | // :: error: return.type.incompatible 19 | return new Foo(); 20 | } 21 | 22 | @NotOwning Object getNotOwningFooWrong() { 23 | // OCC must-call checker will warn about this; MC checker isn't responsible 24 | return new Foo(); 25 | } 26 | 27 | Object getNotOwningFooRightButNoNotOwningAnno() { 28 | Foo f = new Foo(); 29 | f.a(); 30 | // This is still an error for now, because it's treated as an owning pointer. TODO: fix this kind of FP? 31 | // :: error: return.type.incompatible 32 | return f; 33 | } 34 | 35 | @NotOwning Object getNotOwningFooRight() { 36 | Foo f = new Foo(); 37 | f.a(); 38 | return f; 39 | } 40 | 41 | @MustCall("a") Object getNotOwningFooRight2() { 42 | return new Foo(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /fse-2021/LICENSE.md: -------------------------------------------------------------------------------- 1 | Our tool itself is distributed under an MIT license. This license also applies to our scripts, which were originally part of the tool repository. 2 | 3 | Our modifications to the case study programs (and those programs themselves) are distributed under their respective open-source licenses. 4 | 5 | # MIT License 6 | 7 | Copyright (c) 2021 Martin Kellogg, Narges Shadab, Manu Sridharan, Michael Ernst 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /must-call-checker/tests/nolightweightownership/BorrowOnReturn.java: -------------------------------------------------------------------------------- 1 | // tests that the MustCall checker respects the OCC's Owning and NotOwning annotations on return values. 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | 6 | class BorrowOnReturn { 7 | @MustCall("a") class Foo { 8 | void a() { } 9 | } 10 | 11 | @Owning Object getOwnedFoo() { 12 | // :: error: return.type.incompatible 13 | return new Foo(); 14 | } 15 | 16 | Object getNoAnnoFoo() { 17 | // Treat as owning, so warn 18 | // :: error: return.type.incompatible 19 | return new Foo(); 20 | } 21 | 22 | @NotOwning Object getNotOwningFooWrong() { 23 | // :: error: return.type.incompatible 24 | return new Foo(); 25 | } 26 | 27 | Object getNotOwningFooRightButNoNotOwningAnno() { 28 | Foo f = new Foo(); 29 | f.a(); 30 | // This is still an error for now, because it's treated as an owning pointer. TODO: fix this kind of FP? 31 | // :: error: return.type.incompatible 32 | return f; 33 | } 34 | 35 | @NotOwning Object getNotOwningFooRight() { 36 | Foo f = new Foo(); 37 | f.a(); 38 | // :: error: return.type.incompatible 39 | return f; 40 | } 41 | 42 | @MustCall("a") Object getNotOwningFooRight2() { 43 | return new Foo(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /object-construction-checker/tests/noaccumulationframes/SocketContainer.java: -------------------------------------------------------------------------------- 1 | // A simple class that has a Socket as an owning field. 2 | // This is a modified version of tests/socket/SocketContainer.java 3 | // for checking that without CO support we can't assign to non-final owning fields at all. 4 | 5 | import java.net.*; 6 | import java.io.*; 7 | 8 | import org.checkerframework.checker.objectconstruction.qual.*; 9 | import org.checkerframework.checker.calledmethods.qual.*; 10 | import org.checkerframework.checker.mustcall.qual.*; 11 | 12 | @MustCall("close") 13 | class SocketContainer { 14 | @Owning Socket sock; 15 | 16 | public SocketContainer(String host, int port) throws Exception { 17 | // Assignments to owning fields should not be permitted. 18 | // :: error: required.method.not.called 19 | sock = new Socket(host, port); 20 | } 21 | 22 | // No missing create obligation error is issued, since CO is disabled... 23 | public void reassign(String host, int port) throws Exception { 24 | sock.close(); 25 | // For the RHS, because the field can't take ownership 26 | // :: error: required.method.not.called 27 | Socket sr = new Socket(host, port); 28 | // No warning for overwriting the field, since it can't take ownership! 29 | sock = sr; 30 | } 31 | 32 | @EnsuresCalledMethods(value="this.sock", methods="close") 33 | public void close() throws IOException { 34 | sock.close(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /object-construction-checker/tests/lombok/OldInherited.java: -------------------------------------------------------------------------------- 1 | // This is a test for the old @ReturnsReceiver annotation, which is inherited. 2 | // No one should ever write that annotation, but Lombok generates it as of version 1.18.10. 3 | 4 | import org.checkerframework.checker.builder.qual.ReturnsReceiver; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | 7 | class OldInherited { 8 | @ReturnsReceiver 9 | OldInherited getThis() { 10 | return this; 11 | } 12 | 13 | static class OldInheritedChild extends OldInherited { 14 | @java.lang.Override 15 | OldInherited getThis() { 16 | return this; 17 | } 18 | } 19 | 20 | void requiresGetThis(@CalledMethods("getThis") OldInherited this) { } 21 | 22 | public static void testGoodParent() { 23 | OldInherited o = new OldInherited(); 24 | o.getThis(); 25 | o.requiresGetThis(); 26 | } 27 | 28 | public static void testGoodChild() { 29 | OldInheritedChild o = new OldInheritedChild(); 30 | o.getThis(); 31 | o.requiresGetThis(); 32 | } 33 | 34 | public static void testBadParent() { 35 | OldInherited o = new OldInherited(); 36 | // :: error: finalizer.invocation.invalid 37 | o.requiresGetThis(); 38 | } 39 | 40 | public static void testBadChild() { 41 | OldInheritedChild o = new OldInheritedChild(); 42 | // :: error: finalizer.invocation.invalid 43 | o.requiresGetThis(); 44 | } 45 | } -------------------------------------------------------------------------------- /object-construction-checker/stubs/GenerateDataKey.astub: -------------------------------------------------------------------------------- 1 | package com.amazonaws.services.kms; 2 | 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.common.returnsreceiver.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | 7 | interface AWSKMS { 8 | // The predicate implements a modified one-hot detector that permits e.g. setNumberOfBytes and withNumberOfBytes to be called together, but not setNumberOfBytes and withKeySpec. Basically, this predicate enforces that only one of the two categories are represented. 9 | GenerateDataKeyResult generateDataKey(@CalledMethodsPredicate("(setNumberOfBytes || withNumberOfBytes || setKeySpec || withKeySpec) && !(setNumberOfBytes && setKeySpec) && !(setNumberOfBytes && withKeySpec) && !(withNumberOfBytes && setKeySpec) && !(withNumberOfBytes && withKeySpec)") GenerateDataKeyRequest request); 10 | } 11 | 12 | package com.amazonaws.services.kms.model; 13 | 14 | class GenerateDataKeyRequest { 15 | @This GenerateDataKeyRequest withEncryptionContext(Map encryptionContext); 16 | @This GenerateDataKeyRequest withGrantTokens(Collection grantTokens); 17 | @This GenerateDataKeyRequest withGrantTokens(String... grantTokens); 18 | @This GenerateDataKeyRequest withKeyId(String keyId); 19 | @This GenerateDataKeyRequest withKeySpec(DataKeySpec keySpec); 20 | @This GenerateDataKeyRequest withKeySpec(String keySpec); 21 | @This GenerateDataKeyRequest withNumberOfBytes(Integer numberOfBytes); 22 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Xor.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | class Xor { 4 | 5 | class Foo { 6 | void a() {} 7 | void b() {} 8 | void c() {} 9 | // SPEL doesn't support XOR directly (it represents exponentiation as ^ instead), 10 | // so use a standard gate encoding 11 | void aXorB(@CalledMethodsPredicate("(a || b) && !(a && b)") Foo this) {} 12 | } 13 | 14 | void test1(Foo f) { 15 | // :: error: method.invocation.invalid 16 | f.aXorB(); 17 | } 18 | 19 | void test2(Foo f) { 20 | f.c(); 21 | // :: error: method.invocation.invalid 22 | f.aXorB(); 23 | } 24 | 25 | void test3(Foo f) { 26 | f.a(); 27 | f.aXorB(); 28 | } 29 | 30 | void test4(Foo f) { 31 | f.b(); 32 | f.aXorB(); 33 | } 34 | 35 | void test5(Foo f) { 36 | f.a(); 37 | f.b(); 38 | // :: error: method.invocation.invalid 39 | f.aXorB(); 40 | } 41 | 42 | void callA(Foo f) { 43 | f.a(); 44 | } 45 | 46 | void test6(Foo f) { 47 | callA(f); 48 | f.b(); 49 | // DEMONSTRATION OF UNSOUNDNESS 50 | f.aXorB(); 51 | } 52 | 53 | void test7(@CalledMethods("a") Foo f) { 54 | f.aXorB(); 55 | } 56 | 57 | void test8(Foo f) { 58 | callA(f); 59 | // THIS IS AN UNAVOIDABLE FALSE POSITIVE 60 | // :: error: method.invocation.invalid 61 | f.aXorB(); 62 | } 63 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/RequiresCalledMethodsTest.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.mustcall.qual.*; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import java.io.*; 5 | 6 | public class RequiresCalledMethodsTest { 7 | 8 | @MustCall("a") 9 | static class Foo { 10 | void a() {} 11 | void c() {} 12 | } 13 | 14 | @MustCall("releaseFoo") 15 | static class FooField { 16 | private @Owning Foo foo = null; 17 | 18 | @RequiresCalledMethods(value = {"this.foo"}, methods = {"a"}) 19 | @CreatesObligation("this") 20 | void overwriteFooCorrect() { 21 | this.foo = new Foo(); 22 | } 23 | 24 | @CreatesObligation("this") 25 | void overwriteFooWrong() { 26 | // :: error: required.method.not.called 27 | this.foo = new Foo(); 28 | } 29 | 30 | @CreatesObligation("this") 31 | void overwriteFooWithoutReleasing() { 32 | // :: error: contracts.precondition.not.satisfied 33 | overwriteFooCorrect(); 34 | } 35 | 36 | void releaseThenOverwriteFoo() { 37 | releaseFoo(); 38 | // :: error: reset.not.owning 39 | overwriteFooCorrect(); 40 | } 41 | 42 | @EnsuresCalledMethods(value = {"this.foo"}, methods = {"a"}) 43 | void releaseFoo() { 44 | if (this.foo != null) { 45 | foo.a(); 46 | } 47 | } 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /experimental-machinery/ablation/plume-util-ablation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #set -e 4 | set -u 5 | set -o pipefail 6 | 7 | # this script performs the ablation study in section 8.2 on the plume-util benchmark. Note that this script 8 | # doesn't terminate when errors are encountered, because grep will fail if there are no new warnings or 9 | # no old warnings that aren't issued, which would cause the script to terminate early 10 | 11 | # prereq: plume-util is checked out in the current directory, with the no-lo, no-ra, no-af branches available locally. 12 | # prereq: run-always-call-on-plume-util.sh is in the current directory 13 | # prereq: the checker and the Checker Framework are built in the appropriate places 14 | 15 | run_ablation () { 16 | 17 | variant_name=$1 18 | 19 | cd plume-util 20 | 21 | git checkout "${variant_name}" 22 | 23 | cd .. 24 | 25 | sh run-always-call-on-plume-util.sh &> "${variant_name}-results" || true 26 | 27 | echo "new errors produced by ${variant_name} configuration:" 28 | 29 | cat "${variant_name}-results" | grep "error:" | sort | uniq | wc -l 30 | 31 | echo "old errors no longer produced by ${variant_name} configuration:" 32 | 33 | grep "unneeded.suppression" "${variant_name}-results" | wc -l 34 | 35 | echo "the result for the RLC paper's table 3 is the difference between these two numbers plus the number of false positives on the with-annotations branch" 36 | 37 | } 38 | 39 | run_ablation "no-lo" 40 | 41 | echo "" 42 | 43 | run_ablation "no-ra" 44 | 45 | echo "" 46 | 47 | run_ablation "no-af" 48 | -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/MustCallTypeValidator.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall; 2 | 3 | import com.sun.source.tree.Tree; 4 | import javax.lang.model.element.Element; 5 | import org.checkerframework.checker.mustcall.qual.MustCallAlias; 6 | import org.checkerframework.common.basetype.BaseTypeChecker; 7 | import org.checkerframework.common.basetype.BaseTypeValidator; 8 | import org.checkerframework.common.basetype.BaseTypeVisitor; 9 | import org.checkerframework.framework.type.AnnotatedTypeFactory; 10 | import org.checkerframework.framework.type.AnnotatedTypeMirror; 11 | import org.checkerframework.javacutil.AnnotationUtils; 12 | import org.checkerframework.javacutil.TreeUtils; 13 | 14 | /** 15 | * This type validator is identical to BaseTypeValidator, except that it always permits the use of 16 | * {@link MustCallAlias} annotations on type uses, because these will be validated by the Object 17 | * Construction Checker's -AcheckMustCall algorithm. 18 | */ 19 | public class MustCallTypeValidator extends BaseTypeValidator { 20 | public MustCallTypeValidator( 21 | BaseTypeChecker checker, BaseTypeVisitor visitor, AnnotatedTypeFactory atypeFactory) { 22 | super(checker, visitor, atypeFactory); 23 | } 24 | 25 | @Override 26 | protected void reportInvalidAnnotationsOnUse(AnnotatedTypeMirror type, Tree p) { 27 | Element elt = TreeUtils.elementFromTree(p); 28 | if (AnnotationUtils.containsSameByClass(elt.getAnnotationMirrors(), MustCallAlias.class)) { 29 | return; 30 | } 31 | super.reportInvalidAnnotationsOnUse(type, p); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Generics.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | import org.checkerframework.common.returnsreceiver.qual.*; 3 | 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.ArrayList; 7 | import java.util.stream.Stream; 8 | 9 | class Generics { 10 | 11 | static interface Symbol { 12 | 13 | boolean isStatic(); 14 | 15 | void finalize(@CalledMethods("isStatic") Symbol this); 16 | } 17 | 18 | static List<@CalledMethods("isStatic") Symbol> makeList(Symbol s) { 19 | s.isStatic(); 20 | ArrayList<@CalledMethods("isStatic") Symbol> l = new ArrayList<>(); 21 | l.add(s); 22 | return l; 23 | } 24 | 25 | static void useList() { 26 | Symbol s = null; 27 | for (Symbol t: makeList(s)) { 28 | t.finalize(); 29 | } 30 | } 31 | 32 | // reduced from real-world code 33 | private <@CalledMethods({}) T extends Symbol> T getMember(Class type, boolean b) { 34 | if (b) { 35 | T sym = getMember(type, !b); 36 | if (sym != null && sym.isStatic()) { 37 | return sym; 38 | } 39 | } else { 40 | T sym = getMember(type, b); 41 | if (sym != null) { 42 | return sym; 43 | } 44 | } 45 | return null; 46 | } 47 | 48 | static Stream stringList() { 49 | String s = "hi"; 50 | // dummy method call 51 | s.contains("h"); 52 | // should infer type Stream<@CalledMethods({}) String> 53 | return Arrays.asList(s).stream(); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/ConnectingServerSockets.java: -------------------------------------------------------------------------------- 1 | // a set of test cases that demonstrate that errors are actually insued in appropriate 2 | // places when ServerSockets are connected 3 | 4 | import java.net.*; 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class ConnectingServerSockets { 9 | 10 | static void simple_ss_test(SocketAddress sa) throws Exception { 11 | // :: error: required.method.not.called 12 | ServerSocket s = new ServerSocket(); 13 | s.bind(sa); 14 | } 15 | 16 | static void simple_ss_test2(SocketAddress sa) throws Exception { 17 | ServerSocket s = new ServerSocket(); 18 | // s.bind(sa); 19 | } 20 | 21 | static void simple_ss_test4(SocketAddress sa, int to) throws Exception { 22 | // :: error: required.method.not.called 23 | ServerSocket s = new ServerSocket(); 24 | s.bind(sa, to); 25 | } 26 | 27 | static @MustCall({}) ServerSocket makeUnconnected() throws Exception { 28 | return new ServerSocket(); 29 | } 30 | 31 | static void simple_ss_test5(SocketAddress sa) throws Exception { 32 | // :: error: required.method.not.called 33 | ServerSocket s = makeUnconnected(); 34 | s.bind(sa); 35 | } 36 | 37 | static void simple_ss_test6(SocketAddress sa) throws Exception { 38 | ServerSocket s = makeUnconnected(); 39 | // s.bind(sa); 40 | } 41 | 42 | static void simple_ss_test8(SocketAddress sa, int to) throws Exception { 43 | // :: error: required.method.not.called 44 | ServerSocket s = makeUnconnected(); 45 | s.bind(sa, to); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/PolyTests.java: -------------------------------------------------------------------------------- 1 | // Unit tests for the poly annotation. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.objectconstruction.qual.*; 5 | 6 | @MustCall("close") 7 | class PolyTests { 8 | static @PolyMustCall Object id(@PolyMustCall Object obj) { 9 | return obj; 10 | } 11 | 12 | static void test1(@Owning @MustCall("close") Object o) { 13 | @MustCall("close") Object o1 = id(o); 14 | // :: error: assignment.type.incompatible 15 | @MustCall({}) Object o2 = id(o); 16 | } 17 | 18 | static void test2(@Owning @MustCall({}) Object o) { 19 | @MustCall("close") Object o1 = id(o); 20 | @MustCall({}) Object o2 = id(o); 21 | } 22 | 23 | // These sort of constructors will always appear in stub files and are unverifiable for now. 24 | @SuppressWarnings("mustcall:type.invalid.annotations.on.use") 25 | @PolyMustCall PolyTests(@PolyMustCall Object obj) { 26 | 27 | } 28 | 29 | static void test3(@Owning @MustCall({"close"}) Object o) { 30 | @MustCall("close") Object o1 = new PolyTests(o); 31 | // :: error: assignment.type.incompatible 32 | @MustCall({}) Object o2 = new PolyTests(o); 33 | } 34 | 35 | static void test4(@Owning @MustCall({}) Object o) { 36 | @MustCall("close") Object o1 = new PolyTests(o); 37 | @MustCall({}) Object o2 = new PolyTests(o); 38 | } 39 | 40 | static void testArbitary(@Owning PolyTests p) { 41 | @MustCall("close") Object o1 = p; 42 | // :: error: assignment.type.incompatible 43 | @MustCall({}) Object o2 = p; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Not.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | class Not { 4 | 5 | class Foo { 6 | void a() {} 7 | void b() {} 8 | void c() {} 9 | void notA(@CalledMethodsPredicate("!a") Foo this) {} 10 | void notB(@CalledMethodsPredicate("!b") Foo this) {} 11 | } 12 | 13 | void test1(Foo f) { 14 | f.notA(); 15 | f.notB(); 16 | } 17 | 18 | void test2(Foo f) { 19 | f.c(); 20 | f.notA(); 21 | f.notB(); 22 | } 23 | 24 | void test3(Foo f) { 25 | f.a(); 26 | // :: error: method.invocation.invalid 27 | f.notA(); 28 | f.notB(); 29 | } 30 | 31 | void test4(Foo f) { 32 | f.b(); 33 | f.notA(); 34 | // :: error: method.invocation.invalid 35 | f.notB(); 36 | } 37 | 38 | void test5(Foo f) { 39 | f.a(); 40 | f.b(); 41 | // :: error: method.invocation.invalid 42 | f.notA(); 43 | // :: error: method.invocation.invalid 44 | f.notB(); 45 | } 46 | 47 | void callA(Foo f) { 48 | f.a(); 49 | } 50 | 51 | void test6(Foo f) { 52 | callA(f); 53 | // DEMONSTRATION OF UNSOUNDNESS 54 | f.notA(); 55 | } 56 | 57 | void test7(@CalledMethods("a") Foo f) { 58 | // :: error: method.invocation.invalid 59 | f.notA(); 60 | } 61 | 62 | void test8(Foo f, boolean test) { 63 | if (test) { 64 | f.a(); 65 | } else { 66 | f.b(); 67 | } 68 | // DEMONSTRATION OF UNSOUNDNESS 69 | f.notA(); 70 | } 71 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/DoubleIf.java: -------------------------------------------------------------------------------- 1 | // Based on an FP in ZK. Only #parse threw an error; 2 | // removing either if statement made the code verifiable. 3 | // Adding an @CalledMethods annotation, like in parse4, also 4 | // makes this code verifiable... 5 | 6 | import java.io.*; 7 | 8 | import org.checkerframework.checker.calledmethods.qual.CalledMethods; 9 | 10 | class DoubleIf { 11 | 12 | String fn; 13 | 14 | public void parse(boolean b, boolean c) throws Exception { 15 | if (c) { 16 | FileInputStream fis1 = new FileInputStream(fn); 17 | try { 18 | } finally { 19 | fis1.close(); 20 | } 21 | if (b) { 22 | } 23 | } 24 | } 25 | 26 | public void parse2(boolean c) throws Exception { 27 | if (c) { 28 | FileInputStream fis2 = new FileInputStream(fn); 29 | try { 30 | } finally { 31 | fis2.close(); 32 | } 33 | } 34 | } 35 | 36 | public void parse3(boolean b) throws Exception { 37 | FileInputStream fis3 = new FileInputStream(fn); 38 | try { 39 | } finally { 40 | fis3.close(); 41 | } 42 | if (b) { 43 | } 44 | } 45 | 46 | public void parse4(boolean b, boolean c) throws Exception { 47 | if (c) { 48 | FileInputStream fis4 = new FileInputStream(fn); 49 | try { 50 | } finally { 51 | fis4.close(); 52 | } 53 | if (b) { 54 | } 55 | @CalledMethods("close") FileInputStream fis24 = fis4; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/hbase-ablation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #set -e 4 | set -u 5 | #set -x 6 | set -o pipefail 7 | 8 | # this script performs the ablation study in section 8.2 on the HBase benchmark. Note that this script 9 | # doesn't terminate when errors are encountered, because grep will fail if there are no new warnings or 10 | # no old warnings that aren't issued, which would cause the script to terminate early 11 | 12 | # prereq: hbase is checked out in the current directory, with the no-lo, no-ra, no-af branches available locally. 13 | # prereq: run-always-call-on-hbase.sh is in the current directory 14 | # prereq: the checker and the Checker Framework are built in the appropriate places 15 | # prereq: the JAVA8_HOME environment variable is set 16 | 17 | run_ablation () { 18 | variant_name=$1 19 | cd hbase 20 | git checkout "${variant_name}" 21 | cd .. 22 | sh run-always-call-on-hbase.sh &> "${variant_name}-results" || true 23 | echo "new errors produced by ${variant_name} configuration:" 24 | ./warnings-without-custom-types.sh "${variant_name}-results" | sort | uniq | wc -l 25 | echo "old errors no longer produced by ${variant_name} configuration:" 26 | grep "unneeded.suppression" "${variant_name}-results" | wc -l 27 | echo "the result for the RLC paper's table 3 is the difference between these two numbers plus the number of false positives on the with-annotations branch" 28 | } 29 | 30 | if [ "x${JAVA8_HOME}" = "x" ]; then 31 | echo "Please set JAVA8_HOME to run the checker on hbase. hbase requires Java 8." 32 | exit 1 33 | fi 34 | 35 | run_ablation "no-lo" 36 | echo "" 37 | run_ablation "no-ra" 38 | echo "" 39 | run_ablation "no-af" 40 | -------------------------------------------------------------------------------- /must-call-qual/src/main/java/org/checkerframework/checker/mustcall/qual/MustCall.java: -------------------------------------------------------------------------------- 1 | package org.checkerframework.checker.mustcall.qual; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | import org.checkerframework.framework.qual.DefaultFor; 8 | import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; 9 | import org.checkerframework.framework.qual.SubtypeOf; 10 | import org.checkerframework.framework.qual.TypeUseLocation; 11 | 12 | /** 13 | * If an expression has type {@code @MustCall({"m1", "m2"})}, then the Object Construction Checker's 14 | * {@code -AcheckMustCall} mode will enforce that the methods "m1" and "m2" are called on the 15 | * annotated value before it is deallocated. 16 | * 17 | *

The subtyping relationship is: 18 | * 19 | *

{@code @MustCall({"m1"}) <: @MustCall({"m1", "m2"})}
20 | * 21 | *

In practice, this means that value of a type that is annotated with {@code @MustCall({"m1", 22 | * "m2"})} may be obligated to call "m1" and/or "m2" before it is deallocated. Such a value is 23 | * guaranteed not to be obligated to call any other methods. 24 | */ 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) 27 | @SubtypeOf({MustCallUnknown.class}) 28 | @DefaultQualifierInHierarchy 29 | @DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER}) 30 | public @interface MustCall { 31 | /** 32 | * Methods that must be called, on any expression whose type is annotated. 33 | * 34 | * @return methods that must be called 35 | */ 36 | public String[] value() default {}; 37 | } 38 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/hadoop-ablation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #set -e 4 | set -u 5 | #set -x 6 | set -o pipefail 7 | 8 | # this script performs the ablation study in section 8.2 on the Hadoop benchmark. Note that this script 9 | # doesn't terminate when errors are encountered, because grep will fail if there are no new warnings or 10 | # no old warnings that aren't issued, which would cause the script to terminate early 11 | 12 | # prereq: hadoop is checked out in the current directory, with the no-lo, no-ra, no-af branches available locally. 13 | # prereq: run-always-call-on-hadoop.sh is in the current directory 14 | # prereq: the checker and the Checker Framework are built in the appropriate places 15 | # prereq: the JAVA8_HOME environment variable is set 16 | 17 | run_ablation () { 18 | variant_name=$1 19 | cd hadoop 20 | git checkout "${variant_name}" 21 | cd .. 22 | sh run-always-call-on-hadoop.sh &> "${variant_name}-results" || true 23 | echo "new errors produced by ${variant_name} configuration:" 24 | ./warnings-without-custom-types.sh "${variant_name}-results" | sort | uniq | wc -l 25 | echo "old errors no longer produced by ${variant_name} configuration:" 26 | grep "unneeded.suppression" "${variant_name}-results" | wc -l 27 | echo "the result for the RLC paper's table 3 is the difference between these two numbers plus the number of false positives on the with-annotations branch" 28 | } 29 | 30 | if [ "x${JAVA8_HOME}" = "x" ]; then 31 | echo "Please set JAVA8_HOME to run the checker on hadoop. hadoop requires Java 8." 32 | exit 1 33 | fi 34 | 35 | run_ablation "no-lo" 36 | echo "" 37 | run_ablation "no-ra" 38 | echo "" 39 | run_ablation "no-af" 40 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasExamples.java: -------------------------------------------------------------------------------- 1 | // Simple tests of @MustCallAlias functionality on wrapper streams. 2 | 3 | import java.io.*; 4 | import java.net.*; 5 | import org.checkerframework.checker.objectconstruction.qual.*; 6 | import java.io.IOException; 7 | import org.checkerframework.checker.calledmethods.qual.*; 8 | 9 | class MustCallAliasExamples { 10 | 11 | void test_two_locals(String address) { 12 | Socket socket = null; 13 | try { 14 | socket = new Socket(address, 8000); 15 | DataInputStream d = new DataInputStream(socket.getInputStream()); 16 | } catch (IOException e){ 17 | 18 | } finally { 19 | closeSocket(socket); 20 | } 21 | } 22 | 23 | void test_close_wrapper(@Owning InputStream b) throws IOException { 24 | DataInputStream d = new DataInputStream(b); 25 | d.close(); 26 | } 27 | 28 | void test_close_nonwrapper(@Owning InputStream b) throws IOException { 29 | DataInputStream d = new DataInputStream(b); 30 | b.close(); 31 | } 32 | 33 | // :: error: required.method.not.called 34 | void test_no_close(@Owning InputStream b) { 35 | DataInputStream d = new DataInputStream(b); 36 | } 37 | 38 | // :: error: required.method.not.called 39 | void test_no_assign(@Owning InputStream b) { 40 | new DataInputStream(new BufferedInputStream(b)); 41 | } 42 | 43 | @EnsuresCalledMethods(value = "#1", methods = "close") 44 | void closeSocket(Socket sock) { 45 | try { 46 | if(sock!=null){ 47 | sock.close(); 48 | } 49 | } catch (IOException e) { 50 | 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/ClassForNameInit.java: -------------------------------------------------------------------------------- 1 | // Based on a number of false positives in Zookeeper that all use this pattern to reflectively 2 | // initialize a class. -AresolveReflection fixes this version, but most (4/5) of the failures in Zookeeper 3 | // persist even with that flag, which also imposes about a 50% perf overhead. So these are now expected warnings. 4 | 5 | import java.io.*; 6 | import java.lang.reflect.Constructor; 7 | 8 | class ClassForNameInit { 9 | 10 | public static InputStream inputStreamFactory() throws Exception { 11 | // FYI this code will always fail if you run it, so don't. 12 | // There's no ByteArrayInputStream constructor that takes no arguments. 13 | Class baisClass = Class.forName("java.io.ByteArrayInputStream"); 14 | Object bais = baisClass.getConstructor().newInstance(); 15 | return (InputStream) bais; 16 | } 17 | 18 | public static Object objectFactory() throws Exception { 19 | Class objClass = Class.forName("java.lang.Object"); 20 | Object obj = objClass.getConstructor().newInstance(); 21 | return (Object) obj; 22 | } 23 | 24 | private static Object getAuditLogger(String auditLoggerClass) { 25 | if (auditLoggerClass == null) { 26 | auditLoggerClass = Object.class.getName(); 27 | } 28 | try { 29 | Constructor clientCxnConstructor = Class.forName(auditLoggerClass) 30 | .getDeclaredConstructor(); 31 | Object auditLogger = (Object) clientCxnConstructor.newInstance(); 32 | return auditLogger; 33 | } catch (Exception e) { 34 | throw new RuntimeException("Couldn't instantiate " + auditLoggerClass, e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /experimental-machinery/case-studies/how-to-count-added-lines.txt: -------------------------------------------------------------------------------- 1 | The basic idea is that we only want to count changes that are actually 2 | to the program text. The results on Zookeeper should make it clear 3 | why: using this method, there are actually only 12 lines of the 4 | original Zookeeper program that we changed in any meaningful way 5 | (compared to the naive estimate of 192 lines). 6 | 7 | We also want to collect the reasons for each code change, since we might 8 | want to report that rather than the raw numbers. These should be collected 9 | in a similar manner to false positive reasons; as an example, here are 10 | the reasons that changes were made to Zookeeper (5 total changes): 11 | 12 | * reorder try and null check to allow @EnsuresCalledMethods to verify: 2 13 | * add final to a field: 2 14 | * create a new local variable so that the store can track multiple uses of socket.get(): 1 15 | 16 | Here's how to compute these: 17 | 1. from the target project's directory corresponding to the module of interest (e.g. zookeeper/zookeeper-server for me), run a command like this: 18 | 19 | > git diff origin/with-checker -- '*.java' 20 | 21 | 2. Look through the results and count (I used some scratch paper - the 22 | numbers should be pretty small) how many added (in green) lines there 23 | are that do NOT match one of the following conditions (i.e. discard 24 | any lines like the following): 25 | 26 | * lines that are blank 27 | * lines that are import statements for annotations 28 | * lines that are comments 29 | * lines that contain only a warning suppression 30 | * lines which changed only to add one or more annotations 31 | 32 | For each group of changes, record the reason that the change was made 33 | as a comment in the benchmark repository, for reproducibility. 34 | -------------------------------------------------------------------------------- /must-call-checker/src/main/java/org/checkerframework/checker/mustcall/SocketAccumulationFrames.astub: -------------------------------------------------------------------------------- 1 | // This version of the standard assumptions for sockets is intended for use with accumulation frame support (i.e. 2 | // without the -AnoAccumulationFrames argument to the checker). It includes @MustCall({}) annotations for no-argument 3 | // socket constructors, which would be unsound if accumulation frame support was disabled. The checker chooses 4 | // whether to use it automatically, when invoked via the Object Construction Checker. 5 | 6 | package java.net; 7 | 8 | 9 | import org.checkerframework.checker.mustcall.qual.*; 10 | import org.checkerframework.checker.objectconstruction.qual.*; 11 | import org.checkerframework.checker.calledmethods.qual.*; 12 | import org.checkerframework.common.returnsreceiver.qual.*; 13 | 14 | 15 | class Socket implements Closeable { 16 | @MustCall({}) Socket(); 17 | @MustCall({}) Socket(Proxy arg0); 18 | } 19 | 20 | class ServerSocket implements Closeable { 21 | @MustCall({}) ServerSocket() throws IOException; 22 | } 23 | 24 | package javax.net; 25 | 26 | class SocketFactory { 27 | @Owning @MustCall({}) Socket createSocket() throws IOException; 28 | } 29 | 30 | class ServerSocketFactory { 31 | @Owning @MustCall({}) ServerSocket createServerSocket() throws IOException; 32 | } 33 | 34 | package java.nio.channels; 35 | 36 | class SocketChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel { 37 | static @MustCall({}) SocketChannel open() throws IOException; 38 | } 39 | 40 | class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel { 41 | static @MustCall({}) ServerSocketChannel open() throws IOException; 42 | } 43 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/Cve2.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.model.Filter; 4 | import com.amazonaws.services.ec2.AmazonEC2; 5 | 6 | import java.util.Collections; 7 | 8 | // https://nvd.nist.gov/vuln/detail/CVE-2018-15869 9 | public class Cve2 { 10 | private static final String IMG_NAME = "some_linux_img"; 11 | 12 | public static void onlyNames(AmazonEC2 client) { 13 | // Should not be allowed unless .withOwner is also used 14 | DescribeImagesRequest request = new DescribeImagesRequest(); 15 | request.withFilters(new Filter("name").withValues(IMG_NAME)); 16 | 17 | // :: error: argument.type.incompatible 18 | DescribeImagesResult result = client.describeImages(request); 19 | } 20 | 21 | public static void correct1(AmazonEC2 client) { 22 | DescribeImagesRequest request = new DescribeImagesRequest(); 23 | request.withFilters(new Filter("name").withValues(IMG_NAME)); 24 | request.withOwners("martin"); 25 | 26 | DescribeImagesResult result = client.describeImages(request); 27 | } 28 | 29 | public static void correct2(AmazonEC2 client) { 30 | DescribeImagesRequest request = new DescribeImagesRequest(); 31 | request.withImageIds("myImageId"); 32 | 33 | DescribeImagesResult result = client.describeImages(request); 34 | } 35 | 36 | public static void correct3(AmazonEC2 client) { 37 | DescribeImagesRequest request = new DescribeImagesRequest(); 38 | request.setExecutableUsers(Collections.singletonList("myUser1")); 39 | 40 | DescribeImagesResult result = client.describeImages(request); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /object-construction-checker/tests/nolightweightownership/ACOwning.java: -------------------------------------------------------------------------------- 1 | // This copy of the ACOwning.java test from the mustcall tests 2 | // has expected errors as if ownership transfer can only happen based 3 | // on defaults - that is, that @Owning and @NotOwning annotations are 4 | // ignored. 5 | 6 | import org.checkerframework.checker.objectconstruction.qual.*; 7 | import org.checkerframework.checker.mustcall.qual.*; 8 | import org.checkerframework.common.returnsreceiver.qual.*; 9 | 10 | class ACOwning { 11 | 12 | @MustCall("a") 13 | static class Foo { 14 | void a() {} 15 | } 16 | 17 | Foo makeFoo() { 18 | return new Foo(); 19 | } 20 | 21 | static void takeOwnership(@Owning Foo foo) { 22 | foo.a(); 23 | } 24 | 25 | static void noOwnership(Foo foo) {} 26 | 27 | static void takeOwnershipWrong(@Owning Foo foo) { 28 | 29 | } 30 | 31 | static @NotOwning Foo getNonOwningFoo() { 32 | return new Foo(); 33 | } 34 | 35 | static void callGetNonOwningFoo() { 36 | // :: error: required.method.not.called 37 | getNonOwningFoo(); 38 | } 39 | 40 | static void ownershipInCallee() { 41 | // :: error: required.method.not.called 42 | Foo f = new Foo(); 43 | takeOwnership(f); 44 | // :: error: required.method.not.called 45 | Foo g = new Foo(); 46 | noOwnership(g); 47 | } 48 | 49 | @Owning public Foo owningAtReturn() { 50 | return new Foo(); 51 | } 52 | 53 | 54 | void owningAtReturnTest() { 55 | // :: error: required.method.not.called 56 | Foo f = owningAtReturn(); 57 | } 58 | 59 | 60 | void ownershipTest(){ 61 | // :: error: required.method.not.called 62 | takeOwnership(new Foo()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /experimental-machinery/ablation/zookeeper-ablation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #set -e 4 | set -u 5 | set -o pipefail 6 | 7 | # this script performs the ablation study in section 8.2 on the Zookeeper benchmark. Note that this script 8 | # doesn't terminate when errors are encountered, because grep will fail if there are no new warnings or 9 | # no old warnings that aren't issued, which would cause the script to terminate early 10 | 11 | # prereq: zookeeper is checked out in the current directory, with the no-lo, no-ra, no-af branches available locally. 12 | # prereq: run-always-call-on-zookeeper.sh is in the current directory 13 | # prereq: the checker and the Checker Framework are built in the appropriate places 14 | # prereq: the JAVA8_HOME environment variable is set 15 | 16 | run_ablation () { 17 | 18 | variant_name=$1 19 | 20 | cd zookeeper 21 | 22 | git checkout "${variant_name}" 23 | 24 | cd .. 25 | 26 | sh run-always-call-on-zookeeper.sh &> "${variant_name}-results" || true 27 | 28 | echo "new errors produced by ${variant_name} configuration:" 29 | 30 | ./errors-without-custom-types.sh "${variant_name}-results" | grep "error:" | sort | uniq | wc -l 31 | 32 | echo "old errors no longer produced by ${variant_name} configuration:" 33 | 34 | grep "unneeded.suppression" "${variant_name}-results" | wc -l 35 | 36 | echo "the result for the RLC paper's table 3 is the difference between these two numbers plus the number of false positives on the with-annotations branch" 37 | 38 | } 39 | 40 | if [ "x${JAVA8_HOME}" = "x" ]; then 41 | echo "Please set JAVA8_HOME to run the checker on ZooKeeper. ZooKeeper requires Java 8." 42 | exit 1 43 | fi 44 | 45 | run_ablation "no-lo" 46 | 47 | echo "" 48 | 49 | run_ablation "no-ra" 50 | 51 | echo "" 52 | 53 | run_ablation "no-af" 54 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/TryWithResourcesSimple.java: -------------------------------------------------------------------------------- 1 | // A test that try-with-resources variables are always @MustCall({}). 2 | 3 | import java.net.*; 4 | import java.io.*; 5 | import org.checkerframework.checker.mustcall.qual.MustCall; 6 | 7 | public class TryWithResourcesSimple { 8 | static void test(String address, int port) { 9 | try (Socket socket = new Socket(address, port)) { 10 | @MustCall({}) Object s = socket; 11 | } catch (Exception e) { 12 | 13 | } 14 | } 15 | 16 | @SuppressWarnings("mustcall:type.invalid.annotations.on.use") 17 | public static @MustCall({"close", "myMethod"}) Socket getFancySocket() { 18 | return null; 19 | } 20 | 21 | void test_fancy_sock(String address, int port) { 22 | // This is illegal, because getFancySock()'s return type has another MC method beyond "close", 23 | // which is the only MC method for Socket itself. 24 | // :: error: assignment.type.incompatible 25 | try (Socket socket = getFancySocket()) { 26 | @MustCall({}) Object s = socket; 27 | } catch (Exception e) { 28 | 29 | } 30 | } 31 | 32 | static void test_poly(String address, int port) { 33 | try (Socket socket = new Socket(address, port)) { 34 | // getChannel is @MustCallAlias (= poly) with the socket, so it should also be @MC({}) 35 | @MustCall({}) Object s = socket.getChannel(); 36 | } catch (Exception e) { 37 | 38 | } 39 | } 40 | 41 | static void test_two_mca_variables(String address, int port) { 42 | try (Socket socket = new Socket(address, port); 43 | InputStream in = socket.getInputStream()) { 44 | @MustCall({}) Object s = in; 45 | } catch (Exception e) { 46 | 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/AnimalNoSet.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | /** 6 | * Adapted from the standard AutoValue example code: 7 | * https://github.com/google/auto/blob/master/value/userguide/builders.md 8 | */ 9 | @AutoValue 10 | abstract class AnimalNoSet { 11 | abstract String name(); 12 | 13 | abstract @Nullable String habitat(); 14 | 15 | abstract int numberOfLegs(); 16 | 17 | public String getStr() { 18 | return "str"; 19 | } 20 | 21 | static Builder builder() { 22 | return new AutoValue_AnimalNoSet.Builder(); 23 | } 24 | 25 | @AutoValue.Builder 26 | abstract static class Builder { 27 | 28 | abstract Builder name(String value); 29 | 30 | abstract Builder numberOfLegs(int value); 31 | 32 | abstract Builder habitat(String value); 33 | 34 | abstract AnimalNoSet build(); 35 | } 36 | 37 | public static void buildSomethingWrong() { 38 | Builder b = builder(); 39 | b.name("Frank"); 40 | // :: error: finalizer.invocation.invalid 41 | b.build(); 42 | } 43 | 44 | public static void buildSomethingRight() { 45 | Builder b = builder(); 46 | b.name("Frank"); 47 | b.numberOfLegs(4); 48 | b.build(); 49 | } 50 | 51 | public static void buildSomethingRightIncludeOptional() { 52 | Builder b = builder(); 53 | b.name("Frank"); 54 | b.numberOfLegs(4); 55 | b.habitat("jungle"); 56 | b.build(); 57 | } 58 | 59 | public static void buildSomethingWrongFluent() { 60 | // :: error: finalizer.invocation.invalid 61 | builder().name("Frank").build(); 62 | } 63 | 64 | public static void buildSomethingRightFluent() { 65 | builder().name("Jim").numberOfLegs(7).build(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/Postconditions.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | /** 4 | * Test for postcondition support via @EnsureCalledMethods 5 | */ 6 | class Postconditions { 7 | void build(@CalledMethods({"a", "b", "c"}) Postconditions this) { } 8 | 9 | void a() {} 10 | 11 | void b() {} 12 | 13 | void c() {} 14 | 15 | @EnsuresCalledMethods(value = "#1", methods = "b") 16 | static void callB(Postconditions x) { 17 | x.b(); 18 | } 19 | 20 | @EnsuresCalledMethods(value = "#1", methods = "b") 21 | // :: error: contracts.postcondition.not.satisfied 22 | static void doesNotCallB(Postconditions x) { 23 | } 24 | 25 | @EnsuresCalledMethods(value = "#1", methods = {"b","c"}) 26 | static void callBAndC(Postconditions x) { 27 | x.b(); 28 | x.c(); 29 | } 30 | 31 | static void allInOneMethod() { 32 | Postconditions y = new Postconditions(); 33 | y.a(); 34 | y.b(); 35 | y.c(); 36 | y.build(); 37 | } 38 | 39 | static void invokeCallB() { 40 | Postconditions y = new Postconditions(); 41 | y.a(); 42 | callB(y); 43 | y.c(); 44 | y.build(); 45 | } 46 | 47 | static void invokeCallBLast() { 48 | Postconditions y = new Postconditions(); 49 | y.a(); 50 | y.c(); 51 | callB(y); 52 | y.build(); 53 | } 54 | 55 | static void invokeCallBAndC() { 56 | Postconditions y = new Postconditions(); 57 | y.a(); 58 | callBAndC(y); 59 | y.build(); 60 | } 61 | 62 | static void invokeCallBAndCWrong() { 63 | Postconditions y = new Postconditions(); 64 | callBAndC(y); 65 | // :: error: finalizer.invocation.invalid 66 | y.build(); 67 | } 68 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/lombok/LombokDefaultAssignments.java: -------------------------------------------------------------------------------- 1 | // Generated by delombok at Thu Aug 01 14:08:43 PDT 2019 2 | 3 | import java.util.Optional; 4 | 5 | public class LombokDefaultAssignments { 6 | @lombok.NonNull 7 | Optional bar; 8 | 9 | public static class LombokDefaultAssignmentsBuilder { 10 | private Optional bar = Optional.empty(); 11 | 12 | @java.lang.SuppressWarnings("all") 13 | @lombok.Generated 14 | LombokDefaultAssignmentsBuilder() { 15 | } 16 | 17 | @java.lang.SuppressWarnings("all") 18 | @lombok.Generated 19 | public LombokDefaultAssignmentsBuilder bar(@lombok.NonNull final Optional bar) { 20 | if (bar == null) { 21 | throw new java.lang.NullPointerException("bar is marked non-null but is null"); 22 | } 23 | this.bar = bar; 24 | return this; 25 | } 26 | 27 | @java.lang.SuppressWarnings("all") 28 | @lombok.Generated 29 | public LombokDefaultAssignments build() { 30 | return new LombokDefaultAssignments(bar); 31 | } 32 | 33 | @java.lang.Override 34 | @java.lang.SuppressWarnings("all") 35 | @lombok.Generated 36 | public java.lang.String toString() { 37 | return "LombokDefaultAssignments.LombokDefaultAssignmentsBuilder(bar=" + this.bar + ")"; 38 | } 39 | } 40 | 41 | static void test() { 42 | LombokDefaultAssignments.builder().build(); 43 | } 44 | 45 | @java.lang.SuppressWarnings("all") 46 | @lombok.Generated 47 | LombokDefaultAssignments(@lombok.NonNull final Optional bar) { 48 | if (bar == null) { 49 | throw new java.lang.NullPointerException("bar is marked non-null but is null"); 50 | } 51 | this.bar = bar; 52 | } 53 | 54 | @java.lang.SuppressWarnings("all") 55 | @lombok.Generated 56 | public static LombokDefaultAssignmentsBuilder builder() { 57 | return new LombokDefaultAssignmentsBuilder(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/ReassignmentWithMCA.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.security.*; 3 | import org.checkerframework.checker.objectconstruction.qual.*; 4 | import org.checkerframework.checker.calledmethods.qual.*; 5 | 6 | public class ReassignmentWithMCA 7 | { 8 | void testReassignment(File newFile, MessageDigest digester) throws IOException { 9 | FileOutputStream fout = new FileOutputStream(newFile); 10 | DigestOutputStream fos = new DigestOutputStream(fout, digester); 11 | DataOutputStream out = new DataOutputStream(fos); 12 | try { 13 | out = new DataOutputStream(new BufferedOutputStream(fos)); 14 | fout.getChannel(); 15 | } finally { 16 | out.close(); 17 | } 18 | } 19 | 20 | void testReassignmentWithoutMCA(@Owning FileOutputStream fout1, @Owning FileOutputStream fout2, MessageDigest digester) throws IOException { 21 | DigestOutputStream fos1 = new DigestOutputStream(fout1, digester); 22 | DataOutputStream out = new DataOutputStream(fos1); 23 | try { 24 | DigestOutputStream fos2 = new DigestOutputStream(fout2, digester); 25 | out = new DataOutputStream(new BufferedOutputStream(fos2)); 26 | fout1.getChannel(); 27 | } finally { 28 | callClose(fout1); 29 | callClose(fout2); 30 | } 31 | } 32 | 33 | void testReassignmentSetSizeOne(@Owning FilterOutputStream out) throws IOException { 34 | out = new DataOutputStream(out); 35 | out.close(); 36 | } 37 | 38 | @EnsuresCalledMethods(value = "#1", methods = "close") 39 | void callClose(Closeable c) { 40 | try { 41 | if(c!=null){ 42 | c.close(); 43 | } 44 | } catch (IOException e) { 45 | 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/noaccumulationframes/ConnectingServerSockets.java: -------------------------------------------------------------------------------- 1 | // a set of test cases that demonstrate that errors are actually insued in appropriate 2 | // places when ServerSockets are connected 3 | 4 | // This version of the test expects that the accumulation frames support (i.e. the @CreatesObligation annotation 5 | // and its accompanying logic) has been disabled. 6 | 7 | import java.net.*; 8 | 9 | import org.checkerframework.checker.mustcall.qual.*; 10 | 11 | class ConnectingServerSockets { 12 | 13 | static void simple_ss_test(SocketAddress sa) throws Exception { 14 | // :: error: required.method.not.called 15 | ServerSocket s = new ServerSocket(); 16 | s.bind(sa); 17 | } 18 | 19 | static void simple_ss_test2(SocketAddress sa) throws Exception { 20 | // :: error: required.method.not.called 21 | ServerSocket s = new ServerSocket(); 22 | // s.bind(sa); 23 | } 24 | 25 | static void simple_ss_test4(SocketAddress sa, int to) throws Exception { 26 | // :: error: required.method.not.called 27 | ServerSocket s = new ServerSocket(); 28 | s.bind(sa, to); 29 | } 30 | 31 | static @MustCall({}) ServerSocket makeUnconnected() throws Exception { 32 | // :: error: return.type.incompatible 33 | return new ServerSocket(); 34 | } 35 | 36 | static void simple_ss_test5(SocketAddress sa) throws Exception { 37 | ServerSocket s = makeUnconnected(); 38 | s.bind(sa); 39 | } 40 | 41 | static void simple_ss_test6(SocketAddress sa) throws Exception { 42 | ServerSocket s = makeUnconnected(); 43 | // s.bind(sa); 44 | } 45 | 46 | static void simple_ss_test8(SocketAddress sa, int to) throws Exception { 47 | ServerSocket s = makeUnconnected(); 48 | s.bind(sa, to); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/BasicTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | /** 10 | * Test runner that uses the Checker Framework's tooling. 11 | * 12 | *

The test data is located in the "tests/basic" folder (by Checker Framework convention). If 13 | * this test fails, that means that one or more of the Java files in that directory, when run 14 | * through the typechecker, did not produce the expected results. 15 | * 16 | *

When looking at the tests in that folder, lines starting with "// :: error: " are expected 17 | * errors. The test will fail if they are not present. The rest of those lines are error keys, which 18 | * are printed by the typechecker. So, for example, "// :: error: argument.type.incompatible" means 19 | * that an argument type on the following line should be incompatible with a parameter type. 20 | * 21 | *

To add a new test case, create a Java file in that directory. Use the "// :: error: " syntax 22 | * to add any expected warnings. All files ending in .java in that directory will automatically be 23 | * run by this test runner. 24 | * 25 | *

This test runner depends on the Checker Framework's testing library, which is found in the 26 | * Maven artifact org.checkerframework:framework-test. 27 | */ 28 | public class BasicTest extends CheckerFrameworkPerDirectoryTest { 29 | public BasicTest(List testFiles) { 30 | super(testFiles, ObjectConstructionChecker.class, "basic", "-Anomsgtext", "-nowarn"); 31 | } 32 | 33 | @Parameters 34 | public static String[] getTestDirs() { 35 | return new String[] {"basic"}; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/SpecialNames.java: -------------------------------------------------------------------------------- 1 | // This test ensures that special handling for method 2 | // names in DescribeImagesRequest isn't used for other 3 | // classes with the same names. 4 | 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import org.checkerframework.common.returnsreceiver.qual.*; 7 | 8 | class SpecialNames { 9 | @This SpecialNames withFilters() { return this; } 10 | void setFilters() {} 11 | @This SpecialNames withFilters(SpecialNames f) { return this; } 12 | void setFilters(SpecialNames f) {} 13 | @This SpecialNames withName() { return this; } 14 | @This SpecialNames withName(String f) { return this; } 15 | 16 | SpecialNames() { 17 | 18 | } 19 | 20 | SpecialNames(String x) { 21 | 22 | } 23 | 24 | static void test(SpecialNames s) { 25 | // :: error: assignment.type.incompatible 26 | @CalledMethods("withOwners") SpecialNames x = s.withFilters(new SpecialNames().withName("owner")); 27 | } 28 | 29 | static void test2(SpecialNames s) { 30 | s.setFilters(new SpecialNames("owner")); 31 | // :: error: assignment.type.incompatible 32 | @CalledMethods("withOwners") SpecialNames x = s; 33 | } 34 | 35 | static void test3(SpecialNames s) { 36 | // :: error: assignment.type.incompatible 37 | @CalledMethods("withOwners") SpecialNames x = s.withFilters(new SpecialNames().withName("owner")); 38 | } 39 | 40 | static void test4(SpecialNames s) { 41 | s.setFilters(new SpecialNames("owner")); 42 | // :: error: assignment.type.incompatible 43 | @CalledMethods("withOwners") SpecialNames x = s; 44 | } 45 | 46 | static void testForCrashes(SpecialNames s) { 47 | s.setFilters(); 48 | s.withFilters(); 49 | 50 | s.setFilters(new SpecialNames().withName()); 51 | } 52 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/UnparseablePredicate.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | 3 | class UnparseablePredicate { 4 | 5 | // :: error: predicate.invalid 6 | void unclosedOpen(@CalledMethodsPredicate("(foo && bar") Object x) { } 7 | 8 | // :: error: predicate.invalid 9 | void unopenedClose(@CalledMethodsPredicate("foo || bar)") Object x) { } 10 | 11 | // :: error: predicate.invalid 12 | void badKeywords1(@CalledMethodsPredicate("foo OR bar") Object x) { } 13 | 14 | // :: error: predicate.invalid 15 | void badKeywords2(@CalledMethodsPredicate("foo AND bar") Object x) { } 16 | 17 | // These tests check that valid java identifiers don't cause problems 18 | // when evaluating predicates. Examples of identifiers from 19 | // https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.8 20 | 21 | void jls0Example(@CalledMethodsPredicate("String") Object x) { } 22 | void callJls0Example(@CalledMethods("String") Object y) { 23 | jls0Example(y); 24 | } 25 | 26 | void jls1Example(@CalledMethodsPredicate("i3") Object x) { } 27 | void callJls1Example(@CalledMethods("i3") Object y) { 28 | jls1Example(y); 29 | } 30 | 31 | // TODO: support Unicode. SPEL, which we use to parse expressions, doesn't. 32 | /* void jls2Example(@CalledMethodsPredicate("αρετη") Object x) { } 33 | void callJls2Example(@CalledMethods("αρετη") Object y) { 34 | jls2Example(y); 35 | }*/ 36 | 37 | void jls3Example(@CalledMethodsPredicate("MAX_VALUE") Object x) { } 38 | void callJls3Example(@CalledMethods("MAX_VALUE") Object y) { 39 | jls3Example(y); 40 | } 41 | 42 | void jls4Example(@CalledMethodsPredicate("isLetterOrDigit") Object x) { } 43 | void callJls4Example(@CalledMethods("isLetterOrDigit") Object y) { 44 | jls4Example(y); 45 | } 46 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/cve/MorePreciseFilters.java: -------------------------------------------------------------------------------- 1 | import com.amazonaws.services.ec2.model.DescribeImagesRequest; 2 | import com.amazonaws.services.ec2.model.DescribeImagesResult; 3 | import com.amazonaws.services.ec2.model.Filter; 4 | import com.amazonaws.services.ec2.AmazonEC2; 5 | 6 | import java.util.List; 7 | import java.util.Collections; 8 | import java.util.Arrays; 9 | import java.util.ArrayList; 10 | 11 | class MorePreciseFilters { 12 | 13 | /* TODO: handle lists 14 | void ownerAliasList(AmazonEC2 ec2Client) { 15 | DescribeImagesRequest imagesRequest = new DescribeImagesRequest(); 16 | List imageFilters = new ArrayList(); 17 | imageFilters.add(new Filter().withName("owner-alias").withValues("microsoft")); 18 | ec2Client.describeImages(imagesRequest.withFilters(imageFilters)).getImages(); 19 | } 20 | */ 21 | 22 | void withFilterNameInList(AmazonEC2 ec2Client) { 23 | DescribeImagesRequest request = new DescribeImagesRequest(); 24 | request.setFilters(Collections.singletonList( 25 | new Filter().withName("image-id").withValues("12345") 26 | )); 27 | 28 | DescribeImagesResult result = ec2Client.describeImages(request); 29 | } 30 | 31 | void withOwnerId(AmazonEC2 ec2) { 32 | DescribeImagesRequest request = new DescribeImagesRequest() 33 | .withFilters(new Filter("name", Arrays.asList("my_image_name")), 34 | new Filter("owner-id", Arrays.asList("12345"))); 35 | DescribeImagesResult result = ec2.describeImages(request); 36 | } 37 | 38 | void withName(AmazonEC2 ec2Client) { 39 | DescribeImagesRequest request = new DescribeImagesRequest(); 40 | request.withFilters(new Filter().withName("image-id").withValues("12345")); 41 | DescribeImagesResult result = ec2Client.describeImages(request); 42 | } 43 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/BindChannel.java: -------------------------------------------------------------------------------- 1 | // A test for code encountered by Narges. 2 | 3 | import java.nio.channels.*; 4 | import java.io.*; 5 | import java.net.*; 6 | 7 | class BindChannel { 8 | static void test(InetSocketAddress addr, boolean b) { 9 | try { 10 | // This channel is bound - so even with unconnected socket support, we need to 11 | // treat either this channel or the .socket() expression as must-close. 12 | // 13 | // Even though there's now a temporary in the Must Call Checker for the value that 14 | // has the reset method (bind) called on it below, we can't successfully translate 15 | // the reset expression to that temporary, since all we have is a string (from the 16 | // reset annotation) and so we have to go through the type factory's parsing facility, 17 | // which doesn't know about the temporaries and so doesn't return them. We're therefore 18 | // limited to issuing the reset.not.owning error below, 19 | // instead of the preferable required.method.not.called error on this line - as in 20 | // the method below, which extracts the socket into a local variable, which can be 21 | // parsed as an CO target. 22 | ServerSocketChannel httpChannel = ServerSocketChannel.open(); 23 | // :: error: reset.not.owning 24 | httpChannel.socket().bind(addr); 25 | } catch (IOException io) { 26 | 27 | } 28 | } 29 | 30 | static void test_lv(InetSocketAddress addr, boolean b) { 31 | try { 32 | ServerSocketChannel httpChannel = ServerSocketChannel.open(); 33 | // :: error: required.method.not.called 34 | ServerSocket httpSock = httpChannel.socket(); 35 | httpSock.bind(addr); 36 | } catch (IOException io) { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/COAnonymousClass.java: -------------------------------------------------------------------------------- 1 | // Test case for https://github.com/kelloggm/object-construction-checker/issues/368 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | 5 | class COAnonymousClass { 6 | static class Foo { 7 | 8 | @CreatesObligation("this") 9 | void resetFoo() { } 10 | 11 | void other() { 12 | 13 | Runnable r = new Runnable() { 14 | @Override 15 | @CreatesObligation("Foo.this") 16 | // :: error: creates.obligation.override.invalid 17 | public void run() { 18 | // Ideally, we would not issue the following error. However, the Checker Framework's 19 | // JavaExpression support (https://checkerframework.org/manual/#java-expressions-as-arguments) 20 | // treats all versions of "this" (including "Foo.this") as referring to the object 21 | // that directly contains the annotation, so we treat this call to resetFoo as not permitted. 22 | // :: error: reset.not.owning 23 | resetFoo(); 24 | } 25 | }; 26 | call_run(r); 27 | } 28 | 29 | void other2() { 30 | 31 | Runnable r = new Runnable() { 32 | @Override 33 | @CreatesObligation("this") 34 | // :: error: creates.obligation.override.invalid 35 | public void run() { 36 | // This error definitely must be issued, since Foo.this != this. 37 | // :: error: reset.not.owning 38 | resetFoo(); 39 | } 40 | }; 41 | call_run(r); 42 | } 43 | 44 | // If this call to run() were permitted with no errors, this would be unsound. 45 | void call_run(Runnable r) { r.run(); } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /must-call-checker/tests/mustcall/Subtype0.java: -------------------------------------------------------------------------------- 1 | // A test that the @InheritedMustCall declaration annotation works correctly. 2 | 3 | import org.checkerframework.checker.mustcall.qual.*; 4 | import org.checkerframework.checker.objectconstruction.qual.*; 5 | 6 | @InheritableMustCall("a") 7 | public class Subtype0 { 8 | 9 | public class Subtype1 extends Subtype0 { 10 | void m1() { } 11 | } 12 | public class Subtype2 extends Subtype1 { } 13 | 14 | static void test(@Owning Subtype0 s0, @Owning Subtype1 s1, @Owning Subtype2 s2, @Owning Subtype3 s3, @Owning Subtype4 s4) { 15 | // :: error: assignment.type.incompatible 16 | @MustCall({}) Object obj1 = s0; 17 | @MustCall({"a"}) Object obj2 = s0; 18 | 19 | // :: error: assignment.type.incompatible 20 | @MustCall({}) Object obj3 = s1; 21 | @MustCall({"a"}) Object obj4 = s1; 22 | 23 | // :: error: assignment.type.incompatible 24 | @MustCall({}) Object obj5 = s2; 25 | @MustCall({"a"}) Object obj6 = s2; 26 | 27 | @MustCall({}) Object obj7 = s3; 28 | @MustCall({"a"}) Object obj8 = s3; 29 | 30 | @MustCall({}) Object obj9 = s4; 31 | @MustCall({"a"}) Object obj10 = s4; 32 | } 33 | 34 | @MustCall({}) 35 | // :: error: inconsistent.mustcall.subtype :: error: super.invocation.invalid 36 | public class Subtype3 extends Subtype0 { } 37 | 38 | @InheritableMustCall({}) 39 | // :: error: super.invocation.invalid 40 | public class Subtype4 extends Subtype0 { } 41 | 42 | @MustCall({"a"}) 43 | public class Subtype5 extends Subtype0 { } 44 | 45 | @InheritableMustCall({"a"}) 46 | public class Subtype6 extends Subtype0 { } 47 | 48 | public class Container { 49 | Subtype0 in; 50 | 51 | void test() { 52 | if (in instanceof Subtype1) { 53 | ((Subtype1) in).m1(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/ZookeeperByteBufferInputStream.java: -------------------------------------------------------------------------------- 1 | // This is a test that the correct errors are thrown on this class, which is a copy of one defined 2 | // by Zookeeper. Earlier versions of our code to handle this issued many duplicate inconsistent.mustcall.subtype 3 | // errors. This test case doesn't actually test for that, but it's useful for debugging and as a regression 4 | // test that at least one error is still issued. 5 | 6 | import java.io.*; 7 | import java.nio.ByteBuffer; 8 | 9 | import org.checkerframework.checker.mustcall.qual.MustCall; 10 | 11 | @MustCall({}) 12 | // :: error: inconsistent.mustcall.subtype 13 | public class ZookeeperByteBufferInputStream extends InputStream { 14 | 15 | ByteBuffer bb; 16 | 17 | // :: error: super.invocation.invalid 18 | public ZookeeperByteBufferInputStream(ByteBuffer bb) { 19 | this.bb = bb; 20 | } 21 | 22 | @Override 23 | public int read() throws IOException { 24 | if (bb.remaining() == 0) { 25 | return -1; 26 | } 27 | return bb.get() & 0xff; 28 | } 29 | 30 | @Override 31 | public int available() throws IOException { 32 | return bb.remaining(); 33 | } 34 | 35 | @Override 36 | public int read(byte[] b, int off, int len) throws IOException { 37 | if (bb.remaining() == 0) { 38 | return -1; 39 | } 40 | if (len > bb.remaining()) { 41 | len = bb.remaining(); 42 | } 43 | bb.get(b, off, len); 44 | return len; 45 | } 46 | 47 | @Override 48 | public int read(byte[] b) throws IOException { 49 | return read(b, 0, b.length); 50 | } 51 | 52 | @Override 53 | public long skip(long n) throws IOException { 54 | if (n < 0L) { 55 | return 0; 56 | } 57 | n = Math.min(n, bb.remaining()); 58 | bb.position(bb.position() + (int) n); 59 | return n; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /object-construction-checker/tests/mustcall/MustCallAliasPassthroughChain.java: -------------------------------------------------------------------------------- 1 | // This test checks that a chain of several MCA methods can be verified, and that messing up the chain 2 | // leads to errors. 3 | 4 | import org.checkerframework.checker.mustcall.qual.*; 5 | import org.checkerframework.checker.calledmethods.qual.*; 6 | import org.checkerframework.checker.objectconstruction.qual.*; 7 | import java.io.*; 8 | 9 | class MustCallAliasPassthroughChain { 10 | 11 | static @MustCallAlias InputStream withMCA(@MustCallAlias InputStream is) { 12 | return is; 13 | } 14 | 15 | static @MustCallAlias InputStream chain1(@MustCallAlias InputStream is) { 16 | return withMCA(is); 17 | } 18 | 19 | static @MustCallAlias InputStream chain2(@MustCallAlias InputStream is) { 20 | InputStream s = withMCA(is); 21 | return s; 22 | } 23 | 24 | static @MustCallAlias InputStream chain3(@MustCallAlias InputStream is) { 25 | return withMCA(chain1(is)); 26 | } 27 | 28 | static @MustCallAlias InputStream chain4(@MustCallAlias InputStream is) { 29 | return withMCA(chain1(chain3(is))); 30 | } 31 | 32 | static @MustCallAlias InputStream chain5(@MustCallAlias InputStream is) { 33 | InputStream s = withMCA(chain1(is)); 34 | return s; 35 | } 36 | 37 | // :: error: required.method.not.called 38 | static @MustCallAlias InputStream chain_bad1(@MustCallAlias InputStream is) { 39 | InputStream s = withMCA(chain1(is)); 40 | return null; 41 | } 42 | 43 | // :: error: required.method.not.called 44 | static @MustCallAlias InputStream chain_bad2(@MustCallAlias InputStream is) { 45 | withMCA(chain1(is)); 46 | return null; 47 | } 48 | 49 | // :: error: required.method.not.called 50 | static @MustCallAlias InputStream chain_bad3(@MustCallAlias InputStream is, boolean b) { 51 | return b ? null : withMCA(chain1(is)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /object-construction-checker/tests/noaccumulationframes/ConnectingSockets.java: -------------------------------------------------------------------------------- 1 | // a set of test cases that demonstrate that errors are actually issued in appropriate 2 | // places when Sockets are connected 3 | 4 | import java.net.*; 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class ConnectingSockets { 9 | 10 | static void simple_ns_test(SocketAddress sa) throws Exception { 11 | // :: error: required.method.not.called 12 | Socket s = new Socket(); 13 | s.bind(sa); 14 | } 15 | 16 | static void simple_ns_test2(SocketAddress sa) throws Exception { 17 | // :: error: required.method.not.called 18 | Socket s = new Socket(); 19 | // s.bind(sa); 20 | } 21 | 22 | static void simple_ns_test3(SocketAddress sa) throws Exception { 23 | // :: error: required.method.not.called 24 | Socket s = new Socket(); 25 | s.connect(sa); 26 | } 27 | 28 | static void simple_ns_test4(SocketAddress sa, int to) throws Exception { 29 | // :: error: required.method.not.called 30 | Socket s = new Socket(); 31 | s.connect(sa, to); 32 | } 33 | 34 | static @MustCall({}) Socket makeUnconnected() throws Exception { 35 | // :: error: return.type.incompatible 36 | return new Socket(); 37 | } 38 | 39 | static void simple_ns_test5(SocketAddress sa) throws Exception { 40 | Socket s = makeUnconnected(); 41 | s.bind(sa); 42 | } 43 | 44 | static void simple_ns_test6(SocketAddress sa) throws Exception { 45 | Socket s = makeUnconnected(); 46 | // s.bind(sa); 47 | } 48 | 49 | static void simple_ns_test7(SocketAddress sa) throws Exception { 50 | Socket s = makeUnconnected(); 51 | s.connect(sa); 52 | } 53 | 54 | static void simple_ns_test8(SocketAddress sa, int to) throws Exception { 55 | Socket s = makeUnconnected(); 56 | s.connect(sa, to); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /object-construction-checker/tests/autovalue/GetAnimal.java: -------------------------------------------------------------------------------- 1 | import com.google.auto.value.AutoValue; 2 | import org.checkerframework.checker.calledmethods.qual.*; 3 | import org.checkerframework.checker.nullness.qual.*; 4 | 5 | /** 6 | * Adapted from the standard AutoValue example code: 7 | * https://github.com/google/auto/blob/master/value/userguide/builders.md 8 | */ 9 | @AutoValue 10 | abstract class GetAnimal { 11 | abstract String getName(); 12 | 13 | abstract @Nullable String getHabitat(); 14 | 15 | abstract int getNumberOfLegs(); 16 | 17 | abstract boolean isHasArms(); 18 | 19 | static Builder builder() { 20 | return new AutoValue_GetAnimal.Builder(); 21 | } 22 | 23 | @AutoValue.Builder 24 | abstract static class Builder { 25 | 26 | abstract Builder setName(String value); 27 | 28 | abstract Builder setNumberOfLegs(int value); 29 | 30 | abstract Builder setHabitat(String value); 31 | 32 | abstract Builder setHasArms(boolean b); 33 | 34 | abstract GetAnimal build(); 35 | } 36 | 37 | public static void buildSomethingWrong() { 38 | Builder b = builder(); 39 | b.setName("Frank"); 40 | // :: error: finalizer.invocation.invalid 41 | b.build(); 42 | } 43 | 44 | public static void buildSomethingRight() { 45 | Builder b = builder(); 46 | b.setName("Frank"); 47 | b.setNumberOfLegs(4); 48 | b.setHasArms(true); 49 | b.build(); 50 | } 51 | 52 | public static void buildSomethingRightIncludeOptional() { 53 | Builder b = builder(); 54 | b.setName("Frank"); 55 | b.setNumberOfLegs(4); 56 | b.setHabitat("jungle"); 57 | b.setHasArms(true); 58 | b.build(); 59 | } 60 | 61 | public static void buildSomethingWrongFluent() { 62 | // :: error: finalizer.invocation.invalid 63 | builder().setName("Frank").build(); 64 | } 65 | 66 | public static void buildSomethingRightFluent() { 67 | builder().setName("Jim").setNumberOfLegs(7).setHasArms(false).build(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /object-construction-checker/src/test/java/tests/LombokTest.java: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import org.checkerframework.checker.objectconstruction.ObjectConstructionChecker; 6 | import org.checkerframework.framework.test.CheckerFrameworkPerDirectoryTest; 7 | import org.junit.runners.Parameterized.Parameters; 8 | 9 | /** 10 | * Test runner that uses the Checker Framework's tooling. 11 | * 12 | *

The test data is located in the "tests/basic" folder (by Checker Framework convention). If 13 | * this test fails, that means that one or more of the Java files in that directory, when run 14 | * through the typechecker, did not produce the expected results. 15 | * 16 | *

When looking at the tests in that folder, lines starting with "// :: error: " are expected 17 | * errors. The test will fail if they are not present. The rest of those lines are error keys, which 18 | * are printed by the typechecker. So, for example, "// :: error: argument.type.incompatible" means 19 | * that an argument type on the following line should be incompatible with a parameter type. 20 | * 21 | *

To add a new test case, create a Java file in that directory. Use the "// :: error: " syntax 22 | * to add any expected warnings. All files ending in .java in that directory will automatically be 23 | * run by this test runner. 24 | * 25 | *

This test runner depends on the Checker Framework's testing library, which is found in the 26 | * Maven artifact org.checkerframework:framework-test. 27 | */ 28 | public class LombokTest extends CheckerFrameworkPerDirectoryTest { 29 | public LombokTest(List testFiles) { 30 | super( 31 | testFiles, 32 | ObjectConstructionChecker.class, 33 | "lombok", 34 | "-Anomsgtext", 35 | "-nowarn", 36 | "-AsuppressWarnings=type.anno.before.modifier"); 37 | } 38 | 39 | @Parameters 40 | public static String[] getTestDirs() { 41 | return new String[] {"lombok"}; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /object-construction-checker/tests/basic/SimpleFluentInference.java: -------------------------------------------------------------------------------- 1 | import org.checkerframework.checker.calledmethods.qual.*; 2 | import org.checkerframework.common.returnsreceiver.qual.*; 3 | 4 | /* Simple inference of a fluent builder */ 5 | class SimpleFluentInference { 6 | SimpleFluentInference build(@CalledMethods({"a", "b"}) SimpleFluentInference this) { return this; } 7 | SimpleFluentInference weakbuild(@CalledMethods({"a"}) SimpleFluentInference this) { return this; } 8 | 9 | @This SimpleFluentInference a() { return this; } 10 | 11 | @This SimpleFluentInference b() { return this; } 12 | 13 | // intentionally does not have an @This annotation 14 | SimpleFluentInference c() { return new SimpleFluentInference(); } 15 | 16 | static void doStuffCorrect() { 17 | SimpleFluentInference s = new SimpleFluentInference() 18 | .a() 19 | .b() 20 | .build(); 21 | } 22 | 23 | static void doStuffWrong() { 24 | SimpleFluentInference s = new SimpleFluentInference() 25 | .a() 26 | // :: error: finalizer.invocation.invalid 27 | .build(); 28 | } 29 | 30 | static void doStuffRightWeak() { 31 | SimpleFluentInference s = new SimpleFluentInference() 32 | .a() 33 | .weakbuild(); 34 | } 35 | 36 | static void noReturnsReceiverAnno() { 37 | SimpleFluentInference s = new SimpleFluentInference() 38 | .a() 39 | .b() 40 | .c() 41 | // :: error: finalizer.invocation.invalid 42 | .build(); 43 | } 44 | 45 | static void fluentLoop() { 46 | SimpleFluentInference s = new SimpleFluentInference().a(); 47 | int i = 10; 48 | while (i > 0) { 49 | // :: error: finalizer.invocation.invalid 50 | s.b().build(); 51 | i--; 52 | s = new SimpleFluentInference(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /object-construction-checker/tests/socket/ConnectingSockets.java: -------------------------------------------------------------------------------- 1 | // a set of test cases that demonstrate that errors are actually insued in appropriate 2 | // places when Sockets are connected 3 | 4 | import java.net.*; 5 | 6 | import org.checkerframework.checker.mustcall.qual.*; 7 | 8 | class ConnectingSockets { 9 | 10 | static void simple_ns_test(SocketAddress sa) throws Exception { 11 | // :: error: required.method.not.called 12 | Socket s = new Socket(); 13 | s.bind(sa); 14 | } 15 | 16 | static void simple_ns_test2(SocketAddress sa) throws Exception { 17 | Socket s = new Socket(); 18 | // s.bind(sa); 19 | } 20 | 21 | static void simple_ns_test3(SocketAddress sa) throws Exception { 22 | // :: error: required.method.not.called 23 | Socket s = new Socket(); 24 | s.connect(sa); 25 | } 26 | 27 | static void simple_ns_test4(SocketAddress sa, int to) throws Exception { 28 | // :: error: required.method.not.called 29 | Socket s = new Socket(); 30 | s.connect(sa, to); 31 | } 32 | 33 | static @MustCall({}) Socket makeUnconnected() throws Exception { 34 | return new Socket(); 35 | } 36 | 37 | static void simple_ns_test5(SocketAddress sa) throws Exception { 38 | // :: error: required.method.not.called 39 | Socket s = makeUnconnected(); 40 | s.bind(sa); 41 | } 42 | 43 | static void simple_ns_test6(SocketAddress sa) throws Exception { 44 | Socket s = makeUnconnected(); 45 | // s.bind(sa); 46 | } 47 | 48 | static void simple_ns_test7(SocketAddress sa) throws Exception { 49 | // :: error: required.method.not.called 50 | Socket s = makeUnconnected(); 51 | s.connect(sa); 52 | } 53 | 54 | static void simple_ns_test8(SocketAddress sa, int to) throws Exception { 55 | // :: error: required.method.not.called 56 | Socket s = makeUnconnected(); 57 | s.connect(sa, to); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /object-construction-checker/tests/lombok/DefaultedName.java: -------------------------------------------------------------------------------- 1 | // Generated by delombok at Fri Jul 26 12:44:07 PDT 2019 2 | 3 | class DefaultedName { 4 | @lombok.NonNull 5 | String name; 6 | 7 | static void test1() { 8 | builder().build(); 9 | } 10 | 11 | static void test2(Object foo) { 12 | DefaultedNameBuilder b = builder(); 13 | if (foo != null) { 14 | b.name(foo.toString()); 15 | } 16 | b.build(); 17 | } 18 | 19 | @java.lang.SuppressWarnings("all") 20 | private static String $default$name() { 21 | return "Martin"; 22 | } 23 | 24 | @java.lang.SuppressWarnings("all") 25 | DefaultedName(@lombok.NonNull final String name) { 26 | if (name == null) { 27 | throw new java.lang.NullPointerException("name is marked non-null but is null"); 28 | } 29 | this.name = name; 30 | } 31 | 32 | 33 | @java.lang.SuppressWarnings("all") 34 | public static class DefaultedNameBuilder { 35 | @java.lang.SuppressWarnings("all") 36 | private boolean name$set; 37 | @java.lang.SuppressWarnings("all") 38 | private String name; 39 | 40 | @java.lang.SuppressWarnings("all") 41 | DefaultedNameBuilder() { 42 | } 43 | 44 | @java.lang.SuppressWarnings("all") 45 | public DefaultedNameBuilder name(@lombok.NonNull final String name) { 46 | if (name == null) { 47 | throw new java.lang.NullPointerException("name is marked non-null but is null"); 48 | } 49 | this.name = name; 50 | name$set = true; 51 | return this; 52 | } 53 | 54 | @java.lang.SuppressWarnings("all") 55 | public DefaultedName build() { 56 | String name = this.name; 57 | if (!name$set) name = DefaultedName.$default$name(); 58 | return new DefaultedName(name); 59 | } 60 | 61 | @java.lang.Override 62 | @java.lang.SuppressWarnings("all") 63 | public java.lang.String toString() { 64 | return "DefaultedName.DefaultedNameBuilder(name=" + this.name + ")"; 65 | } 66 | } 67 | 68 | @java.lang.SuppressWarnings("all") 69 | public static DefaultedNameBuilder builder() { 70 | return new DefaultedNameBuilder(); 71 | } 72 | } 73 | --------------------------------------------------------------------------------