or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

concurrency.mdconstant-value.mdcontracts.mdframework.mdindex-bounds.mdindex.mdnullness.mdoptional.mdresources.mdsignedness.mdstring-format.mdunits.md

string-format.mddocs/

0

# String and Format Annotations

1

2

String and format annotations validate string formats, regular expressions, and format strings to prevent runtime errors from malformed strings, invalid regex patterns, and mismatched format arguments.

3

4

## Capabilities

5

6

### Regular Expression Annotations

7

8

Annotations for validating regular expression patterns.

9

10

```java { .api }

11

/**

12

* Indicates that the annotated String is a valid regular expression

13

*/

14

@Documented

15

@Retention(RetentionPolicy.RUNTIME)

16

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

17

@SubtypeOf(UnknownRegex.class)

18

public @interface Regex {}

19

20

/**

21

* Indicates a partial regular expression (part of a larger pattern)

22

*/

23

@Documented

24

@Retention(RetentionPolicy.RUNTIME)

25

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

26

@SubtypeOf(Regex.class)

27

public @interface PartialRegex {}

28

29

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

30

@SubtypeOf({})

31

@DefaultQualifierInHierarchy

32

public @interface UnknownRegex {}

33

34

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

35

@SubtypeOf(Regex.class)

36

public @interface RegexBottom {}

37

38

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

39

@PolymorphicQualifier(UnknownRegex.class)

40

public @interface PolyRegex {}

41

```

42

43

**Usage Examples:**

44

45

```java

46

import org.checkerframework.checker.regex.qual.*;

47

import java.util.regex.Pattern;

48

49

public class RegexExample {

50

// Method requires a valid regex pattern

51

public Pattern compile(@Regex String pattern) {

52

return Pattern.compile(pattern); // Safe - pattern is validated

53

}

54

55

// Method that validates and returns regex

56

public @Regex String createEmailPattern() {

57

return "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}";

58

}

59

60

// Polymorphic method preserves regex properties

61

public @PolyRegex String process(@PolyRegex String input) {

62

return input.trim();

63

}

64

65

public void example() {

66

@Regex String emailRegex = createEmailPattern();

67

Pattern emailPattern = compile(emailRegex); // Safe

68

69

String userInput = getUserInput(); // Unknown if valid regex

70

// compile(userInput); // Would cause type error

71

72

if (isValidRegex(userInput)) {

73

@Regex String validPattern = (@Regex String) userInput;

74

Pattern pattern = compile(validPattern); // Safe after validation

75

}

76

}

77

78

private boolean isValidRegex(String pattern) {

79

try {

80

Pattern.compile(pattern);

81

return true;

82

} catch (Exception e) {

83

return false;

84

}

85

}

86

}

87

```

88

89

### Format String Annotations

90

91

Annotations for validating printf-style format strings and their arguments.

92

93

```java { .api }

94

/**

95

* Indicates a valid format string with specified conversion categories

96

*/

97

@Documented

98

@Retention(RetentionPolicy.RUNTIME)

99

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

100

@SubtypeOf(UnknownFormat.class)

101

public @interface Format {

102

ConversionCategory[] value();

103

}

104

105

/**

106

* Indicates an invalid format string

107

*/

108

@Documented

109

@Retention(RetentionPolicy.RUNTIME)

110

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

111

@SubtypeOf(UnknownFormat.class)

112

public @interface InvalidFormat {}

113

114

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

115

@SubtypeOf({})

116

@DefaultQualifierInHierarchy

117

public @interface UnknownFormat {}

118

119

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

120

@SubtypeOf(Format.class)

121

public @interface FormatBottom {}

122

123

/**

124

* Method that returns a format string

125

*/

126

@Target(ElementType.METHOD)

127

@Retention(RetentionPolicy.RUNTIME)

128

public @interface ReturnsFormat {}

129

130

/**

131

* Method that uses format strings (like printf)

132

*/

133

@Target(ElementType.METHOD)

134

@Retention(RetentionPolicy.RUNTIME)

135

public @interface FormatMethod {}

136

137

/**

138

* Categories of printf conversion specifiers

139

*/

140

public enum ConversionCategory {

141

UNUSED, // %% - literal %

142

GENERAL, // %s, %b, %h - any type

143

CHAR, // %c - char, Character, byte, Byte, short, Short, int, Integer

144

INT, // %d, %o, %x - integral types

145

FLOAT, // %e, %f, %g - floating point types

146

TIME, // %t - date/time types

147

CHAR_AND_INT, // %c or %d

148

INT_AND_TIME // %d or %t

149

}

150

```

151

152

**Usage Examples:**

153

154

```java

155

import org.checkerframework.checker.formatter.qual.*;

156

157

public class FormatExample {

158

// Method that requires a format string with specific argument types

159

@FormatMethod

160

public void logMessage(@Format({ConversionCategory.GENERAL, ConversionCategory.INT}) String format,

161

Object message, int count) {

162

System.out.printf(format, message, count); // Safe - format matches arguments

163

}

164

165

@ReturnsFormat({ConversionCategory.GENERAL, ConversionCategory.FLOAT})

166

public String createFormatString() {

167

return "User: %s, Score: %.2f";

168

}

169

170

public void example() {

171

// Valid format strings

172

@Format({ConversionCategory.GENERAL}) String nameFormat = "Hello, %s!";

173

@Format({ConversionCategory.INT, ConversionCategory.FLOAT}) String scoreFormat = "Score: %d/%.1f";

174

175

// Safe usage

176

System.out.printf(nameFormat, "Alice");

177

System.out.printf(scoreFormat, 85, 100.0);

178

179

// Using method-created format

180

@Format({ConversionCategory.GENERAL, ConversionCategory.FLOAT}) String userFormat = createFormatString();

181

System.out.printf(userFormat, "Bob", 92.5);

182

183

// Log with specific format requirements

184

logMessage("Processing %s: %d items", "data.txt", 150);

185

}

186

187

// Method that validates and converts unknown format strings

188

public @Format({ConversionCategory.GENERAL}) String validateSimpleFormat(String input) {

189

if (input.matches("^[^%]*%s[^%]*$")) {

190

return (@Format({ConversionCategory.GENERAL}) String) input;

191

}

192

throw new IllegalArgumentException("Invalid format string");

193

}

194

}

195

```

196

197

### Signature String Annotations

198

199

Annotations for Java signature strings used in reflection and bytecode operations.

200

201

```java { .api }

202

/**

203

* Java binary name (e.g., "java.lang.Object", "java.lang.Object$Inner")

204

*/

205

@Documented

206

@Retention(RetentionPolicy.RUNTIME)

207

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

208

@SubtypeOf(SignatureUnknown.class)

209

public @interface BinaryName {}

210

211

/**

212

* Java canonical name (e.g., "java.lang.Object", "java.lang.Object.Inner")

213

*/

214

@Documented

215

@Retention(RetentionPolicy.RUNTIME)

216

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

217

@SubtypeOf(SignatureUnknown.class)

218

public @interface CanonicalName {}

219

220

/**

221

* JVM field descriptor (e.g., "I", "Ljava/lang/String;")

222

*/

223

@Documented

224

@Retention(RetentionPolicy.RUNTIME)

225

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

226

@SubtypeOf(SignatureUnknown.class)

227

public @interface FieldDescriptor {}

228

229

/**

230

* JVM method descriptor (e.g., "(I)V", "(Ljava/lang/String;)I")

231

*/

232

@Documented

233

@Retention(RetentionPolicy.RUNTIME)

234

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

235

@SubtypeOf(SignatureUnknown.class)

236

public @interface MethodDescriptor {}

237

238

/**

239

* Java identifier (valid Java name)

240

*/

241

@Documented

242

@Retention(RetentionPolicy.RUNTIME)

243

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

244

@SubtypeOf(SignatureUnknown.class)

245

public @interface Identifier {}

246

247

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

248

@DefaultQualifierInHierarchy

249

public @interface SignatureUnknown {}

250

251

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

252

@SubtypeOf(BinaryName.class)

253

public @interface SignatureBottom {}

254

255

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

256

@PolymorphicQualifier(SignatureUnknown.class)

257

public @interface PolySignature {}

258

```

