or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdenvironment.mdindex.mdjs-wasm-compilation.mdjvm-compilation.mdmessage-handling.mdplugin-development.md

plugin-development.mddocs/

0

# Plugin Development

1

2

The Kotlin compiler provides a comprehensive plugin architecture enabling custom compilation phases, code generation, IDE integration, and language extensions. The modern K2-compatible plugin system uses CompilerPluginRegistrar for clean separation of concerns and improved performance.

3

4

## Capabilities

5

6

### Modern Plugin Architecture (K2-Compatible)

7

8

The current plugin system designed for the K2 compiler frontend with improved performance and maintainability.

9

10

```kotlin { .api }

11

/**

12

* Modern plugin registration interface for K2 compiler

13

* Provides clean separation between compiler phases and IDE integration

14

*/

15

interface CompilerPluginRegistrar {

16

/**

17

* Register compiler plugin components

18

* Called during compiler initialization to set up plugin infrastructure

19

*

20

* @param project MockProject instance for component registration

21

* @param configuration Compiler configuration with plugin settings

22

*/

23

fun registerProjectComponents(

24

project: MockProject,

25

configuration: CompilerConfiguration

26

)

27

28

companion object {

29

/**

30

* Extension point for plugin registrars

31

* Used by the compiler to discover and load plugins

32

*/

33

val EXTENSION_POINT_NAME: ExtensionPointName<CompilerPluginRegistrar>

34

}

35

}

36

```

37

38

### Command Line Processing

39

40

Plugin command-line option processing for configuration and parameter passing.

41

42

```kotlin { .api }

43

/**

44

* Processes command-line options for compiler plugins

45

* Handles option parsing and configuration setup

46

*/

47

interface CommandLineProcessor {

48

/** Unique identifier for the plugin */

49

val pluginId: String

50

51

/** Available plugin options */

52

val pluginOptions: Collection<PluginOption>

53

54

/**

55

* Process a single command-line option

56

*

57

* @param option The CLI option being processed

58

* @param value The option value from command line

59

* @param configuration Compiler configuration to update

60

* @return true if option was handled, false otherwise

61

*/

62

fun processOption(

63

option: AbstractCliOption,

64

value: String,

65

configuration: CompilerConfiguration

66

): Boolean

67

}

68

69

/**

70

* Definition of a plugin command-line option

71

*/

72

class PluginOption(

73

/** Option name (without -- prefix) */

74

val optionName: String,

75

76

/** Value description for help text */

77

val valueDescription: String,

78

79

/** Help description for the option */

80

val description: String,

81

82

/** Whether option requires a value */

83

val required: Boolean = true,

84

85

/** Allow multiple values for this option */

86

val allowMultipleOccurrences: Boolean = false

87

)

88

```

89

90

### Legacy Plugin Architecture (K1)

91

92

Legacy plugin system maintained for backward compatibility.

93

94

```kotlin { .api }

95

/**

96

* Legacy plugin registration interface (deprecated)

97

* Maintained for K1 compiler compatibility

98

*/

99

@Deprecated("Use CompilerPluginRegistrar for K2 compatibility")

100

interface ComponentRegistrar {

101

/**

102

* Register plugin components with the compiler

103

*

104

* @param project Project instance

105

* @param configuration Compiler configuration

106

*/

107

fun registerProjectComponents(

108

project: MockProject,

109

configuration: CompilerConfiguration

110

)

111

112

companion object {

113

val EXTENSION_POINT_NAME: ExtensionPointName<ComponentRegistrar>

114

}

115

}

116

```

117

118

### Plugin Extension Points

119

120

Core extension points for implementing plugin functionality.

121

122

```kotlin { .api }

123

/**

124

* Extension for IR generation and transformation

125

* Allows plugins to generate and modify intermediate representation

126

*/

127

interface IrGenerationExtension {

128

/**

129

* Generate additional IR declarations

130

* Called during IR generation phase

131

*/

132

fun generate(

133

moduleFragment: IrModuleFragment,

134

pluginContext: IrPluginContext

135

)

136

}

137

138

/**

139

* Extension for frontend analysis and resolution

140

* Enables custom declarations and synthetic elements

141

*/

142

interface AnalysisExtension {

143

/**

144

* Perform additional analysis

145

* Called during frontend analysis phase

146

*/

147

fun doAnalysis(

148

project: Project,

149

module: ModuleDescriptor,

150

bindingTrace: BindingTrace,

151

files: Collection<KtFile>

152

): AnalysisResult?

153

}

154

155

/**

156

* Extension for code generation

157

* Allows custom bytecode generation and optimization

158

*/

159

interface CodegenExtension {

160

/**

161

* Generate additional code

162

* Called during code generation phase

163

*/

164

fun generateCode(

165

codegen: ExpressionCodegen,

166

expression: KtExpression,

167

type: Type

168

): StackValue?

169

}

170

```

