or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations-and-metadata.mdcode-generation.mdfile-and-type-generation.mdfunction-and-property-generation.mdindex.mdtype-system.mdutilities.md

code-generation.mddocs/

0

# Code Generation and Templates

1

2

Template-based code generation with placeholder substitution, supporting complex code structures and maintaining proper formatting and imports.

3

4

## Capabilities

5

6

### Code Block Generation

7

8

Creates formatted code fragments with placeholder substitution and proper indentation, forming the building blocks for all code generation in KotlinPoet.

9

10

```kotlin { .api }

11

/**

12

* A fragment of code with placeholder substitution

13

*/

14

class CodeBlock private constructor() {

15

/** Whether this code block is empty */

16

val isEmpty: Boolean

17

18

/** Whether this code block is not empty */

19

val isNotEmpty: Boolean

20

21

companion object {

22

/** Creates a code block from a format string and arguments */

23

fun of(format: String, vararg args: Any?): CodeBlock

24

25

/** Creates an empty code block */

26

fun builder(): Builder

27

28

/** Joins multiple code blocks with a separator */

29

fun join(codeBlocks: Iterable<CodeBlock>, separator: String = ", "): CodeBlock

30

fun join(codeBlocks: Iterable<CodeBlock>, separator: CodeBlock): CodeBlock

31

32

/** Creates a code block that joins with newlines */

33

fun joining(separator: String = ", "): Collector<CodeBlock, *, CodeBlock>

34

fun joining(separator: CodeBlock): Collector<CodeBlock, *, CodeBlock>

35

}

36

37

/** Builder for constructing CodeBlock instances */

38

class Builder {

39

/** Adds formatted code to the block */

40

fun add(format: String, vararg args: Any?): Builder

41

42

/** Adds named code with a parameter map */

43

fun addNamed(format: String, args: Map<String, *>): Builder

44

45

/** Adds a code statement (automatically adds newline) */

46

fun addStatement(format: String, vararg args: Any?): Builder

47

48

/** Adds raw code without formatting */

49

fun add(codeBlock: CodeBlock): Builder

50

51

/** Begins a control flow block (if, for, while, etc.) */

52

fun beginControlFlow(controlFlow: String, vararg args: Any?): Builder

53

54

/** Adds to the current control flow */

55

fun nextControlFlow(controlFlow: String, vararg args: Any?): Builder

56

57

/** Ends the current control flow block */

58

fun endControlFlow(): Builder

59

fun endControlFlow(controlFlow: String, vararg args: Any?): Builder

60

61

/** Increases indentation level */

62

fun indent(): Builder

63

64

/** Decreases indentation level */

65

fun unindent(): Builder

66

67

/** Clears all content from the builder */

68

fun clear(): Builder

69

70

/** Builds the CodeBlock */

71

fun build(): CodeBlock

72

}

73

}

74

```

75

76

**Usage Examples:**

77

78

```kotlin

79

import com.squareup.kotlinpoet.*

80

81

// Simple code block with placeholders

82

val simpleCode = CodeBlock.of("println(%S)", "Hello, World!")

83

84

// Code block with multiple statements

85

val multiStatementCode = CodeBlock.builder()

86

.addStatement("val name = %S", "Alice")

87

.addStatement("val age = %L", 25)

88

.addStatement("println(%S + name + %S + age)", "Name: ", ", Age: ")

89

.build()

90

91

// Control flow structures

92

val ifElseCode = CodeBlock.builder()

93

.beginControlFlow("if (age >= 18)")

94

.addStatement("println(%S)", "Adult")

95

.nextControlFlow("else")

96

.addStatement("println(%S)", "Minor")

97

.endControlFlow()

98

.build()

99

100

// For loop

101

val forLoopCode = CodeBlock.builder()

102

.beginControlFlow("for (i in 1..10)")

103

.addStatement("println(i)")

104

.endControlFlow()

105

.build()

106

107

// When expression

108

val whenCode = CodeBlock.builder()

109

.beginControlFlow("when (status)")

110

.addStatement("%T.PENDING -> %S", statusEnum, "Waiting for approval")

111

.addStatement("%T.APPROVED -> %S", statusEnum, "Request approved")

112

.addStatement("%T.REJECTED -> %S", statusEnum, "Request rejected")

113

.addStatement("else -> %S", "Unknown status")

114

.endControlFlow()

115

.build()

116

117

// Try-catch block

118

val tryCatchCode = CodeBlock.builder()

119

.beginControlFlow("try")

120

.addStatement("val result = riskyOperation()")

121

.addStatement("return result")

122

.nextControlFlow("catch (e: %T)", Exception::class)

123

.addStatement("logger.error(%S, e)", "Operation failed")

124

.addStatement("throw e")

125

.endControlFlow()

126

.build()

127

128

// Named parameters for complex formatting

129

val namedCode = CodeBlock.builder()

130

.addNamed(

131

"val \$field:L = \$type:T(\$value:S)",

132

mapOf(

133

"field" to "username",

134

"type" to String::class,

135

"value" to "john_doe"

136

)

137

)

138

.build()

139

140

// Joining code blocks

141

val parameters = listOf("name", "age", "email")

142

val paramCode = parameters.map { CodeBlock.of("%L", it) }

143

val joinedParams = CodeBlock.join(paramCode, ", ")

144

```

145

146

### Template Placeholders and Formatting

147

148

KotlinPoet supports various placeholder types for different kinds of code substitution.

149

150

```kotlin { .api }

151

// Placeholder types for CodeBlock.of() and related methods:

152

// %S - String literals (automatically escaped)

153

// %L - Literals (numbers, booleans, raw code)

154

// %N - Names (identifiers, avoiding keywords)

155

// %T - Types (TypeName instances, handles imports)

156

// %M - Members (MemberName instances, handles imports)

157

// %% - Literal percent sign

158

```

159

160

**Usage Examples:**

161

162

```kotlin

163

// String literals (%S) - automatically escaped

164

val stringLiteral = CodeBlock.of("val message = %S", "Hello \"World\"")

165

// Result: val message = "Hello \"World\""

166

167

// Literals (%L) - raw values

168

val numberLiteral = CodeBlock.of("val count = %L", 42)

169

val booleanLiteral = CodeBlock.of("val isActive = %L", true)

170

val rawCode = CodeBlock.of("val result = %L", "computeValue()")

171

172

// Names (%N) - identifiers that avoid keywords

173

val identifier = CodeBlock.of("val %N = %S", "class", "This would be escaped")

174

// Result: val `class` = "This would be escaped"

175

176

// Types (%T) - TypeName instances with import handling

177

val typeUsage = CodeBlock.of("val list = %T<%T>()", ArrayList::class, String::class)

178

// Result: val list = ArrayList<String>()

179

// Imports: import java.util.ArrayList, import kotlin.String

180

181

// Members (%M) - MemberName instances with import handling

182

val memberUsage = CodeBlock.of(

183

"return %M(items)",

184

MemberName.get(ClassName.get("kotlin.collections", "Collections"), "sort")

185

)

186

187

// Complex template with multiple placeholders

188

val complexTemplate = CodeBlock.of(

189

"val %N: %T = %T.%M(%S, %L)",

190

"result",

191

String::class,

192

String::class,

193

MemberName.get(String::class, "format"),

194

"Value: %d",

195

42

196

)

197

198

// Template with collections

199

val listTemplate = CodeBlock.of(

200

"val items = %T(%L)",

201

LIST,

202

listOf(1, 2, 3).joinToString(", ")

203

)

204

```

205

206

### Member Name Resolution

207

208

Manages references to class members (functions, properties, constants) with automatic import resolution.

209

210

```kotlin { .api }

211

/**

212

* Reference to a member (property, function, constructor, etc.)

213

*/

214

class MemberName {

215

/** The enclosing type that contains this member */

216

val enclosingClassName: ClassName?

217

218

/** The package name if this is a top-level member */

219

val packageName: String

220

221

/** The simple name of the member */

222

val simpleName: String

223

224

/** Whether this member can be imported */

225

val isExtension: Boolean

226

227

companion object {

228

/** Creates a member reference for a class member */

229

fun get(enclosingClassName: ClassName, simpleName: String): MemberName

230

231

/** Creates a member reference for a top-level member */

232

fun get(packageName: String, simpleName: String): MemberName

233

234

/** Creates a member reference from a Java Class and member name */

235

fun get(clazz: Class<*>, simpleName: String): MemberName

236

237

/** Creates a member reference from a KClass and member name */

238

fun get(klass: KClass<*>, simpleName: String): MemberName

239

}

240

}

241

```

242

243

**Usage Examples:**

244

245

```kotlin

246

// Class member references

247

val stringFormat = MemberName.get(String::class, "format")

248

val listOf = MemberName.get(ClassName.get("kotlin.collections", "Collections"), "listOf")

249

250

// Top-level function references

251

val println = MemberName.get("kotlin.io", "println")

252

val maxOf = MemberName.get("kotlin", "maxOf")

253

254

// Extension function references

255

val stringIsBlank = MemberName.get(String::class, "isBlank")

256

257

// Using member names in code

258

val memberUsageCode = CodeBlock.builder()

259

.addStatement("val greeting = %M(%S, name)", stringFormat, "Hello, %s!")

260

.addStatement("%M(greeting)", println)

261

.addStatement("val numbers = %M(1, 2, 3)", listOf)

262

.build()

263

264

// Member names for constants

265

val piConstant = MemberName.get(Math::class, "PI")

266

val maxValue = MemberName.get(Integer::class, "MAX_VALUE")

267

268

val constantUsage = CodeBlock.of(

269

"val area = %M * radius * radius",

270

piConstant

271

)

272

```

