Skip to content

Commit

Permalink
Merge branch 'master' into custom-list-of-ignored-rlc-exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Calvin-L committed Oct 27, 2023
2 parents 0e0fa16 + d282234 commit d41e33f
Show file tree
Hide file tree
Showing 60 changed files with 2,027 additions and 200 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
// https://github.com/tbroyer/gradle-errorprone-plugin
id 'net.ltgt.errorprone' version '3.1.0'
// https://plugins.gradle.org/plugin/org.ajoberstar.grgit
id 'org.ajoberstar.grgit' version '5.2.0' apply false
id 'org.ajoberstar.grgit' version '5.2.1' apply false

id 'groovy' // needed for formatting Gradle files
// Code formatting; defines targets "spotlessApply" and "spotlessCheck".
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
@PostconditionAnnotation(qualifier = CalledMethods.class)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Repeatable(EnsuresCalledMethods.List.class)
@InheritedAnnotation
public @interface EnsuresCalledMethods {
/**
* The Java expressions that will have methods called on them.
Expand Down Expand Up @@ -72,6 +73,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@InheritedAnnotation
@PostconditionAnnotation(qualifier = CalledMethods.class)
public static @interface List {
/**
* Return the repeatable annotations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand All @@ -22,6 +23,7 @@
@Retention(RetentionPolicy.RUNTIME)
@PreconditionAnnotation(qualifier = CalledMethods.class)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Repeatable(RequiresCalledMethods.List.class)
public @interface RequiresCalledMethods {
/**
* The Java expressions that must have had methods called on them.
Expand All @@ -48,6 +50,7 @@
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@PreconditionAnnotation(qualifier = CalledMethods.class)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public static @interface List {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.checkerframework.checker.optional.qual;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.checkerframework.framework.qual.InheritedAnnotation;
import org.checkerframework.framework.qual.PostconditionAnnotation;

/**
* Indicates that the expression evaluates to a non-empty Optional, if the method terminates
* successfully.
*
* <p>This postcondition annotation is useful for methods that construct a non-empty Optional:
*
* <pre><code>
* {@literal @}EnsuresPresent("optStr")
* void initialize() {
* optStr = Optional.of("abc");
* }
* </code></pre>
*
* It can also be used for a method that fails if a given Optional value is empty, indicating that
* the argument is null if the method returns normally:
*
* <pre><code>
* /** Throws an exception if the argument is empty. *&#47;
* {@literal @}EnsuresPresent("#1")
* void useTheOptional(Optional&lt;T&gt; arg) { ... }
* </code></pre>
*
* @see Present
* @see org.checkerframework.checker.optional.OptionalChecker
* @checker_framework.manual #optional-checker Optional Checker
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@PostconditionAnnotation(qualifier = Present.class)
@InheritedAnnotation
public @interface EnsuresPresent {
/**
* The expression (of Optional type) that is present, if the method returns normally.
*
* @return the expression (of Optional type) that is present, if the method returns normally
*/
String[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private NullnessUtil() {
*
* // one way to use as a statement:
* castNonNull(possiblyNull3);
* possiblyNull3.toString();`
* possiblyNull3.toString();
* </code></pre>
*
* The {@code castNonNull} method is intended to be used in situations where the programmer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.checkerframework.checker.optional.util;

import java.util.Optional;
import org.checkerframework.checker.optional.qual.EnsuresPresent;
import org.checkerframework.checker.optional.qual.MaybePresent;
import org.checkerframework.checker.optional.qual.Present;
import org.checkerframework.framework.qual.AnnotatedFor;

/**
* This is a utility class for the Optional Checker.
*
* <p>To avoid the need to write the OptionalUtil class name, do:
*
* <pre>import static org.checkerframework.checker.optional.util.OptionalUtil.castPresent;</pre>
*
* or
*
* <pre>import static org.checkerframework.checker.optional.util.OptionalUtil.*;</pre>
*
* <p><b>Runtime Dependency</b>: If you use this class, you must distribute (or link to) {@code
* checker-qual.jar}, along with your binaries. Or, you can copy this class into your own project.
*/
@SuppressWarnings({
"optional", // Optional utilities are trusted regarding the Optional type.
"cast" // Casts look redundant if Optional Checker is not run.
})
@AnnotatedFor("optional")
public final class OptionalUtil {

/** The OptionalUtil class should not be instantiated. */
private OptionalUtil() {
throw new AssertionError("do not instantiate");
}

/**
* A method that suppresses warnings from the Optional Checker.
*
* <p>The method takes a possibly-empty Optional reference, unsafely casts it to have the @Present
* type qualifier, and returns it. The Optional Checker considers both the return value, and also
* the argument, to be present after the method call. Therefore, the {@code castPresent} method
* can be used either as a cast expression or as a statement.
*
* <pre><code>
* // one way to use as a cast:
* {@literal @}Present String s = castPresent(possiblyEmpty1);
*
* // another way to use as a cast:
* castPresent(possiblyEmpty2).toString();
*
* // one way to use as a statement:
* castPresent(possiblyEmpty3);
* possiblyEmpty3.toString();
* </code></pre>
*
* The {@code castPresent} method is intended to be used in situations where the programmer
* definitively knows that a given Optional reference is present, but the type system is unable to
* make this deduction. It is not intended for defensive programming, in which a programmer cannot
* prove that the value is not empty but wishes to have an earlier indication if it is. See the
* Checker Framework Manual for further discussion.
*
* <p>The method throws {@link AssertionError} if Java assertions are enabled and the argument is
* empty. If the exception is ever thrown, then that indicates that the programmer misused the
* method by using it in a circumstance where its argument can be empty.
*
* @param <T> the type of content of the Optional
* @param ref an Optional reference of @MaybePresent type, that is present at run time
* @return the argument, casted to have the type qualifier @Present
*/
@EnsuresPresent("#1")
public static <T extends @MaybePresent Object> @Present Optional<T> castPresent(
@MaybePresent Optional<T> ref) {
assert ref.isPresent() : "Misuse of castPresent: called with an empty Optional";
return (@Present Optional<T>) ref;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.checkerframework.checker.optional.util;

import java.util.Optional;
import org.checkerframework.checker.optional.qual.Present;
import org.junit.Assert;
import org.junit.Test;

public final class OptionalUtilTest {

@Test
public void test_castPresent() {

Optional<String> nonEmptyOpt = Optional.of("non-empty");
Optional<String> emptyOpt = Optional.empty();

Assert.assertFalse(nonEmptyOpt.isEmpty());
@Present Optional<String> foo = OptionalUtil.castPresent(nonEmptyOpt);
Assert.assertEquals(foo.get(), "non-empty");

Assert.assertTrue(emptyOpt.isEmpty());
Assert.assertThrows(Error.class, () -> OptionalUtil.castPresent(emptyOpt));
}
}
5 changes: 3 additions & 2 deletions checker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ dependencies {
// For the Resource Leak Checker's support for JavaEE.
testImplementation 'javax.servlet:javax.servlet-api:4.0.1'
// For the Resource Leak Checker's support for IOUtils.
testImplementation 'commons-io:commons-io:2.14.0'
testImplementation 'commons-io:commons-io:2.15.0'

testImplementation group: 'junit', name: 'junit', version: '4.13.2'
testImplementation project(':framework-test')
Expand Down Expand Up @@ -910,7 +910,8 @@ task wpiManyTest(group: 'Verification') {
'-i', "${project.projectDir}/tests/wpi-many/testin.txt",
'-o', "${project.projectDir}/build/wpi-many-tests",
'-s',
'--', '--checker', 'nullness,interning,lock,regex,signature,calledmethods,resourceleak'
'--', '--checker', 'nullness,interning,lock,regex,signature,calledmethods,resourceleak',
'--extraJavacArgs=-AenableWpiForRlc'
}
} catch (Exception e) {
println('Failure: Running wpi-many.sh failed with a non-zero exit code.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.calledmethods.builder.AutoValueSupport;
Expand Down Expand Up @@ -39,6 +40,7 @@
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.checkerframework.javacutil.UserError;

/** The annotated type factory for the Called Methods Checker. */
Expand Down Expand Up @@ -428,4 +430,21 @@ private AnnotationMirror ensuresCMAnno(String[] expressions, List<String> called
AnnotationMirror am = builder.build();
return am;
}

/**
* Returns true if the checker should ignore exceptional control flow due to the given exception
* type.
*
* @param exceptionType exception type
* @return {@code true} if {@code exceptionType} is a member of {@link
* CalledMethodsAnalysis#ignoredExceptionTypes}, {@code false} otherwise
*/
@Override
public boolean isIgnoredExceptionType(TypeMirror exceptionType) {
if (exceptionType.getKind() == TypeKind.DECLARED) {
return CalledMethodsAnalysis.ignoredExceptionTypes.contains(
TypesUtils.getQualifiedName((DeclaredType) exceptionType));
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.checkerframework.checker.calledmethods;

import com.sun.source.tree.Tree;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
Expand All @@ -11,6 +12,7 @@
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.calledmethods.qual.EnsuresCalledMethodsVarArgs;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.resourceleak.ResourceLeakChecker;
import org.checkerframework.common.accumulation.AccumulationStore;
import org.checkerframework.common.accumulation.AccumulationTransfer;
import org.checkerframework.common.accumulation.AccumulationValue;
Expand Down Expand Up @@ -50,6 +52,12 @@ public class CalledMethodsTransfer extends AccumulationTransfer {
*/
private final ExecutableElement calledMethodsValueElement;

/**
* True if -AenableWpiForRlc was passed on the command line. See {@link
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
*/
private final boolean enableWpiForRlc;

/**
* Create a new CalledMethodsTransfer.
*
Expand All @@ -59,6 +67,40 @@ public CalledMethodsTransfer(CalledMethodsAnalysis analysis) {
super(analysis);
calledMethodsValueElement =
((CalledMethodsAnnotatedTypeFactory) atypeFactory).calledMethodsValueElement;
enableWpiForRlc = atypeFactory.getChecker().hasOption(ResourceLeakChecker.ENABLE_WPI_FOR_RLC);
}

/**
* @param tree a tree
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
* -AenableWpiForRlc flag (see {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}) is not passed
* as a command line argument, otherwise returns the result of the super call
*/
@Override
protected boolean shouldPerformWholeProgramInference(Tree tree) {
if (!isWpiEnabledForRLC()
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
return false;
}
return super.shouldPerformWholeProgramInference(tree);
}

/**
* See {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
*
* @param expressionTree a tree
* @param lhsTree its element
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
* -AenableWpiForRlc flag is not passed as a command line argument, otherwise returns the
* result of the super call
*/
@Override
protected boolean shouldPerformWholeProgramInference(Tree expressionTree, Tree lhsTree) {
if (!isWpiEnabledForRLC()
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
return false;
}
return super.shouldPerformWholeProgramInference(expressionTree, lhsTree);
}

@Override
Expand Down Expand Up @@ -229,4 +271,14 @@ private void handleEnsuresCalledMethodsVarArgs(

return atypeFactory.createAccumulatorAnnotation(newList);
}

/**
* Checks if WPI is enabled for the Resource Leak Checker inference. See {@link
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
*
* @return returns true if WPI is enabled for the Resource Leak Checker
*/
protected boolean isWpiEnabledForRLC() {
return enableWpiForRlc;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.checkerframework.checker.index.samelen;

import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.qual.RelevantJavaTypes;
import org.checkerframework.framework.source.SuppressWarningsPrefix;

/**
Expand All @@ -10,7 +9,9 @@
*
* @checker_framework.manual #index-checker Index Checker
*/
@RelevantJavaTypes({CharSequence.class, Object[].class, Object.class})
// This @RelevantJavaTypes annotation is incorrect, because @SameLen can apply to an arbitrary
// user-defined datatype: https://checkerframework.org/manual/#index-annotating-fixed-size .
// @RelevantJavaTypes({CharSequence.class, Object[].class, Object.class})
@SuppressWarningsPrefix({"index", "samelen"})
public class SameLenChecker extends BaseTypeChecker {
/** Create a new SameLenChecker. */
Expand Down

0 comments on commit d41e33f

Please sign in to comment.