171

172

### Synthetic Declaration Provider

173

174

Plugin support for synthetic declarations and members.

175

176

```kotlin { .api }

177

/**

178

* Provides synthetic declarations not present in source code

179

* Useful for frameworks that generate members at compile time

180

*/

181

interface SyntheticResolveExtension {

182

/**

183

* Generate synthetic companion object

184

*/

185

fun generateSyntheticClasses(

186

moduleDescriptor: ModuleDescriptor,

187

name: Name,

188

ctx: LazyClassContext,

189

declarationProvider: PackageMemberDeclarationProvider,

190

result: MutableSet<ClassDescriptor>

191

)

192

193

/**

194

* Generate synthetic members for existing classes

195

*/

196

fun generateSyntheticMethods(

197

thisDescriptor: ClassDescriptor,

198

name: Name,

199

bindingContext: BindingContext,

200

fromSupertypes: List<SimpleFunctionDescriptor>,

201

result: MutableCollection<SimpleFunctionDescriptor>

202

)

203

204

/**

205

* Generate synthetic properties

206

*/

207

fun generateSyntheticProperties(

208

thisDescriptor: ClassDescriptor,

209

name: Name,

210

bindingContext: BindingContext,

211

fromSupertypes: ArrayList<PropertyDescriptor>,

212

result: MutableSet<PropertyDescriptor>

213

)

214

}

215

```

216

217

### Plugin Context and Services

218

219

Context and service access for plugin implementations.

220

221

```kotlin { .api }

222

/**

223

* Plugin context providing access to compiler services

224

* Available during plugin execution phases

225

*/

226

interface IrPluginContext {

227

/** Module descriptor being compiled */

228

val moduleDescriptor: ModuleDescriptor

229

230

/** Symbol table for IR operations */

231

val symbolTable: SymbolTable

232

233

/** Type translator for IR types */

234

val typeTranslator: TypeTranslator

235

236

/** IR factory for creating IR elements */

237

val irFactory: IrFactory

238

239

/** Access to built-in types and symbols */

240

val irBuiltIns: IrBuiltIns

241

}

242

243

/**

244

* Configuration keys specific to plugins

245

*/

246

object PluginConfigurationKeys {

247

/** Plugin options from command line */

248

val PLUGIN_OPTIONS: CompilerConfigurationKey<List<PluginOption>>

249

250

/** Plugin classpath */

251

val PLUGIN_CLASSPATH: CompilerConfigurationKey<List<String>>

252

}

253

```

254

255

## Usage Examples

256

257

### Creating a Basic Plugin

258

259

```kotlin

260

import org.jetbrains.kotlin.compiler.plugin.*

261

import org.jetbrains.kotlin.config.CompilerConfiguration

262

import com.intellij.mock.MockProject

263

264

// 1. Define plugin options

265

class MyPluginCommandLineProcessor : CommandLineProcessor {

266

override val pluginId = "my-plugin"

267

268

override val pluginOptions = listOf(

269

PluginOption(

270

optionName = "enabled",

271

valueDescription = "true|false",

272

description = "Enable my plugin functionality"

273

),

274

PluginOption(

275

optionName = "output-dir",

276

valueDescription = "path",

277

description = "Output directory for generated files"

278

)

279

)

280

281

override fun processOption(

282

option: AbstractCliOption,

283

value: String,

284

configuration: CompilerConfiguration

285

): Boolean {

286

return when (option.optionName) {

287

"enabled" -> {

288

configuration.put(MyPluginConfigurationKeys.ENABLED, value.toBoolean())

289

true

290

}

291

"output-dir" -> {

292

configuration.put(MyPluginConfigurationKeys.OUTPUT_DIR, value)

293

true

294

}

295

else -> false

296

}

297

}

298

}

299

300

// 2. Define configuration keys

301

object MyPluginConfigurationKeys {

302

val ENABLED = CompilerConfigurationKey<Boolean>("my.plugin.enabled")

303

val OUTPUT_DIR = CompilerConfigurationKey<String>("my.plugin.output.dir")

304

}

305

306

// 3. Implement plugin registrar

307

class MyCompilerPluginRegistrar : CompilerPluginRegistrar {

308

override fun registerProjectComponents(

309

project: MockProject,

310

configuration: CompilerConfiguration

311

) {

312

val enabled = configuration.get(MyPluginConfigurationKeys.ENABLED) ?: false

313

if (!enabled) return

314

315

// Register IR generation extension

316

IrGenerationExtension.registerExtension(

317

project,

318

MyIrGenerationExtension(configuration)

319

)

320

321

// Register synthetic resolve extension

322

SyntheticResolveExtension.registerExtension(

323

project,

324

MySyntheticResolveExtension()

325

)

326

}

327

}

328

```

