Skip to content

Commit

Permalink
Try adding the JSpecify annotations to see if it helps with CI.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpovirk committed Oct 7, 2024
1 parent 694ca99 commit ba27da4
Show file tree
Hide file tree
Showing 6 changed files with 487 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/java.base/share/classes/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
exports javax.security.auth.spi;
exports javax.security.auth.x500;
exports javax.security.cert;
exports org.jspecify.annotations;


// additional qualified exports may be inserted at build time
Expand Down
96 changes: 96 additions & 0 deletions src/java.base/share/classes/org/jspecify/annotations/NonNull.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2022 The JSpecify Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jspecify.annotations;

import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Indicates that the annotated <a href="https://github.com/jspecify/jspecify/wiki/type-usages">type
* usage</a> (commonly a parameter type or return type) is considered to <i>exclude</i> {@code null}
* as a value; rarely needed within {@linkplain NullMarked null-marked} code.
*
* <p>This annotation serves two primary purposes:
*
* <ul>
* <li>To mark any sporadic non-null type usages inside a scope that is not ready to be fully
* {@link NullMarked} yet.
* <li>To perform a <i>non-null projection</i> of a type variable, explained below.
* </ul>
*
* <p>For a comprehensive introduction to JSpecify, please see <a
* href="http://jspecify.org">jspecify.org</a>.
*
* <h2 id="projection">Non-null projection</h2>
*
* <p>In the following example, {@code MyOptional}'s type parameter {@code T} accepts only non-null
* type arguments, but {@code MyList}'s type parameter {@code E} will accept either a non-null or
* nullable type argument.
*
* <pre>{@code
* // All the below is null-marked code
*
* class MyOptional<T> { … }
*
* interface MyList<E extends @Nullable Object> {
* // Returns the first non-null element, if such element exists.
* MyOptional<E> firstNonNull() { … } // problem here!
* }
*
* MyList<@Nullable String> maybeNulls = …
* MyList<String> nonNulls = …
* }</pre>
*
* <p>Because {@code MyOptional} accepts only non-null type arguments, we need both {@code
* maybeNulls.firstNonNull()} and {@code nonNulls.firstNonNull()} to produce the same return type:
* {@code MyOptional!<String!>} (see <a
* href="https://github.com/jspecify/jspecify/wiki/notation#shorthand-notation">notation</a>).
* However, as specified above, they won't do that. In fact, there is a problem with the {@code
* firstNonNull} signature, since the type argument {@code String?} would not meet the requirements
* of {@code MyOptional}'s type parameter.
*
* <p>The solution is to <b>project</b> the type argument to its non-null counterpart:
*
* <pre>{@code
* // Returns the first non-null element, if such element exists.
* MyOptional<@NonNull E> firstNonNull() { … } // problem fixed!
* }</pre>
*
* <p>Here, {@code @NonNull E} selects the non-null form of the type argument, whether it was
* already non-null or not, which is just what we need in this scenario.
*
* <p>If {@code E} has a non-null upper bound, then the apparent projection {@code @NonNull E} is
* redundant but harmless.
*
* <p><a href="Nullable.html#projection">Nullable projection</a> serves the equivalent purpose in
* the opposite direction, and is far more commonly useful.
*
* <p>If a type variable has <i>all</i> its usages being projected in one direction or the other, it
* should be given a non-null upper bound, and any non-null projections can then be removed.
*
* <h2>Where it is applicable</h2>
*
* <p>{@code @NonNull} is applicable in all the <a href="Nullable.html#applicability">same
* locations</a> as {@link Nullable}.
*/
@Documented
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface NonNull {}
112 changes: 112 additions & 0 deletions src/java.base/share/classes/org/jspecify/annotations/NullMarked.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2018-2020 The JSpecify Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jspecify.annotations;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Indicates that the annotated element and the code transitively {@linkplain
* javax.lang.model.element.Element#getEnclosedElements() enclosed} within it are <b>null-marked
* code</b>: there, type usages are generally considered to exclude {@code null} as a value unless
* specified otherwise. Using this annotation avoids the need to write {@link NonNull @NonNull} many
* times throughout your code.
*
* <p>For a comprehensive introduction to JSpecify, please see <a
* href="http://jspecify.org">jspecify.org</a>.
*
* <h2 id="effects">Effects of being null-marked</h2>
*
* <p>Within null-marked code, as a <i>general</i> rule, a type usage is considered non-null (to
* exclude {@code null} as a value) unless explicitly annotated as {@link Nullable}. However, there
* are several special cases to address.
*
* <h3 id="effects-special-cases">Special cases</h3>
*
* <p>Within null-marked code:
*
* <ul>
* <li>We might expect the type represented by a <b>wildcard</b> (like the {@code ?} in {@code
* List<?>}) to be non-null, but it isn't necessarily. It's non-null only if it {@code
* extends} a non-null type (like in {@code List<? extends String>}), or if the <i>class</i>
* in use accepts only non-null type arguments (such as if {@code List} were declared as
* {@code class List<E extends String>}). But if {@code List} does accept nullable type
* arguments, then the wildcards seen in {@code List<?>} and {@code List<? super String>} must
* include {@code null}, because they have no "upper bound". (<a
* href="https://bit.ly/3ppb8ZC">Why?</a>)
* <ul>
* <li>Conversely, a <b>type parameter</b> is always considered to have an upper bound; when
* none is given explicitly, {@code Object} is filled in by the compiler. The example
* {@code class MyList<E>} is interpreted identically to {@code class MyList<E extends
* Object>}: in both cases the type argument in {@code MyList<@Nullable Foo>} is
* out-of-bounds, so the list elements are always non-null. (<a
* href="https://bit.ly/3ppb8ZC">Why?</a>)
* </ul>
* <li>Otherwise, being null-marked has no consequence for any type usage where {@code @Nullable}
* and {@code @NonNull} are <a href="Nullable.html#applicability"><b>not applicable</b></a>,
* such as the root type of a local variable declaration.
* <li>When a type variable has a nullable upper bound, such as the {@code E} in {@code class
* Foo<E extends @Nullable Bar>}), an unannotated usage of this type variable is not
* considered nullable, non-null, or even of "unspecified" nullness. Rather it has
* <b>parametric nullness</b>. In order to support both nullable and non-null type arguments
* safely, the {@code E} type itself must be handled <i>strictly</i>: as if nullable when
* "read from", but as if non-null when "written to". (Contrast with {@code class Foo<E
* extends Bar>}, where usages of {@code E} are simply non-null, just like usages of {@code
* String} are.)
* <li>By using {@link NullUnmarked}, an element within null-marked code can be excluded and made
* null-unmarked, exactly as if there were no enclosing {@code @NullMarked} element at all.
* </ul>
*
* <h2 id="where">Where it can be used</h2>
*
* {@code @NullMarked} and {@link NullUnmarked @NullUnmarked} can be used on any package, class,
* method, or constructor declaration; {@code @NullMarked} can be used on a module declaration as
* well. Special considerations:
*
* <ul>
* <li>To apply this annotation to an entire (single) <b>package</b>, create a <a
* href="https://docs.oracle.com/javase/specs/jls/se22/html/jls-7.html#jls-7.4.1">{@code
* package-info.java}</a> file there. This is recommended so that newly-created classes will
* be null-marked by default. This annotation has no effect on "subpackages". <b>Warning</b>:
* if the package does not belong to a module, be very careful: it can easily happen that
* different versions of the package-info file are seen and used in different circumstances,
* causing the same classes to be interpreted inconsistently. For example, a package-info file
* from a {@code test} source path might hide the corresponding one from the {@code main}
* source path, or generated code might be compiled without seeing a package-info file at all.
* <li>Although Java permits it to be applied to a <b>record component</b> declaration (as in
* {@code record Foo(@NullMarked String bar) {...}}), this annotation has no meaning when used
* in that way.
* <li>Applying this annotation to an instance <b>method</b> of a <i>generic</i> class is
* acceptable, but is not recommended because it can lead to some confusing situations.
* <li>An advantage of Java <b>modules</b> is that you can make a lot of code null-marked with
* just a single annotation (before the {@code module} keyword). {@link NullUnmarked} is not
* supported on modules, since it's already the default.
* <li>If both {@code @NullMarked} and {@code @NullUnmarked} appear together on the same element,
* <i>neither</i> one is recognized.
* </ul>
*/
@Documented
@Target({MODULE, PACKAGE, TYPE, METHOD, CONSTRUCTOR})
@Retention(RUNTIME)
public @interface NullMarked {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2022 The JSpecify Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jspecify.annotations;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Indicates that the annotated element and the code transitively {@linkplain
* javax.lang.model.element.Element#getEnclosedElements() enclosed} within it is <b>null-unmarked
* code</b>: there, type usages generally have <b>unspecified nullness</b> unless explicitly
* annotated otherwise.
*
* <p>This annotation's purpose is to ease migration of a large existing codebase to null-marked
* status. It makes it possible to "flip the default" for new code added to a class or package even
* before that class or package has been fully migrated. Since new code is the most important code
* to analyze, this is strongly recommended as a temporary measure whenever necessary. However, once
* a codebase has been fully migrated it would be appropriate to ban use of this annotation.
*
* <p>For a comprehensive introduction to JSpecify, please see <a
* href="http://jspecify.org">jspecify.org</a>.
*
* <h2>Null-marked and null-unmarked code</h2>
*
* <p>{@link NullMarked} and this annotation work as a pair to include and exclude sections of code
* from null-marked status (respectively). Specifically, code is considered null-marked if the most
* narrowly enclosing element annotated with either of these two annotations exists and is annotated
* with {@code @NullMarked}.
*
* <p>Otherwise it is considered null-unmarked. This can happen in two ways: either it is more
* narrowly enclosed by a {@code @NullUnmarked}-annotated element than by any
* {@code @NullMarked}-annotated element, or neither annotation is present on any enclosing element.
* No distinction is made between these cases.
*
* <p>The effects of being null-marked are described in the <a
* href="NullMarked.html#effects">Effects</a> section of {@code NullMarked}.
*
* <h2>Unspecified nullness</h2>
*
* <p>Within null-unmarked code, a type usage with no nullness annotation has <b>unspecified
* nullness</b> (<a href="https://bit.ly/3ppb8ZC">Why?</a>). This means that, while there is always
* <i>some</i> correct way to annotate it for nullness, that information is missing: we <i>do not
* know</i> whether it includes or excludes {@code null} as a value. In such a case, tools can vary
* widely in how strict or lenient their enforcement is, or might make it configurable.
*
* <p>For more, please see this more <a
* href="https://github.com/jspecify/jspecify/wiki/nullness-unspecified">comprehensive
* discussion</a> of unspecified nullness.
*
* <p>There is no way for an individual type usage within null-marked code to have unspecified
* nullness. (<a href="https://bit.ly/3ppb8ZC">Why?</a>)
*
* <h2>Where it can be used</h2>
*
* The information in the <a href="NullMarked.html#where">Where it can be used</a> section of {@code
* NullMarked} applies as well to this annotation.
*/
// TODO(kevinb9n): word the middle section better with good words
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({PACKAGE, TYPE, METHOD, CONSTRUCTOR})
public @interface NullUnmarked {}
Loading

0 comments on commit ba27da4

Please sign in to comment.