or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-tool.mdcodemods.mdindex.mdmatchers.mdmetadata.mdnodes.mdparsing.mdutilities.mdvisitors.md

matchers.mddocs/

0

# Pattern Matching

1

2

LibCST's matcher system provides declarative pattern matching for finding, extracting, and replacing specific code patterns in concrete syntax trees. The system supports complex matching logic, data extraction, and integration with the visitor framework.

3

4

## Capabilities

5

6

### Core Matching Functions

7

8

Find and analyze code patterns declaratively without writing custom visitors.

9

10

```python { .api }

11

def matches(node: CSTNode, matcher: Union[CSTNode, MatcherPattern]) -> bool:

12

"""

13

Check if a node matches a given pattern.

14

15

Parameters:

16

- node: CST node to test

17

- matcher: Pattern to match against

18

19

Returns:

20

bool: True if node matches the pattern

21

"""

22

23

def findall(tree: CSTNode, matcher: Union[CSTNode, MatcherPattern]) -> Sequence[CSTNode]:

24

"""

25

Find all nodes in tree that match the pattern.

26

27

Parameters:

28

- tree: CST tree to search

29

- matcher: Pattern to match

30

31

Returns:

32

Sequence[CSTNode]: All matching nodes

33

"""

34

35

def extract(node: CSTNode, matcher: Union[CSTNode, MatcherPattern]) -> Dict[str, Union[CSTNode, Sequence[CSTNode]]]:

36

"""

37

Extract captured groups from a matching node.

38

39

Parameters:

40

- node: CST node that matches the pattern

41

- matcher: Pattern with capture groups

42

43

Returns:

44

Dict[str, Union[CSTNode, Sequence[CSTNode]]]: Captured groups by name

45

"""

46

47

def extractall(tree: CSTNode, matcher: Union[CSTNode, MatcherPattern]) -> Sequence[Dict[str, Union[CSTNode, Sequence[CSTNode]]]]:

48

"""

49

Extract captured groups from all matching nodes in tree.

50

51

Parameters:

52

- tree: CST tree to search

53

- matcher: Pattern with capture groups

54

55

Returns:

56

Sequence[Dict]: List of captured groups from each match

57

"""

58

59

def replace(tree: CSTNode, matcher: Union[CSTNode, MatcherPattern], replacement: Union[CSTNode, Callable]) -> CSTNode:

60

"""

61

Replace all matching nodes with replacement.

62

63

Parameters:

64

- tree: CST tree to transform

65

- matcher: Pattern to match

66

- replacement: Replacement node or callable

67

68

Returns:

69

CSTNode: Transformed tree

70

"""

71

```

72

73

### Matcher Combinators

74

75

Compose complex matching logic from simple patterns.

76

77

```python { .api }

78

class AllOf:

79

"""Match if all sub-matchers match."""

80

def __init__(self, *matchers: Union[CSTNode, MatcherPattern]) -> None: ...

81

82

class OneOf:

83

"""Match if any sub-matcher matches."""

84

def __init__(self, *matchers: Union[CSTNode, MatcherPattern]) -> None: ...

85

86

class AtLeastN:

87

"""Match if at least N sub-matchers match."""

88

def __init__(self, n: int, matcher: Union[CSTNode, MatcherPattern]) -> None: ...

89

90

class AtMostN:

91

"""Match if at most N sub-matchers match."""

92

def __init__(self, n: int, matcher: Union[CSTNode, MatcherPattern]) -> None: ...

93

94

class ZeroOrMore:

95

"""Match zero or more occurrences."""

96

def __init__(self, matcher: Union[CSTNode, MatcherPattern]) -> None: ...

97

98

class ZeroOrOne:

99

"""Match zero or one occurrence."""

100

def __init__(self, matcher: Union[CSTNode, MatcherPattern]) -> None: ...

101

102

class DoesNotMatch:

103

"""Inverse matcher - match if sub-matcher does not match."""

104

def __init__(self, matcher: Union[CSTNode, MatcherPattern]) -> None: ...

105

106

class DoNotCare:

107

"""Wildcard matcher - matches any node."""

108

```

109

110

### Special Matchers

111

112

Advanced matching capabilities for specific use cases.

113

114

```python { .api }

115

class MatchIfTrue:

116

"""Match based on predicate function."""

117

def __init__(self, func: Callable[[CSTNode], bool]) -> None: ...

118

119

class MatchRegex:

120

"""Match string content with regular expressions."""

121

def __init__(self, pattern: str, flags: int = 0) -> None: ...

122

123

class MatchMetadata:

124

"""Match based on metadata values."""

125

def __init__(self, provider: Type[BaseMetadataProvider], metadata: Any) -> None: ...

126

127

class MatchMetadataIfTrue:

128

"""Match based on metadata predicate."""

129

def __init__(self, provider: Type[BaseMetadataProvider], func: Callable[[Any], bool]) -> None: ...

130

131

class SaveMatchedNode:

132

"""Capture matched nodes with a name."""

133

def __init__(self, name: str, matcher: Union[CSTNode, MatcherPattern] = DoNotCare()) -> None: ...

134

135

class TypeOf:

136

"""Match nodes of specific type."""

137

def __init__(self, node_type: Type[CSTNode]) -> None: ...

138

```

139

140

### Visitor Integration

141

142

Integrate matchers with the visitor framework for enhanced traversal control.

143

144

```python { .api }

145

def call_if_inside(matcher: Union[CSTNode, MatcherPattern]) -> Callable:

146

"""

147

Decorator to call visitor method only if inside matching context.

148

149

Parameters:

150

- matcher: Pattern that must match ancestor node

151

152

Returns:

153

Callable: Decorator function

154

"""

155

156

def call_if_not_inside(matcher: Union[CSTNode, MatcherPattern]) -> Callable:

157

"""

158

Decorator to call visitor method only if not inside matching context.

159

160

Parameters:

161

- matcher: Pattern that must not match ancestor node

162

163

Returns:

164

Callable: Decorator function

165

"""

166

167

def visit(*matchers: Union[CSTNode, MatcherPattern]) -> Callable:

168

"""

169

Decorator to call visitor method only for matching nodes.

170

171

Parameters:

172

- matchers: Patterns that must match the current node

173

174

Returns:

175

Callable: Decorator function

176

"""

177

178

def leave(*matchers: Union[CSTNode, MatcherPattern]) -> Callable:

179

"""

180

Decorator to call leave method only for matching nodes.

181

182

Parameters:

183

- matchers: Patterns that must match the current node

184

185

Returns:

186

Callable: Decorator function

187

"""

188

```

189

190

### Matcher-Enabled Visitors

191

192

Base classes that provide matcher decorator support.

193

194

```python { .api }

195

class MatcherDecoratableVisitor(CSTVisitor):

196

"""Base visitor with matcher decorator support."""

197

198

class MatcherDecoratableTransformer(CSTTransformer):

199

"""Base transformer with matcher decorator support."""

200

201

class MatchDecoratorMismatch(Exception):

202

"""Raised when matcher decorators are used incorrectly."""

203

```

204

205

## Usage Examples

206

207

### Basic Pattern Matching

208

209

```python

210

import libcst as cst

211

from libcst import matchers as m

212

213

source = '''

214

def foo():

215

x = 42

216

y = "hello"

217

z = [1, 2, 3]

218

'''

219

220

module = cst.parse_module(source)

221

222

# Find all assignment statements

223

assignments = m.findall(module, m.Assign())

224

print(f"Found {len(assignments)} assignments")

225

226

# Check if specific pattern exists

227

has_string_assignment = m.matches(

228

module,

229

m.Module(body=[

230

m.AtLeastN(1, m.SimpleStatementLine(body=[

231

m.Assign(value=m.SimpleString())

232

]))

233

])

234

)

235

print(f"Has string assignment: {has_string_assignment}")

236

```

237

238

### Data Extraction

239

240

```python

241

import libcst as cst

242

from libcst import matchers as m

243

244

source = '''

245

def calculate(x, y):

246

return x + y

247

248

def process(data):

249

return data * 2

250

'''

251

252

module = cst.parse_module(source)

253

254

# Extract function names and parameter counts

255

function_pattern = m.FunctionDef(

256

name=m.SaveMatchedNode("name"),

257

params=m.SaveMatchedNode("params")

258

)

259

260

matches = m.extractall(module, function_pattern)

261

for match in matches:

262

name = match["name"].value

263

param_count = len(match["params"].params)

264

print(f"Function {name} has {param_count} parameters")

265

```