329

330

### IR Generation Extension

331

332

```kotlin

333

import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension

334

import org.jetbrains.kotlin.ir.declarations.*

335

import org.jetbrains.kotlin.ir.util.*

336

337

class MyIrGenerationExtension(

338

private val configuration: CompilerConfiguration

339

) : IrGenerationExtension {

340

341

override fun generate(

342

moduleFragment: IrModuleFragment,

343

pluginContext: IrPluginContext

344

) {

345

// Generate additional IR declarations

346

moduleFragment.files.forEach { file ->

347

file.declarations.filterIsInstance<IrClass>().forEach { irClass ->

348

if (shouldProcessClass(irClass)) {

349

generateHelperMethod(irClass, pluginContext)

350

}

351

}

352

}

353

}

354

355

private fun shouldProcessClass(irClass: IrClass): Boolean {

356

// Check if class has specific annotation

357

return irClass.hasAnnotation(FqName("com.example.MyAnnotation"))

358

}

359

360

private fun generateHelperMethod(

361

irClass: IrClass,

362

pluginContext: IrPluginContext

363

) {

364

val function = pluginContext.irFactory.buildFun {

365

name = Name.identifier("generatedHelper")

366

returnType = pluginContext.irBuiltIns.unitType

367

visibility = Visibilities.PUBLIC

368

}

369

370

// Build function body

371

function.body = DeclarationIrBuilder(pluginContext, function.symbol).irBlockBody {

372

// Generate IR statements

373

+irReturn(irUnit())

374

}

375

376

irClass.declarations.add(function)

377

}

378

}

379

```

380

381

### Synthetic Member Generation

382

383

```kotlin

384

import org.jetbrains.kotlin.descriptors.*

385

import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension

386

387

class MySyntheticResolveExtension : SyntheticResolveExtension {

388

389

override fun generateSyntheticMethods(

390

thisDescriptor: ClassDescriptor,

391

name: Name,

392

bindingContext: BindingContext,

393

fromSupertypes: List<SimpleFunctionDescriptor>,

394

result: MutableCollection<SimpleFunctionDescriptor>

395

) {

396

// Generate synthetic getter/setter methods

397

if (name.asString().startsWith("get") || name.asString().startsWith("set")) {

398

val propertyName = name.asString().removePrefix("get").removePrefix("set")

399

400

if (hasPropertyAnnotation(thisDescriptor, propertyName)) {

401

val syntheticFunction = createSyntheticFunction(

402

thisDescriptor,

403

name,

404

propertyName

405

)

406

result.add(syntheticFunction)

407

}

408

}

409

}

410

411

override fun generateSyntheticProperties(

412

thisDescriptor: ClassDescriptor,

413

name: Name,

414

bindingContext: BindingContext,

415

fromSupertypes: ArrayList<PropertyDescriptor>,

416

result: MutableSet<PropertyDescriptor>

417

) {

418

// Generate synthetic properties based on annotations

419

if (hasGeneratePropertyAnnotation(thisDescriptor, name)) {

420

val syntheticProperty = createSyntheticProperty(thisDescriptor, name)

421

result.add(syntheticProperty)

422

}

423

}

424

425

private fun hasPropertyAnnotation(

426

classDescriptor: ClassDescriptor,

427

propertyName: String

428

): Boolean {

429

return classDescriptor.annotations.hasAnnotation(

430

FqName("com.example.GenerateProperty")

431

)

432

}

433

434

private fun createSyntheticFunction(

435

owner: ClassDescriptor,

436

name: Name,

437

propertyName: String

438

): SimpleFunctionDescriptor {

439

return SimpleFunctionDescriptorImpl.create(

440

owner,

441

Annotations.EMPTY,

442

name,

443

CallableMemberDescriptor.Kind.SYNTHESIZED,

444

owner.source

445

).apply {

446

// Configure function parameters and return type

447

initialize(

448

null, // extension receiver

449

owner.thisAsReceiverParameter,

450

emptyList(), // type parameters

451

emptyList(), // value parameters

452

owner.builtIns.stringType, // return type

453

Modality.FINAL,

454

Visibilities.PUBLIC

455

)

456

}

457

}

458

}

459

```

460

461

### Plugin Registration and Discovery

462

463

```kotlin

464

// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar

465

com.example.MyCompilerPluginRegistrar

466

467

// META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor

468

com.example.MyPluginCommandLineProcessor

469

```

470

471

### Plugin Usage in Build Scripts

472

473

**Gradle (build.gradle.kts):**

474

```kotlin

475

plugins {

476

kotlin("jvm")

477

id("my-kotlin-plugin") version "1.0.0"

478

}

479

480

kotlinCompilerPluginOptions {

481

option("my-plugin", "enabled", "true")

482

option("my-plugin", "output-dir", "build/generated")

483

}

484

```

485

486

**Command Line:**

487

```bash

488

kotlinc \

489

-Xplugin=my-plugin.jar \

490

-P plugin:my-plugin:enabled=true \

491

-P plugin:my-plugin:output-dir=build/generated \

492

src/main/kotlin/Main.kt

493

```

494

495

### Advanced Plugin Patterns

496

497

```kotlin

498

// Multi-phase plugin with dependency ordering

499

class AdvancedPluginRegistrar : CompilerPluginRegistrar {

500

501

override fun registerProjectComponents(

502

project: MockProject,

503

configuration: CompilerConfiguration

504

) {

505

// Register multiple extensions with specific ordering

506

val analysisExtension = MyAnalysisExtension(configuration)

507

val irExtension = MyIrGenerationExtension(configuration)

508

val codegenExtension = MyCodegenExtension(configuration)

509

510

// Analysis phase

511

AnalysisExtension.registerExtension(project, analysisExtension)

512

513

// IR generation phase

514

IrGenerationExtension.registerExtension(project, irExtension)

515

516

// Code generation phase

517

CodegenExtension.registerExtension(project, codegenExtension)

518

519

// Cross-phase communication via configuration

520

configuration.put(ANALYSIS_RESULTS, analysisExtension.results)

521

}

522

}

523

524

// Plugin with custom diagnostic reporting

525

class DiagnosticPlugin : IrGenerationExtension {

526

527

override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {

528

moduleFragment.files.forEach { file ->

529

validateFile(file, pluginContext)

530

}

531

}

532

533

private fun validateFile(file: IrFile, context: IrPluginContext) {

534

file.declarations.forEach { declaration ->

535

if (hasIssue(declaration)) {

536

// Report custom diagnostic

537

context.messageCollector.report(

538

CompilerMessageSeverity.WARNING,

539

"Custom plugin warning: ${declaration.name}",

540

CompilerMessageSourceLocation.create(

541

file.path,

542

declaration.startOffset,

543

declaration.endOffset,

544

null

545

)

546

)

547

}

548

}

549

}

550

}

551

```

552

553

## Plugin Distribution

554

555

### JAR Packaging

556

557

Package plugin with service registration files:

558

559

```

560

my-plugin.jar

561

├── com/example/MyPluginRegistrar.class

562

├── com/example/MyCommandLineProcessor.class

563

├── META-INF/

564

│ └── services/

565

│ ├── org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar

566

│ └── org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor

567

└── plugin.xml (optional, for IDE integration)

568

```

569

570

### Error Handling

571

572

```kotlin { .api }

573

class PluginException(message: String, cause: Throwable? = null) : Exception(message, cause)

574

575

class InvalidPluginConfigurationException(message: String) : PluginException(message)

576

```

577

578

Common plugin error scenarios:

579

- **PLUGIN_LOADING_ERROR**: Plugin JAR not found or invalid service registration

580

- **CONFIGURATION_ERROR**: Invalid plugin options or missing required parameters

581

- **COMPILATION_PHASE_ERROR**: Plugin errors during specific compilation phases

582

- **EXTENSION_REGISTRATION_ERROR**: Extension point registration failures