thailint 0.5.0__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. src/cli.py +236 -2
  2. src/core/cli_utils.py +16 -1
  3. src/core/registry.py +1 -1
  4. src/formatters/__init__.py +22 -0
  5. src/formatters/sarif.py +202 -0
  6. src/linter_config/loader.py +5 -4
  7. src/linters/dry/block_filter.py +11 -8
  8. src/linters/dry/cache.py +3 -2
  9. src/linters/dry/duplicate_storage.py +5 -4
  10. src/linters/dry/violation_generator.py +1 -1
  11. src/linters/file_header/atemporal_detector.py +11 -11
  12. src/linters/file_header/base_parser.py +89 -0
  13. src/linters/file_header/bash_parser.py +58 -0
  14. src/linters/file_header/config.py +76 -16
  15. src/linters/file_header/css_parser.py +70 -0
  16. src/linters/file_header/field_validator.py +35 -29
  17. src/linters/file_header/linter.py +113 -121
  18. src/linters/file_header/markdown_parser.py +124 -0
  19. src/linters/file_header/python_parser.py +14 -58
  20. src/linters/file_header/typescript_parser.py +73 -0
  21. src/linters/file_header/violation_builder.py +13 -12
  22. src/linters/file_placement/linter.py +9 -11
  23. src/linters/method_property/__init__.py +49 -0
  24. src/linters/method_property/config.py +135 -0
  25. src/linters/method_property/linter.py +419 -0
  26. src/linters/method_property/python_analyzer.py +472 -0
  27. src/linters/method_property/violation_builder.py +116 -0
  28. src/linters/print_statements/config.py +7 -12
  29. src/linters/print_statements/linter.py +13 -15
  30. src/linters/print_statements/python_analyzer.py +8 -14
  31. src/linters/print_statements/typescript_analyzer.py +9 -14
  32. src/linters/print_statements/violation_builder.py +12 -14
  33. {thailint-0.5.0.dist-info → thailint-0.8.0.dist-info}/METADATA +155 -3
  34. {thailint-0.5.0.dist-info → thailint-0.8.0.dist-info}/RECORD +37 -25
  35. {thailint-0.5.0.dist-info → thailint-0.8.0.dist-info}/WHEEL +0 -0
  36. {thailint-0.5.0.dist-info → thailint-0.8.0.dist-info}/entry_points.txt +0 -0
  37. {thailint-0.5.0.dist-info → thailint-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,22 +6,20 @@ Scope: Validate file organization against allow/deny patterns
6
6
  Overview: Implements file placement validation using regex patterns from JSON/YAML config.
7
7
  Orchestrates configuration loading, pattern validation, path resolution, rule checking,
8
8
  and violation creation through focused helper classes. Supports directory-specific rules,
9
- global patterns, and generates helpful suggestions. Main linter class acts as coordinator.
9
+ global patterns, and generates helpful suggestions. Main linter class acts as coordinator
10
+ using composition pattern with specialized helper classes for configuration loading,
11
+ path resolution, pattern matching, and violation creation.
10
12
 
11
- Dependencies: src.core (base classes, types), pathlib, typing
13
+ Dependencies: src.core (base classes, types), pathlib, typing, json, yaml modules
12
14
 
13
15
  Exports: FilePlacementLinter, FilePlacementRule
14
16
 
15
- Implementation: Composition pattern with helper classes for each responsibility
17
+ Interfaces: lint_path(file_path) -> list[Violation], check_file_allowed(file_path) -> bool,
18
+ lint_directory(dir_path) -> list[Violation]
16
19
 
17
- SRP Exception: FilePlacementRule has 13 methods (exceeds max 8)
18
- Justification: Framework adapter class that bridges BaseLintRule interface with
19
- FilePlacementLinter implementation. Must handle multiple config sources (metadata vs file),
20
- multiple config formats (wrapped vs unwrapped), project root detection with fallbacks,
21
- and linter caching. This complexity is inherent to adapter pattern - splitting would
22
- create unnecessary indirection between framework and implementation without improving
23
- maintainability. All methods are focused on the single responsibility of integrating
24
- file placement validation with the linting framework.
20
+ Implementation: Composition pattern with helper classes for each responsibility
21
+ (ConfigLoader, PathResolver, PatternMatcher, PatternValidator, RuleChecker,
22
+ ViolationFactory)
25
23
  """
26
24
 
27
25
  import json
@@ -0,0 +1,49 @@
1
+ """
2
+ Purpose: Package exports for method-should-be-property linter
3
+
4
+ Scope: Method property linter public API
5
+
6
+ Overview: Exports the MethodPropertyRule class and MethodPropertyConfig dataclass for use by
7
+ the orchestrator and external consumers. Provides a convenience lint() function for
8
+ standalone usage of the linter.
9
+
10
+ Dependencies: MethodPropertyRule from linter module, MethodPropertyConfig from config module
11
+
12
+ Exports: MethodPropertyRule, MethodPropertyConfig, lint function
13
+
14
+ Interfaces: lint(file_path, content, config) -> list[Violation] convenience function
15
+
16
+ Implementation: Simple re-exports from submodules with optional convenience wrapper
17
+ """
18
+
19
+ from .config import MethodPropertyConfig
20
+ from .linter import MethodPropertyRule
21
+
22
+ __all__ = ["MethodPropertyRule", "MethodPropertyConfig", "lint"]
23
+
24
+
25
+ def lint(
26
+ file_path: str,
27
+ content: str,
28
+ config: dict | None = None,
29
+ ) -> list:
30
+ """Lint a file for method-should-be-property violations.
31
+
32
+ Args:
33
+ file_path: Path to the file being linted
34
+ content: Content of the file
35
+ config: Optional configuration dictionary
36
+
37
+ Returns:
38
+ List of Violation objects
39
+ """
40
+ from unittest.mock import Mock
41
+
42
+ rule = MethodPropertyRule()
43
+ context = Mock()
44
+ context.file_path = file_path
45
+ context.file_content = content
46
+ context.language = "python"
47
+ context.config = config
48
+
49
+ return rule.check(context)
@@ -0,0 +1,135 @@
1
+ """
2
+ Purpose: Configuration schema for method-should-be-property linter
3
+
4
+ Scope: Method property linter configuration for Python files
5
+
6
+ Overview: Defines configuration schema for method-should-be-property linter. Provides
7
+ MethodPropertyConfig dataclass with enabled flag, max_body_statements threshold (default 3)
8
+ for determining when a method body is too complex to be a property candidate, and ignore
9
+ patterns list for excluding specific files or directories. Includes configurable action verb
10
+ exclusions (prefixes and names) with sensible defaults that can be extended or overridden.
11
+ Supports per-file and per-directory config overrides through from_dict class method.
12
+ Integrates with orchestrator's configuration system via .thailint.yaml.
13
+
14
+ Dependencies: dataclasses module for configuration structure, typing module for type hints
15
+
16
+ Exports: MethodPropertyConfig dataclass, DEFAULT_EXCLUDE_PREFIXES, DEFAULT_EXCLUDE_NAMES
17
+
18
+ Interfaces: from_dict(config, language) -> MethodPropertyConfig for configuration loading
19
+
20
+ Implementation: Dataclass with defaults matching Pythonic conventions and common use cases
21
+ """
22
+
23
+ from dataclasses import dataclass, field
24
+ from typing import Any
25
+
26
+ # Default action verb prefixes - methods starting with these are excluded
27
+ # These represent actions/transformations, not property access
28
+ DEFAULT_EXCLUDE_PREFIXES: tuple[str, ...] = (
29
+ "to_", # Transformation: to_dict, to_json, to_string
30
+ "dump_", # Serialization: dump_to_json, dump_to_apigw
31
+ "generate_", # Factory: generate_report, generate_html
32
+ "create_", # Factory: create_instance, create_config
33
+ "build_", # Construction: build_query, build_html
34
+ "make_", # Factory: make_request, make_connection
35
+ "render_", # Output: render_template, render_html
36
+ "compute_", # Calculation: compute_hash, compute_total
37
+ "calculate_", # Calculation: calculate_sum, calculate_average
38
+ )
39
+
40
+ # Default action verb names - exact method names that are excluded
41
+ # These are lifecycle hooks, display actions, and resource operations
42
+ DEFAULT_EXCLUDE_NAMES: frozenset[str] = frozenset(
43
+ {
44
+ "finalize", # Lifecycle hook
45
+ "serialize", # Transformation
46
+ "dump", # Serialization
47
+ "validate", # Validation action
48
+ "show", # Display action
49
+ "display", # Display action
50
+ "print", # Output action
51
+ "refresh", # Update action
52
+ "reset", # State action
53
+ "clear", # State action
54
+ "close", # Resource action
55
+ "open", # Resource action
56
+ "save", # Persistence action
57
+ "load", # Persistence action
58
+ "execute", # Action
59
+ "run", # Action
60
+ }
61
+ )
62
+
63
+
64
+ def _load_list_config(
65
+ config: dict[str, Any], key: str, override_key: str, default: tuple[str, ...]
66
+ ) -> tuple[str, ...]:
67
+ """Load a list config with extend/override semantics."""
68
+ if override_key in config and isinstance(config[override_key], list):
69
+ return tuple(config[override_key])
70
+ if key in config and isinstance(config[key], list):
71
+ return default + tuple(config[key])
72
+ return default
73
+
74
+
75
+ def _load_set_config(
76
+ config: dict[str, Any], key: str, override_key: str, default: frozenset[str]
77
+ ) -> frozenset[str]:
78
+ """Load a set config with extend/override semantics."""
79
+ if override_key in config and isinstance(config[override_key], list):
80
+ return frozenset(config[override_key])
81
+ if key in config and isinstance(config[key], list):
82
+ return default | frozenset(config[key])
83
+ return default
84
+
85
+
86
+ @dataclass
87
+ class MethodPropertyConfig: # thailint: ignore[dry]
88
+ """Configuration for method-should-be-property linter."""
89
+
90
+ enabled: bool = True
91
+ max_body_statements: int = 3
92
+ ignore: list[str] = field(default_factory=list)
93
+ ignore_methods: list[str] = field(default_factory=list)
94
+
95
+ # Action verb exclusions (extend defaults or override)
96
+ exclude_prefixes: tuple[str, ...] = DEFAULT_EXCLUDE_PREFIXES
97
+ exclude_names: frozenset[str] = DEFAULT_EXCLUDE_NAMES
98
+
99
+ # dry: ignore-block
100
+ @classmethod
101
+ def from_dict(
102
+ cls, config: dict[str, Any] | None, language: str | None = None
103
+ ) -> "MethodPropertyConfig":
104
+ """Load configuration from dictionary.
105
+
106
+ Args:
107
+ config: Dictionary containing configuration values, or None
108
+ language: Programming language (unused, for interface compatibility)
109
+
110
+ Returns:
111
+ MethodPropertyConfig instance with values from dictionary
112
+ """
113
+ if config is None:
114
+ return cls()
115
+
116
+ ignore_patterns = config.get("ignore", [])
117
+ if not isinstance(ignore_patterns, list):
118
+ ignore_patterns = []
119
+
120
+ ignore_methods = config.get("ignore_methods", [])
121
+ if not isinstance(ignore_methods, list):
122
+ ignore_methods = []
123
+
124
+ return cls(
125
+ enabled=config.get("enabled", True),
126
+ max_body_statements=config.get("max_body_statements", 3),
127
+ ignore=ignore_patterns,
128
+ ignore_methods=ignore_methods,
129
+ exclude_prefixes=_load_list_config(
130
+ config, "exclude_prefixes", "exclude_prefixes_override", DEFAULT_EXCLUDE_PREFIXES
131
+ ),
132
+ exclude_names=_load_set_config(
133
+ config, "exclude_names", "exclude_names_override", DEFAULT_EXCLUDE_NAMES
134
+ ),
135
+ )
@@ -0,0 +1,419 @@
1
+ """
2
+ Purpose: Main method-should-be-property linter rule implementation
3
+
4
+ Scope: Method-should-be-property detection for Python files
5
+
6
+ Overview: Implements method-should-be-property linter rule following MultiLanguageLintRule
7
+ interface. Orchestrates configuration loading, Python AST analysis for property candidates,
8
+ and violation building through focused helper classes. Detects methods that should be
9
+ converted to @property decorators following Pythonic conventions. Supports configurable
10
+ max_body_statements threshold, ignore patterns for excluding files, and inline ignore
11
+ directives (thailint: ignore, noqa) for suppressing specific violations. Handles test file
12
+ detection and non-Python languages gracefully.
13
+
14
+ Dependencies: BaseLintContext and MultiLanguageLintRule from core, ast module, pathlib,
15
+ analyzer classes, config classes
16
+
17
+ Exports: MethodPropertyRule class implementing MultiLanguageLintRule interface
18
+
19
+ Interfaces: check(context) -> list[Violation] for rule validation, standard rule properties
20
+ (rule_id, rule_name, description)
21
+
22
+ Implementation: Composition pattern with helper classes (analyzer, violation builder),
23
+ AST-based analysis for Python with comprehensive exclusion rules
24
+ """
25
+
26
+ import ast
27
+ from pathlib import Path
28
+
29
+ from src.core.base import BaseLintContext, MultiLanguageLintRule
30
+ from src.core.types import Violation
31
+
32
+ from .config import MethodPropertyConfig
33
+ from .python_analyzer import PropertyCandidate, PythonMethodAnalyzer
34
+ from .violation_builder import ViolationBuilder
35
+
36
+
37
+ class MethodPropertyRule(MultiLanguageLintRule): # thailint: ignore[srp,dry]
38
+ """Detects methods that should be @property decorators."""
39
+
40
+ def __init__(self) -> None:
41
+ """Initialize the method property rule."""
42
+ self._violation_builder = ViolationBuilder(self.rule_id)
43
+
44
+ @property
45
+ def rule_id(self) -> str:
46
+ """Unique identifier for this rule."""
47
+ return "method-property.should-be-property"
48
+
49
+ @property
50
+ def rule_name(self) -> str:
51
+ """Human-readable name for this rule."""
52
+ return "method should be property"
53
+
54
+ @property
55
+ def description(self) -> str:
56
+ """Description of what this rule checks."""
57
+ return "Methods should be converted to @property decorators for Pythonic attribute access"
58
+
59
+ def _load_config(self, context: BaseLintContext) -> MethodPropertyConfig:
60
+ """Load configuration from context.
61
+
62
+ Args:
63
+ context: Lint context
64
+
65
+ Returns:
66
+ MethodPropertyConfig instance
67
+ """
68
+ test_config = self._try_load_test_config(context)
69
+ if test_config is not None:
70
+ return test_config
71
+
72
+ return MethodPropertyConfig()
73
+
74
+ # dry: ignore-block
75
+ def _try_load_test_config(self, context: BaseLintContext) -> MethodPropertyConfig | None:
76
+ """Try to load test-style configuration.
77
+
78
+ Args:
79
+ context: Lint context
80
+
81
+ Returns:
82
+ Config if found, None otherwise
83
+ """
84
+ if not hasattr(context, "config"):
85
+ return None
86
+ config_attr = context.config
87
+ if config_attr is None or not isinstance(config_attr, dict):
88
+ return None
89
+
90
+ # Check for method-property specific config
91
+ linter_config = config_attr.get("method-property", config_attr)
92
+ return MethodPropertyConfig.from_dict(linter_config)
93
+
94
+ # dry: ignore-block
95
+ def _is_file_ignored(self, context: BaseLintContext, config: MethodPropertyConfig) -> bool:
96
+ """Check if file matches ignore patterns.
97
+
98
+ Args:
99
+ context: Lint context
100
+ config: Configuration
101
+
102
+ Returns:
103
+ True if file should be ignored
104
+ """
105
+ if not config.ignore:
106
+ return False
107
+
108
+ if not context.file_path:
109
+ return False
110
+
111
+ file_path = Path(context.file_path)
112
+ for pattern in config.ignore:
113
+ if self._matches_pattern(file_path, pattern):
114
+ return True
115
+ return False
116
+
117
+ # dry: ignore-block
118
+ def _matches_pattern(self, file_path: Path, pattern: str) -> bool:
119
+ """Check if file path matches a glob pattern.
120
+
121
+ Args:
122
+ file_path: Path to check
123
+ pattern: Glob pattern
124
+
125
+ Returns:
126
+ True if path matches pattern
127
+ """
128
+ if file_path.match(pattern):
129
+ return True
130
+ if pattern in str(file_path):
131
+ return True
132
+ return False
133
+
134
+ # dry: ignore-block
135
+ def _is_test_file(self, file_path: object) -> bool:
136
+ """Check if file is a test file.
137
+
138
+ Args:
139
+ file_path: Path to check
140
+
141
+ Returns:
142
+ True if test file
143
+ """
144
+ path_str = str(file_path)
145
+ file_name = Path(path_str).name
146
+
147
+ # Check test_*.py pattern
148
+ if file_name.startswith("test_") and file_name.endswith(".py"):
149
+ return True
150
+
151
+ # Check *_test.py pattern
152
+ if file_name.endswith("_test.py"):
153
+ return True
154
+
155
+ return False
156
+
157
+ def _check_python(
158
+ self, context: BaseLintContext, config: MethodPropertyConfig
159
+ ) -> list[Violation]:
160
+ """Check Python code for method property violations.
161
+
162
+ Args:
163
+ context: Lint context with Python file information
164
+ config: Method property configuration
165
+
166
+ Returns:
167
+ List of violations found in Python code
168
+ """
169
+ if self._is_file_ignored(context, config):
170
+ return []
171
+
172
+ if self._is_test_file(context.file_path):
173
+ return []
174
+
175
+ tree = self._parse_python_code(context.file_content)
176
+ if tree is None:
177
+ return []
178
+
179
+ analyzer = PythonMethodAnalyzer(
180
+ max_body_statements=config.max_body_statements,
181
+ exclude_prefixes=config.exclude_prefixes,
182
+ exclude_names=config.exclude_names,
183
+ )
184
+ candidates = analyzer.find_property_candidates(tree)
185
+ candidates = self._filter_ignored_methods(candidates, config)
186
+ return self._collect_violations(candidates, context)
187
+
188
+ def _filter_ignored_methods(
189
+ self,
190
+ candidates: list[PropertyCandidate],
191
+ config: MethodPropertyConfig,
192
+ ) -> list[PropertyCandidate]:
193
+ """Filter out candidates with ignored method names.
194
+
195
+ Args:
196
+ candidates: List of property candidates
197
+ config: Configuration with ignore_methods list
198
+
199
+ Returns:
200
+ Filtered list of candidates
201
+ """
202
+ if not config.ignore_methods:
203
+ return candidates
204
+ return [c for c in candidates if c.method_name not in config.ignore_methods]
205
+
206
+ # dry: ignore-block
207
+ def _parse_python_code(self, code: str | None) -> ast.AST | None:
208
+ """Parse Python code into AST.
209
+
210
+ Args:
211
+ code: Python source code
212
+
213
+ Returns:
214
+ AST or None if parse fails
215
+ """
216
+ try:
217
+ return ast.parse(code or "")
218
+ except SyntaxError:
219
+ return None
220
+
221
+ def _collect_violations(
222
+ self,
223
+ candidates: list[PropertyCandidate],
224
+ context: BaseLintContext,
225
+ ) -> list[Violation]:
226
+ """Collect violations from property candidates.
227
+
228
+ Args:
229
+ candidates: List of property candidates
230
+ context: Lint context
231
+
232
+ Returns:
233
+ List of violations
234
+ """
235
+ violations = []
236
+ for candidate in candidates:
237
+ violation = self._create_violation(candidate, context)
238
+ if not self._should_ignore(violation, candidate, context):
239
+ violations.append(violation)
240
+ return violations
241
+
242
+ def _create_violation(
243
+ self,
244
+ candidate: PropertyCandidate,
245
+ context: BaseLintContext,
246
+ ) -> Violation:
247
+ """Create a violation for a property candidate.
248
+
249
+ Args:
250
+ candidate: The property candidate
251
+ context: Lint context
252
+
253
+ Returns:
254
+ Violation object
255
+ """
256
+ return self._violation_builder.create_violation(
257
+ method_name=candidate.method_name,
258
+ line=candidate.line,
259
+ column=candidate.column,
260
+ file_path=context.file_path,
261
+ is_get_prefix=candidate.is_get_prefix,
262
+ class_name=candidate.class_name,
263
+ )
264
+
265
+ def _should_ignore(
266
+ self,
267
+ violation: Violation,
268
+ candidate: PropertyCandidate,
269
+ context: BaseLintContext,
270
+ ) -> bool:
271
+ """Check if violation should be ignored based on directives.
272
+
273
+ Args:
274
+ violation: Violation to check
275
+ candidate: The property candidate
276
+ context: Lint context
277
+
278
+ Returns:
279
+ True if violation should be ignored
280
+ """
281
+ if self._has_inline_ignore(violation, context):
282
+ return True
283
+ if self._has_docstring_ignore(candidate, context):
284
+ return True
285
+ return False
286
+
287
+ # dry: ignore-block
288
+ def _has_inline_ignore(self, violation: Violation, context: BaseLintContext) -> bool:
289
+ """Check for inline ignore directive on method line.
290
+
291
+ Args:
292
+ violation: Violation to check
293
+ context: Lint context
294
+
295
+ Returns:
296
+ True if line has ignore directive
297
+ """
298
+ line_text = self._get_line_text(violation.line, context)
299
+ if line_text is None:
300
+ return False
301
+
302
+ line_lower = line_text.lower()
303
+
304
+ # Check for thailint: ignore[method-property]
305
+ if "thailint:" in line_lower and "ignore" in line_lower:
306
+ return True
307
+
308
+ # Check for noqa
309
+ if "# noqa" in line_lower:
310
+ return True
311
+
312
+ return False
313
+
314
+ def _has_docstring_ignore(
315
+ self,
316
+ candidate: PropertyCandidate,
317
+ context: BaseLintContext,
318
+ ) -> bool:
319
+ """Check for ignore directive in method docstring.
320
+
321
+ Args:
322
+ candidate: Property candidate
323
+ context: Lint context
324
+
325
+ Returns:
326
+ True if docstring has ignore directive
327
+ """
328
+ tree = self._parse_python_code(context.file_content)
329
+ if tree is None:
330
+ return False
331
+
332
+ docstring = self._find_method_docstring(tree, candidate)
333
+ if docstring is None:
334
+ return False
335
+
336
+ docstring_lower = docstring.lower()
337
+ return "thailint: ignore" in docstring_lower
338
+
339
+ def _find_method_docstring(
340
+ self,
341
+ tree: ast.AST,
342
+ candidate: PropertyCandidate,
343
+ ) -> str | None:
344
+ """Find the docstring for a method.
345
+
346
+ Args:
347
+ tree: AST tree
348
+ candidate: Property candidate
349
+
350
+ Returns:
351
+ Docstring text or None
352
+ """
353
+ target_class = self._find_class_node(tree, candidate.class_name)
354
+ if target_class is None:
355
+ return None
356
+ return self._find_method_in_class(target_class, candidate.method_name)
357
+
358
+ def _find_class_node(self, tree: ast.AST, class_name: str) -> ast.ClassDef | None:
359
+ """Find a class node by name in the AST.
360
+
361
+ Args:
362
+ tree: AST tree
363
+ class_name: Name of the class to find
364
+
365
+ Returns:
366
+ ClassDef node or None
367
+ """
368
+ for node in ast.walk(tree):
369
+ if isinstance(node, ast.ClassDef) and node.name == class_name:
370
+ return node
371
+ return None
372
+
373
+ def _find_method_in_class(self, class_node: ast.ClassDef, method_name: str) -> str | None:
374
+ """Find method docstring within a class.
375
+
376
+ Args:
377
+ class_node: Class node to search
378
+ method_name: Method name to find
379
+
380
+ Returns:
381
+ Docstring or None
382
+ """
383
+ for item in class_node.body:
384
+ if isinstance(item, ast.FunctionDef) and item.name == method_name:
385
+ return ast.get_docstring(item)
386
+ return None
387
+
388
+ def _get_line_text(self, line: int, context: BaseLintContext) -> str | None:
389
+ """Get the text of a specific line.
390
+
391
+ Args:
392
+ line: Line number (1-indexed)
393
+ context: Lint context
394
+
395
+ Returns:
396
+ Line text or None
397
+ """
398
+ if not context.file_content:
399
+ return None
400
+
401
+ lines = context.file_content.splitlines()
402
+ if line <= 0 or line > len(lines):
403
+ return None
404
+
405
+ return lines[line - 1]
406
+
407
+ def _check_typescript(
408
+ self, context: BaseLintContext, config: MethodPropertyConfig
409
+ ) -> list[Violation]:
410
+ """Check TypeScript code for violations.
411
+
412
+ Args:
413
+ context: Lint context
414
+ config: Configuration
415
+
416
+ Returns:
417
+ Empty list (not implemented for TypeScript)
418
+ """
419
+ return []