273

274

### Advanced Code Generation Patterns

275

276

Common patterns and techniques for generating complex code structures.

277

278

**Builder Pattern Generation:**

279

280

```kotlin

281

val builderClass = TypeSpec.classBuilder("PersonBuilder")

282

.addProperty(

283

PropertySpec.varBuilder("name", String::class.copy(nullable = true))

284

.addModifiers(KModifier.PRIVATE)

285

.initializer("null")

286

.build()

287

)

288

.addProperty(

289

PropertySpec.varBuilder("age", Int::class.copy(nullable = true))

290

.addModifiers(KModifier.PRIVATE)

291

.initializer("null")

292

.build()

293

)

294

.addFunction(

295

FunSpec.builder("name")

296

.addParameter("name", String::class)

297

.returns(ClassName("", "PersonBuilder"))

298

.addStatement("this.name = name")

299

.addStatement("return this")

300

.build()

301

)

302

.addFunction(

303

FunSpec.builder("age")

304

.addParameter("age", Int::class)

305

.returns(ClassName("", "PersonBuilder"))

306

.addStatement("this.age = age")

307

.addStatement("return this")

308

.build()

309

)

310

.addFunction(

311

FunSpec.builder("build")

312

.returns(ClassName("", "Person"))

313

.beginControlFlow("return %T(", ClassName("", "Person"))

314

.addStatement("name = checkNotNull(name) { %S }", "Name is required")

315

.addStatement("age = checkNotNull(age) { %S }", "Age is required")

316

.endControlFlow(")")

317

.build()

318

)

319

.build()

320

```

321

322

**Sealed Class Generation:**

323

324

```kotlin

325

val sealedResult = TypeSpec.classBuilder("Result")

326

.addModifiers(KModifier.SEALED)

327

.addTypeVariable(TypeVariableName.get("T"))

328

.addType(

329

TypeSpec.classBuilder("Success")

330

.addModifiers(KModifier.DATA)

331

.addTypeVariable(TypeVariableName.get("T"))

332

.superclass(

333

ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))

334

)

335

.primaryConstructor(

336

FunSpec.constructorBuilder()

337

.addParameter("data", TypeVariableName.get("T"))

338

.build()

339

)

340

.addProperty(

341

PropertySpec.builder("data", TypeVariableName.get("T"))

342

.initializer("data")

343

.build()

344

)

345

.build()

346

)

347

.addType(

348

TypeSpec.classBuilder("Error")

349

.addModifiers(KModifier.DATA)

350

.addTypeVariable(TypeVariableName.get("T"))

351

.superclass(

352

ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))

353

)

354

.primaryConstructor(

355

FunSpec.constructorBuilder()

356

.addParameter("exception", Throwable::class)

357

.build()

358

)

359

.addProperty(

360

PropertySpec.builder("exception", Throwable::class)

361

.initializer("exception")

362

.build()

363

)

364

.build()

365

)

366

.build()

367

```

368

369

**DSL Generation:**

370

371

```kotlin

372

val dslBuilder = TypeSpec.classBuilder("HtmlBuilder")

373

.addFunction(

374

FunSpec.builder("tag")

375

.addParameter("name", String::class)

376

.addParameter(

377

"block",

378

LambdaTypeName.get(

379

receiver = ClassName("", "TagBuilder"),

380

returnType = UNIT

381

)

382

)

383

.addStatement("val tagBuilder = TagBuilder(name)")

384

.addStatement("tagBuilder.block()")

385

.addStatement("append(tagBuilder.build())")

386

.build()

387

)

388

.addFunction(

389

FunSpec.builder("text")

390

.addParameter("content", String::class)

391

.addStatement("append(content)")

392

.build()

393

)

394

.build()

395

```

396

397

### Error Handling in Code Generation

398

399

Common patterns for handling errors and validation during code generation.

400

401

```kotlin

402

// Validation in generated code

403

val validatedProperty = PropertySpec.varBuilder("email", String::class)

404

.setter(

405

FunSpec.setterBuilder()

406

.addParameter("value", String::class)

407

.beginControlFlow("require(value.contains('@'))")

408

.addStatement("%S", "Invalid email format")

409

.endControlFlow()

410

.addStatement("field = value")

411

.build()

412

)

413

.build()

414

415

// Exception handling in generated methods

416

val safeOperation = FunSpec.builder("safeRead")

417

.addParameter("filename", String::class)

418

.returns(String::class.copy(nullable = true))

419

.beginControlFlow("return try")

420

.addStatement("%T(filename).readText()", File::class)

421

.nextControlFlow("catch (e: %T)", IOException::class)

422

.addStatement("null")

423

.endControlFlow()

424

.build()

425

```