89 lines
6.3 KiB
Markdown
89 lines
6.3 KiB
Markdown
# Fir elements
|
|
|
|
- All fir elements are listed in `FirTreeBuilder.kt`
|
|
- Syntax of new element declaration: `element(elementName, elementKind: Kind, vararg parents: Element)`
|
|
- `elementName` is a name of declared element. If `elementName = Foo` then it's type will be `FirFoo`
|
|
- kind describes target package for element. Avaliable kinds:
|
|
- `Expression` (package `fir.expression`)
|
|
- `Declaration` (package `fir.declaration`)
|
|
- `Reference` (package `fir.references`)
|
|
- `TypeRef` (package `fir.types`)
|
|
- `Other` (package `fir`)
|
|
- if no one parent element was not declaraed than generated element will be direct inheritor of `FirElement`
|
|
|
|
# Types
|
|
|
|
- All types, used in elements and their implementations are described with object of class `Type`
|
|
- `Type` objects are used for generating imports in generated files
|
|
- Types commonly used in configuration are listed in `Types.kt`
|
|
- There is multiple ways to describe new type:
|
|
- `type(klass: KClass<*>)` uses FQN of corresponding class
|
|
- `type(packageName: String, typeName: String, exactPackage: Boolean = false)`
|
|
- if `exactPackage = false` it's return type with default package prefix: `org.jetbrains.kotlin.packageName.typeName`
|
|
- otherwise there is no default prefix: `packageName.typeName`
|
|
- `type(typeName: String)` creates type with no package, used only for types of type parameters (**Do not use it directly**)
|
|
- `generatedType([packageName: String], typeName: String)` -- same as `type(packageName, typeName)` but with `org.jetbrains.kotlin.fir` prefix
|
|
|
|
# Content of elements
|
|
|
|
- Fields of elements are described in `NodeConfigurator.kt`
|
|
- Syntax:
|
|
```
|
|
elementName.configure {
|
|
// node configuration
|
|
}
|
|
```
|
|
- **Fields:**
|
|
- `Field` class describes field of element
|
|
- There is multiple ways of creating new fields, but they have similar syntax: `field(..., nullable: Boolean = false, withReplace: Boolean)`
|
|
- if `isNullable` is true then field type will be nullable
|
|
- if `withReplace` is true then in element will be generated method `replace...` for that field
|
|
- in place of `...` you can pass optional name (with `String` type) and `Type` or `Element` object
|
|
- if no `name` passed then it will be generated based on type
|
|
- if `Type` or `Element` has type argumetns you want to specify then you can call method `Type.withArgs(vararg types: String)` or `Element.withArgs(vararg replacements: Pair<String, String>)`
|
|
- Also you can create fields with lists of some types
|
|
- Lists can holds only fir element
|
|
- Syntax: `fieldList([name: String], element: Element)` (if name no specified it will be generated based on type of `element`)
|
|
- And there are helper functions for fields of primitive types that takes name of field: `booleanField`, `intField`, `stringField`
|
|
- If you want generate `transform...` function for field you should call method `withTransform()` on it
|
|
- To add field to configuring node you should call infix `+` operator: `+fieldList("catches", catchClause).withTransform()`
|
|
- Also you can use method `symbol(symbolTypeName: String, [argument: String])` to create field named `symbol` with type lying in `org.jetbrains.kotlin.fir.symbols` package
|
|
- Some predefined fields are listed in `FieldSets.kt`
|
|
- If your node has some `tansform...` methods and you want to add methods for transforming all other children you should call `needTransformOtherChildren()`
|
|
- If your element has type parameters you should declare them using method `withArg(typeParameterName: String, [upperBound: Type/Element])`
|
|
- If element inherits element with type parameters you should match that parameters with concrete types using method `parentArg(parent: Element, typeParameterName: String, typeArgument: String/Type/Element)`
|
|
- Note that if some element contains type parameters it should be configured before it's inheritors (will be fixed later)
|
|
|
|
# Implementations
|
|
|
|
- If element has no inheritors then it will have default implementation. Otherwise you should declare implementation that you want
|
|
- All implementations are described in `ImplementationConfigurator.kt`
|
|
- Syntax:
|
|
- `impl(element: Element, [name: String]) {...}` describes configuration of element with name `name` (if there is no name then it would be `ElementTypeImpl`). Lambda with implementation configuration is optional. Note that this function returns object of type `Implementation`
|
|
- `noImpl(element: Element)` used when you don't want to generated implementation of `element`
|
|
- In configuration lambda you can:
|
|
- Describe kind of implementation -- `FinalClass` (default), `OpenClass`, `AbstractClass`, `Interface` using syntax `kind = Interface`
|
|
- Add parents for implementation class
|
|
- syntax: `parents += parent`
|
|
- `parent` can be only implementation with `kind = Interface`
|
|
- Configure default values for fields:
|
|
- `default(fieldName: String) { ... }`
|
|
- in configuration lambda you can describe:
|
|
- `value = _defaultValue_`
|
|
- `withGetter = true/false` (`false` by default)
|
|
- `delegate = delegateFieldName` (used for generating such fields: `val typeRef: FirTypeRef get() = expression.typeRef`)
|
|
- `delegateName = fieldNameInDelegateType` (`val expressionTypeRef: FirTypeRef get() = expression.typeRef`)
|
|
- `needAcceptAndTransform = true/false` (`true` by default) -- specify it if you don't want to accept field in `acceptChildren`
|
|
- `customSetter = setterExpresison`
|
|
- note that by default all fields with fir elements are mutable and others are immutable
|
|
- Also there is some aliases for that default:
|
|
- `default(fieldName, value)`
|
|
- `defaultNull(fieldName, [withGetter: Boolean])`
|
|
- If some fields should be `lateinit` you describe them in call `lateinit(vararg fields: String)`
|
|
- If you use some types that shoub be imported list them in method `useTypes(vararg types: Type/Element)`
|
|
|
|
# Notes
|
|
|
|
- There is algorithm that automatically makes as most abstract classes instead of interfaces as possible. If you want to some `Element` or `Implementation` should be always an interface you should:
|
|
- call `shouldBeAnInterface` when configuring a `Element` in `NodeConfigurator.kt`
|
|
- specify `kind = Interface` when configuring an `Implementation` in `ImplementationConfigurator.kt` |