-
Notifications
You must be signed in to change notification settings - Fork 3.1k
apiStatus annotation, user-land compilation warning/errors #8820
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
Conversation
|
What's wrong with |
|
In line with "deprecation" as a curse, |
I guess the name is wrong. If I'm removing |
|
Someone claimed previously that Maybe it's coronavirus, sheltering in place, the general need to declutter my life, but now I want no warnings from the compiler, but better integration with tooling for diagnostics. I don't even want deprecations from the compiler, but from a linter, which can decide what is an error and what can be ignored. It's also true that |
lrytz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, #7790 always felt like addressing a mixed bag of concerns and I didn't manage to grasp "what's the goal", that's why I was concerned.
Now this PR is different, I can understand what it does and I can see how some projects would want to use it, so I'm fine with adding it.
🎨 how about adding a parameter to deprecated instead? @deprecated(message = "...", since = "...", error = true)
|
|
I thought |
I agree, but that could be done with the additional parameter too |
That's true. I can make |
| final val Error = "error" | ||
| final val Warning = "warning" | ||
| final val WarningSummary = "warning-summary" | ||
| final val WarningVerbose = "warning-verbose" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably we should not expose the "verbose" options here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 9f6413c. Also added Scaladoc in there.
9f6413c to
c9bef7b
Compare
|
I think it would be better annotate the apiMayChange directly not the ApiStatus |
c9bef7b to
f9de842
Compare
|
@hepin1989
Different library authors want different ways of emitting compiler errors and warnings. Rather than adding each piecemeal, For example, some may opt to display ApiMayChange as a warning, while others may want to keep it silent by default, and let user opt into it as a warning. Same could be said of other more nuanced notifications such as semantics change, performance regression, or non-deterministic output. |
This implements `apiStatus` annotation as a generic form of `deprecated` annotation. While deprecated is only able to discourage someone from using the some API, `apiStatus` can be more nuanced about the state (for instance Category.ApiMayChange), and choose the default compile-time actions (Action.Error, Action.Warning, etc). In other words, this gives library authors the lever to trigger compilation warning or compilation errors! One of the usage is to trigger compiler error from the library when a method is invoked to display migration message. Another usage would be to denote bincompat status of the API as warning. This is a resend of scala#7790 based on the configurable warnings. Ref scala#8373 / https://twitter.com/not_xuwei_k/status/1240354073297268737
f9de842 to
a0f20e8
Compare
|
I feel like the name apiStatus refers to particular use case rather than describing what it's the name of. Can I suggest something like compilationProblem instead? ("Problem" seems to be the term IDEs use to lump errors and warnings together, so something that can be either an error or warning is called a problem.) |
|
@nafg
|
e885c91 to
5167e79
Compare
Library authors can extend apiStatus attribute to provide a custom status annotation. Due to the information available during compilation, the default values for `message`, `category`, or `defaultAction` are specified through the corresponding meta-annotations.
5167e79 to
9bf3a32
Compare
|
Added a few meta-annotations so we can define custom annotations: import scala.annotation.{ apiStatus, apiStatusCategory, apiStatusDefaultAction }
import scala.annotation.meta._
@apiStatusCategory("api-may-change")
@apiStatusDefaultAction(apiStatus.Action.Warning)
@companionClass @companionMethod
final class apiMayChange(
message: String,
since: String = "",
) extends apiStatus(message, since = since)This would simplify the tagging, comparable to @apiMayChange("can DSL is incubating, and future compatibility is not guaranteed")
implicit class CanDSL(s: String) {
def can(o: String): Unit = ()
} |
|
I don't think we have the bandwidth with the time remaining, but let's try to discuss this with you for the next release. |
I very much agree. For the first question, I don't know myself. If the answer is yes, I find it difficult to know what exactly is needed / will be used in reality by the various library authors. For this reason, a very general and configurable solution like it's implemented here makes sense to me (the details should be discussed of course). The simpler proposals in the past always caused a lot of questions in both directions: why not just use existing feature X? Why doesn't it support use case Y? |
|
@eed3si9n I played a bit around with new meta-annotations for defaults which would allow changing @apiStatusCategory(apiStatus.Category.ApiMayChange)
@apiStatusDefaultAction(apiStatus.Action.Warning)
@companionClass @companionMethod
final class apiMayChange(
message: String,
since: String = "",
) extends apiStatus(message, since = since)to @companionClass @companionMethod
final class apiMayChange(
message: String,
since: String = "",
) extends apiStatus(
message,
category = apiStatus.Category.ApiMayChange,
since = since,
defaultAction = apiStatus.Action.Warning)See the last commit message in 2.13.x...lrytz:constAnnDefaults |
That's great. Much more straightforward. |
… adding annotations to classes on the classpath. External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [https://github.com/scalacenter/scalafix](scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath, it scans each classpath entry for a file `/external-annotations.txt`. This means that external annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations. This strategy could be implemented in a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share the parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now). Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [https://github.com/scalacenter/scalafix](scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath, it scans each classpath entry for a file `/external-annotations.txt`. This means that external annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations. This strategy could be implemented in a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined, inheritance is not supported - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share the parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now). Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [https://github.com/scalacenter/scalafix](scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath, it scans each classpath entry for a file `/external-annotations.txt`. This means that external annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations. This strategy could be implemented in a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined, inheritance is not supported - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share the parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now). Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
… adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [scalafix](https://github.com/scalacenter/scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain) and simplicity (writing wartremover or scalafix uses non-trivial APIs). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath. Each each classpath entry is scanned for the file `/external-annotations.txt`. External annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations (they are checked in the refchecks phase). This strategy could be implemented as a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined (don't select inherited members) - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used for symbolic names (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share a parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now) Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [scalafix](https://github.com/scalacenter/scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain) and simplicity (writing wartremover or scalafix uses non-trivial APIs). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath. Each each classpath entry is scanned for the file `/external-annotations.txt`. External annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations (they are checked in the refchecks phase). This strategy could be implemented as a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined (don't select inherited members) - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used for symbolic names (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share a parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now) Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors). # Please enter the commit message for your changes. Lines starting # with '#' will be kept; you may remove them yourself if you want to. # An empty message aborts the commit. # # Date: Wed Jan 6 16:30:34 2021 +0100 # # On branch extAnn # Your branch is up to date with 'origin/extAnn'. # # Changes to be committed: # new file: ../src/compiler/scala/tools/nsc/ExternalAnnotations.scala # modified: ../src/compiler/scala/tools/nsc/Global.scala # modified: ../src/compiler/scala/tools/nsc/Reporting.scala # modified: ../src/compiler/scala/tools/nsc/settings/ScalaSettings.scala # modified: ../src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala # modified: ../src/reflect/scala/reflect/internal/Symbols.scala # modified: ../src/reflect/scala/reflect/internal/pickling/UnPickler.scala # modified: ../src/reflect/scala/reflect/runtime/SymbolLoaders.scala # new file: ../test/files/run/external-annotations.check # new file: ../test/files/run/external-annotations/Test.scala # new file: ../test/files/run/external-annotations/annots.txt # new file: ../test/junit/scala/tools/nsc/ExternalAnnotationsTest.scala #
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [scalafix](https://github.com/scalacenter/scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain) and simplicity (writing wartremover or scalafix uses non-trivial APIs). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath. Each each classpath entry is scanned for the file `/external-annotations.txt`. External annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations (they are checked in the refchecks phase). This strategy could be implemented as a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined (don't select inherited members) - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used for symbolic names (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share a parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now) Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [scalafix](https://github.com/scalacenter/scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain) and simplicity (writing wartremover or scalafix uses non-trivial APIs). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath. Each each classpath entry is scanned for the file `/external-annotations.txt`. External annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations (they are checked in the refchecks phase). This strategy could be implemented as a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined (don't select inherited members) - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used for symbolic names (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share a parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now) Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
|
relevant: Scala 3 appears set to add update: it's been merged |
This change adds support for defining external annotations, i.e., for adding annotations to classes on the classpath. ### Use case External annotations can be used for low-overhead API-level linting by adding deprecations and potentially making them fatal using `-Wconf`. The propsed [`@apiStatus` annotation](scala#8820) which generalizes deprecations would also be a good match for external annotations. There is overlap between external annotations and existing Scala linters like [scalafix](https://github.com/scalacenter/scalafix) and [wartremover](https://github.com/wartremover/wartremover). The advantage of external annotations is the low overhead (low compile-time overhead, no new tool in the toolchain) and simplicity (writing wartremover or scalafix uses non-trivial APIs). External annotations could potentially become useful for [explicit null checking in Scala 3](http://dotty.epfl.ch/docs/internals/explicit-nulls.html). ### Details There are two new compiler flags: - `-Yexternal-annotation-files annots.txt:more-annots.txt` enables reading external annotations from specific files - `-Yexternal-annotations` enables reading external annotations from jars / directories on the classpath. Each each classpath entry is scanned for the file `/external-annotations.txt`. External annotations can be shared across projects by publishing them to a repository and adding them as `libraryDependencies`. External annotations are added to symbols at the moment their lazy type is completed. This makes external annotations available already in the type checking phase, but does not cause symbol infos to be forced eagerly. An alternative implementation strategy is to add external annotations in a separate phase after type checking. This would make external annotations only visible after type checking, which would be OK for deprecations (they are checked in the refchecks phase). This strategy could be implemented as a compiler plugin. Syntax for external annotations: ``` @path.to.annotation @another.annot path.to.Class.method some.Class @scala.deprecated("don't use ???", "2.13.0") scala.Predef$.??? @scala.deprecated("no mutable collections") scala.collection.mutable._ ``` Details: - All paths need to be fully qualified - Members need to be selected from the class in which they are defined (don't select inherited members) - Annotation arguments need to be constants - `lwoercase` and symbolic names (`???`) refer to term symbols - `Uppercase` names refer to type symbols - A trailing `$` changes the name into a term (`Predef$`) - A trailing `#` makes a type name (``scala.collection.immutable.`::`#``) - Backticks can be used for symbolic names (``scala.sys.process.ProcessBuilder.`###` ``) - A wildcard `_` applies to all symbols that share a parent - Packages can't be annotated - For overloaded methods, annotations are applied to all alternatives (for now) Issues with external annotation files (e.g. syntax errors, unknown annotations, non-constant annotation arguments) are reported as warnings with category `other-external-annotations` (not as errors).
|
It remains unclear whether this is ever going to progress. Closing to get it out of the PR queue, at least for the time being. |
This implements
apiStatusannotation as a generic form ofdeprecatedannotation.apiStatustakescategoryanddefaultActionparameters, corresponding to configurable warning's category and action.One of the usage is to trigger compiler error from the library when a method is invoked to display migration message. Another usage would be to denote bincompat status of the API as warning.
This is a resend of #7790 based on the configurable warnings.
Ref #8373 / https://twitter.com/not_xuwei_k/status/1240354073297268737
Ref #7528
From Scaladoc
An annotation to denote the API status, like
scala.deprecatedbut more powerful.While
@deprecatedis only able to discourage someone from using the some API,@apiStatuscan be more nuanced about the state (for instanceCategory.ApiMayChange),and choose the default compile-time actions (
Action.Error,Action.Warning, etc).In other words, this gives library authors the lever to trigger compilation warning or
compilation errors! Here's an example of displaying a migration message:
The compilation will fail and display the migration message if the method is called:
Here's another example of displaying a warning:
The compiler will emit warnings:
Using
-Wconf:cat=api-may-change&origin=foo\..*:silentoption, the user of the library can opt out of theapi-may-changewarnings afterwards.Defining a custom status annotation
Instead of directly using
@apiStatuswe can create a specific status annotation by extendingapiStatus. However, due to the information available to the compiler, the default values formessage,category, ordefaultActioncan be specified through the corresponding meta-annotations.This can be used as follows: