Annotation

Annotation is a special syntax used only in declaration. It is used to provide additional information to the compiler or at runtime to implement specific functionalities.

A basic syntax example of annotations is as follows:

@Annotation1[arg1, arg2]
@Annotation2
class Foo {}

Annotations can be used in different declarations.

The syntax specifications are defined as follows:

annotationList: annotation+;

annotation
    : '@' (identifier '.')* identifier ('[' annotationArgumentList ']')?
    ;

annotationArgumentList
    : annotationArgument (',' annotationArgument)*  ','?
    ;

annotationArgument
    : identifier ':' expression
    | expression
    ;

Annotations and macro calls share the same syntax. Because macros are processed at an earlier stage than annotations, the compiler first attempts to parse the syntax as a macro. If that fails, it then tries to process the syntax as an annotation. If both processing methods fail, a compilation error should be reported.

Custom Annotations

The custom annotation mechanism enables reflection to obtain the annotation content. This mechanism aims to provide more useful information apart from the type metadata to support more complex logic.

Custom annotations can be used in type declarations, member variable declarations, member property declarations, member function declarations, constructor declarations, and function parameter declarations (for the above-mentioned functions).

You can customize annotations through @Annotation.

@Annotation can only modify a class that is not modified by abstract, open, or sealed.

When a class declares that it is labeled with @Annotation, it must provide at least one const init function. Otherwise, the compiler reports an error.

An example of a custom annotation is as follows:

@Annotation
public class CustomAnnotation {
    let name: String
    let version: Int64

    public const init(name: String, version!: Int64 = 0) {
        this.name = name
        this.version = version
    }
}

// use annotation
@CustomAnnotation["Sample", version: 1]
class Foo {}

Annotations need to be generated during compilation and bound to classes. const init must be used in annotation customization to construct valid instances.

The specifications are as follows:

  1. The declaration syntax of an annotation is the same as that of a macro. Parameters in [] must be passed by sequence or naming rule and must be const expressions.
  2. Annotation classes with non-parameterized constructors can omit brackets in declaration.
@Annotation
public class MyAnnotation { ... }

@MyAnnotation // ok
class Foo {}

An annotation class cannot be declared for multiple times for an annotation target. That is, the annotation class must be unique.

The compiler does not guarantee that the sequence of the multiple Annotation metadata generated matches the sequence of Annotation in the code.

@MyAnnotation
@MyAnnotation // error
class Foo {}

@MyAnnotation
@MyAnnotation2
class Bar {}

Annotation is not inherited. Therefore, the annotation metadata of a class comes only from the declared annotations when it is defined. If the annotation metadata of the superclass is required, you need to query the metadata through the reflection interface.

@BaseAnno
open class Base {}

@InterfaceAnno
interface I {}

@SubAnno
class Sub <: Base & I {} // Sub has only SubAnno annotation information.

Custom annotations can also restrict their application scopes to reduce misuse. To this aim, you need to add the target parameter when declaring @Annotation.

The target parameter needs to be passed as a variable-length parameter to the location that the custom annotation is expected to support. The parameter received by target is an Array<AnnotationKind>.

The following application scopes can be restricted:

  • Type declaration (class, struct, enum, interface)
  • Parameters in a member function or constructor
  • Constructor declaration
  • Member function declaration
  • Member variable declaration
  • Member property declaration
public enum AnnotaitionKind {
    | Type
    | Parameter
    | Init
    | MemberProperty
    | MemberFunction
    | MemberVariable
}

@Annotation[target: [Type, MemberFunction]]
class CustomAnnotation{}
@Annotation
class SubCustomAnnotation{}

@Annotation[target: []]
class MyAnno{}

If no target is specified, the custom annotations can be used in all the preceding scopes. When the target is specified, it can be used only in the declared list.