259

260

**Usage Examples:**

261

262

```java

263

import org.checkerframework.checker.signature.qual.*;

264

import java.lang.reflect.Field;

265

import java.lang.reflect.Method;

266

267

public class SignatureExample {

268

// Method that requires a valid Java binary name

269

public Class<?> loadClass(@BinaryName String className) throws ClassNotFoundException {

270

return Class.forName(className); // Safe - className is valid binary name

271

}

272

273

// Method that requires a field descriptor

274

public void analyzeField(@FieldDescriptor String descriptor) {

275

// Parse JVM field descriptor safely

276

char typeChar = descriptor.charAt(0);

277

switch (typeChar) {

278

case 'I': System.out.println("int field"); break;

279

case 'L': System.out.println("object field"); break;

280

// ... other cases

281

}

282

}

283

284

// Convert canonical name to binary name

285

public @BinaryName String canonicalToBinary(@CanonicalName String canonical) {

286

return canonical.replace('.', '$'); // Convert inner class separator

287

}

288

289

public void reflectionExample() throws Exception {

290

// Valid binary names

291

@BinaryName String stringClass = "java.lang.String";

292

@BinaryName String innerClass = "java.util.Map$Entry";

293

294

Class<?> clazz1 = loadClass(stringClass); // Safe

295

Class<?> clazz2 = loadClass(innerClass); // Safe

296

297

// Field descriptors

298

@FieldDescriptor String intDescriptor = "I";

299

@FieldDescriptor String stringDescriptor = "Ljava/lang/String;";

300

301

analyzeField(intDescriptor); // Safe

302

analyzeField(stringDescriptor); // Safe

303

304

// Method descriptors for reflection

305

@MethodDescriptor String voidMethod = "()V";

306

@MethodDescriptor String stringToInt = "(Ljava/lang/String;)I";

307

308

// Use in reflection calls (conceptual)

309

// Method method = getMethodByDescriptor(clazz1, stringToInt);

310

}

311

312

// Validate Java identifier

313

public boolean isValidIdentifier(@Identifier String name) {

314

return Character.isJavaIdentifierStart(name.charAt(0)) &&

315

name.chars().skip(1).allMatch(Character::isJavaIdentifierPart);

316

}

317

}

318

```

319

320

### Internationalization Format Annotations

321

322

Annotations for internationalized format strings and message keys.

323

324

```java { .api }

325

/**

326

* Valid internationalized format string

327

*/

328

@Documented

329

@Retention(RetentionPolicy.RUNTIME)

330

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

331

@SubtypeOf(I18nUnknownFormat.class)

332

public @interface I18nFormat {

333

I18nConversionCategory[] value();

334

}

335

336

/**

337

* Invalid internationalized format string

338

*/

339

@Documented

340

@Retention(RetentionPolicy.RUNTIME)

341

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

342

@SubtypeOf(I18nUnknownFormat.class)

343

public @interface I18nInvalidFormat {}

344

345

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

346

@DefaultQualifierInHierarchy

347

public @interface I18nUnknownFormat {}

348

349

/**

350

* I18n conversion categories (similar to printf categories)

351

*/

352

public enum I18nConversionCategory {

353

UNUSED,

354

GENERAL,

355

DATE,

356

NUMBER

357

}

358

359

/**

360

* Valid localizable key for resource bundles

361

*/

362

@Documented

363

@Retention(RetentionPolicy.RUNTIME)

364

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

365

@SubtypeOf(UnknownLocalizableKey.class)

366

public @interface LocalizableKey {}

367

368

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

369

@DefaultQualifierInHierarchy

370

public @interface UnknownLocalizableKey {}

371

372

/**

373

* String that has been localized

374

*/

375

@Documented

376

@Retention(RetentionPolicy.RUNTIME)

377

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

378

@SubtypeOf(UnknownLocalized.class)

379

public @interface Localized {}

380

381

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

382

@DefaultQualifierInHierarchy

383

public @interface UnknownLocalized {}

384

```

