Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try adding the JSpecify annotations to see if it helps with CI. #100

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ on:
platforms:
description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")'
required: true
default: 'linux-x64, linux-x86-hs, linux-x64-variants, linux-cross-compile, alpine-linux-x64, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs'
default: 'linux-x64'
configure-arguments:
description: 'Additional configure arguments'
required: false
Expand Down Expand Up @@ -153,8 +153,9 @@ jobs:
uses: ./.github/workflows/build-linux.yml
with:
platform: linux-x64
debug-levels: '[ "debug" ]'
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
configure-arguments: ${{ github.event.inputs.configure-arguments }} --disable-jvm-feature-jvmti
make-arguments: ${{ github.event.inputs.make-arguments }}
if: needs.select.outputs.linux-x64 == 'true'

Expand Down
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
Loading