266

267

### Pattern Replacement

268

269

```python

270

import libcst as cst

271

from libcst import matchers as m

272

273

source = '''

274

def foo():

275

print("debug: starting")

276

result = calculate()

277

print("debug: finished")

278

return result

279

'''

280

281

module = cst.parse_module(source)

282

283

# Replace debug print statements with logging calls

284

debug_print = m.Call(

285

func=m.Name("print"),

286

args=[m.Arg(value=m.SimpleString(value=m.MatchRegex(r'"debug:.*"')))]

287

)

288

289

def make_logging_call(node):

290

# Extract the debug message

291

message = node.args[0].value.value

292

return cst.Call(

293

func=cst.Attribute(value=cst.Name("logging"), attr=cst.Name("debug")),

294

args=[cst.Arg(value=cst.SimpleString(message))]

295

)

296

297

new_module = m.replace(module, debug_print, make_logging_call)

298

print(new_module.code)

299

```

300

301

### Visitor with Matcher Decorators

302

303

```python

304

import libcst as cst

305

from libcst import matchers as m

306

307

class SecurityAnalyzer(m.MatcherDecoratableVisitor):

308

def __init__(self):

309

self.security_issues = []

310

311

@m.visit(m.Call(func=m.Name("eval")))

312

def visit_eval_call(self, node):

313

self.security_issues.append("Dangerous eval() call found")

314

315

@m.visit(m.Call(func=m.Name("exec")))

316

def visit_exec_call(self, node):

317

self.security_issues.append("Dangerous exec() call found")

318

319

@m.call_if_inside(m.FunctionDef(name=m.Name("__init__")))

320

@m.visit(m.Assign())

321

def visit_init_assignment(self, node):

322

# Only called for assignments inside __init__ methods

323

pass

324

325

# Usage

326

source = '''

327

class MyClass:

328

def __init__(self):

329

self.x = eval("42")

330

331

def process(self):

332

exec("print('hello')")

333

'''

334

335

module = cst.parse_module(source)

336

analyzer = SecurityAnalyzer()

337

module.visit(analyzer)

338

print(analyzer.security_issues)

339

```

340

341

### Complex Pattern Matching

342

343

```python

344

import libcst as cst

345

from libcst import matchers as m

346

347

# Find all function calls inside try blocks

348

complex_pattern = m.Try(

349

body=m.SimpleStatementSuite(body=[

350

m.ZeroOrMore(m.SimpleStatementLine(body=[

351

m.OneOf(

352

m.Expr(value=m.Call()), # Expression statements with calls

353

m.Assign(value=m.Call()) # Assignments with call values

354

)

355

]))

356

])

357

)

358

359

# Find functions that take at least 3 parameters

360

many_param_functions = m.FunctionDef(

361

params=m.Parameters(params=m.AtLeastN(3, m.Param()))

362

)

363

364

# Match string literals containing SQL keywords

365

sql_strings = m.SimpleString(

366

value=m.MatchRegex(r'".*\b(SELECT|INSERT|UPDATE|DELETE)\b.*"', re.IGNORECASE)

367

)

368

```

369

370

## Types

371

372

```python { .api }

373

# Core matcher types

374

MatcherPattern = Union[

375

CSTNode,

376

"AllOf",

377

"OneOf",

378

"AtLeastN",

379

"AtMostN",

380

"ZeroOrMore",

381

"ZeroOrOne",

382

"DoesNotMatch",

383

"DoNotCare",

384

"MatchIfTrue",

385

"MatchRegex",

386

"MatchMetadata",

387

"MatchMetadataIfTrue",

388

"SaveMatchedNode",

389

"TypeOf"

390

]

391

392

# Visitor decorator types

393

VisitorMethod = Callable[[CSTVisitor, CSTNode], None]

394

TransformerMethod = Callable[[CSTTransformer, CSTNode, CSTNode], Union[CSTNode, RemovalSentinel]]

395

396

# Exception types

397

class MatchDecoratorMismatch(Exception):

398

"""Raised when matcher decorators are misused."""

399

400

# Sentinel for wildcards

401

class DoNotCareSentinel:

402

"""Wildcard matcher that matches anything."""

403

404

DoNotCare: DoNotCareSentinel

405

```