ae902e6fe5
Previously the callee reference of a delegated constructor call is always the same as the call itself. This violates the contract that no two FIR elements can have identical sources. In addition, this sets the entire call expression as the source of the callee expression. This change instead sets the proper constructor ref as the callee. Also fixed EXPLICIT_DELEGATION_CALL_REQUIRED type. It should be an error instead of a warning.
Fir elements
- All fir elements are listed in
FirTreeBuilder.kt - Syntax of new element declaration:
element(elementName, elementKind: Kind, vararg parents: Element)elementNameis a name of declared element. IfelementName = Foothen it's type will beFirFoo- kind describes target package for element. Avaliable kinds:
Expression(packagefir.expression)Declaration(packagefir.declaration)Reference(packagefir.references)TypeRef(packagefir.types)Other(packagefir)
- 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 Typeobjects 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 classtype(packageName: String, typeName: String, exactPackage: Boolean = false)- if
exactPackage = falseit's return type with default package prefix:org.jetbrains.kotlin.packageName.typeName - otherwise there is no default prefix:
packageName.typeName
- if
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 astype(packageName, typeName)but withorg.jetbrains.kotlin.firprefix
Content of elements
- Fields of elements are described in
NodeConfigurator.kt - Syntax:
elementName.configure {
// node configuration
}
- Fields:
Fieldclass describes field of element- There is multiple ways of creating new fields, but they have similar syntax:
field(..., nullable: Boolean = false, withReplace: Boolean)- if
isNullableis true then field type will be nullable - if
withReplaceis true then in element will be generated methodreplace...for that field - in place of
...you can pass optional name (withStringtype) andTypeorElementobject - if no
namepassed then it will be generated based on type - if
TypeorElementhas type argumetns you want to specify then you can call methodType.withArgs(vararg types: String)orElement.withArgs(vararg replacements: Pair<String, String>)
- if
- 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 ofelement)
- 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 methodwithTransform()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 namedsymbolwith type lying inorg.jetbrains.kotlin.fir.symbolspackage - 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 callneedTransformOtherChildren() - 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 namename(if there is no name then it would beElementTypeImpl). Lambda with implementation configuration is optional. Note that this function returns object of typeImplementationnoImpl(element: Element)used when you don't want to generated implementation ofelement
- In configuration lambda you can:
- Describe kind of implementation --
FinalClass(default),OpenClass,AbstractClass,Interfaceusing syntaxkind = Interface - Add parents for implementation class
- syntax:
parents += parent parentcan be only implementation withkind = Interface
- syntax:
- Configure default values for fields:
default(fieldName: String) { ... }- in configuration lambda you can describe:
value = _defaultValue_withGetter = true/false(falseby 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(trueby default) -- specify it if you don't want to accept field inacceptChildrencustomSetter = setterExpresison
- note that by default all fields with fir elements are mutable and others are immutable
- in configuration lambda you can describe:
- Also there is some aliases for that default:
default(fieldName, value)defaultNull(fieldName, [withGetter: Boolean])
- If some fields should be
lateinityou describe them in calllateinit(vararg fields: String) - If you use some types that shoub be imported list them in method
useTypes(vararg types: Type/Element)
- Describe kind of implementation --
Notes
- There is algorithm that automatically makes as most abstract classes instead of interfaces as possible. If you want to some
ElementorImplementationshould be always an interface you should:- call
shouldBeAnInterfacewhen configuring aElementinNodeConfigurator.kt - specify
kind = Interfacewhen configuring anImplementationinImplementationConfigurator.kt
- call