385

386

**Usage Examples:**

387

388

```java

389

import org.checkerframework.checker.i18n.qual.*;

390

import org.checkerframework.checker.i18nformatter.qual.*;

391

import java.util.ResourceBundle;

392

import java.text.MessageFormat;

393

394

public class I18nExample {

395

private ResourceBundle bundle = ResourceBundle.getBundle("messages");

396

397

// Method that requires a valid localizable key

398

public @Localized String getMessage(@LocalizableKey String key) {

399

return bundle.getString(key); // Safe - key is valid

400

}

401

402

// Method that formats i18n messages

403

public @Localized String formatMessage(@I18nFormat({I18nConversionCategory.GENERAL, I18nConversionCategory.NUMBER}) String pattern,

404

Object name, Number count) {

405

return MessageFormat.format(pattern, name, count); // Safe - format matches arguments

406

}

407

408

public void example() {

409

// Valid localizable keys (would be validated against resource bundle)

410

@LocalizableKey String welcomeKey = "welcome.message";

411

@LocalizableKey String errorKey = "error.file_not_found";

412

413

// Get localized strings

414

@Localized String welcome = getMessage(welcomeKey);

415

@Localized String error = getMessage(errorKey);

416

417

// Valid i18n format strings

418

@I18nFormat({I18nConversionCategory.GENERAL}) String nameFormat = "Hello, {0}!";

419

@I18nFormat({I18nConversionCategory.GENERAL, I18nConversionCategory.NUMBER})

420

String countFormat = "Processing {0}: {1} items";

421

422

// Safe formatting

423

@Localized String greeting = formatMessage(nameFormat, "Alice");

424

@Localized String status = formatMessage(countFormat, "data.txt", 150);

425

426

System.out.println(greeting);

427

System.out.println(status);

428

}

429

}

430

```

431

432

### Property Key Annotations

433

434

Annotations for validating property keys in configuration files.

435

436

```java { .api }

437

/**

438

* Valid property key for configuration files

439

*/

440

@Documented

441

@Retention(RetentionPolicy.RUNTIME)

442

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

443

@SubtypeOf(UnknownPropertyKey.class)

444

public @interface PropertyKey {}

445

446

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

447

@DefaultQualifierInHierarchy

448

public @interface UnknownPropertyKey {}

449

450

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

451

@SubtypeOf(PropertyKey.class)

452

public @interface PropertyKeyBottom {}

453

```

454

455

**Usage Examples:**

456

457

```java

458

import org.checkerframework.checker.propkey.qual.*;

459

import java.util.Properties;

460

461

public class PropertyExample {

462

private Properties config = new Properties();

463

464

// Method that requires a valid property key

465

public String getProperty(@PropertyKey String key) {

466

return config.getProperty(key); // Safe - key is validated

467

}

468

469

public void configure() {

470

// Valid property keys (would be validated against properties file)

471

@PropertyKey String dbUrlKey = "database.url";

472

@PropertyKey String dbUserKey = "database.username";

473

@PropertyKey String dbPassKey = "database.password";

474

475

// Safe property access

476

String dbUrl = getProperty(dbUrlKey);

477

String dbUser = getProperty(dbUserKey);

478

String dbPass = getProperty(dbPassKey);

479

480

// Use configuration values

481

connectToDatabase(dbUrl, dbUser, dbPass);

482

}

483

484

private void connectToDatabase(String url, String user, String password) {

485

// Database connection logic

486

}

487

}

488

```