# Dart Rule Execution Flow

> Tài liệu mô tả chi tiết luồng hoạt động của một rule khi phân tích code Dart trong SunLint.
> Lấy ví dụ: **Rule C002 - No Duplicate Code**

## 1. Tổng quan Architecture

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                              SUNLINT ARCHITECTURE                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────┐    ┌──────────────────┐    ┌─────────────────────────────┐ │
│  │   CLI       │───▶│  Heuristic       │───▶│  DartAnalyzer (JS)          │ │
│  │  (cli.js)   │    │  Engine          │    │  (dart-analyzer.js)         │ │
│  └─────────────┘    └──────────────────┘    └──────────────┬──────────────┘ │
│                                                            │                │
│                                              JSON-RPC over STDIO            │
│                                                            │                │
│                     ┌──────────────────────────────────────▼──────────────┐ │
│                     │           DART BINARY (sunlint-dart-macos)          │ │
│                     │  ┌─────────────────┐  ┌──────────────────────────┐  │ │
│                     │  │ JsonRpcServer   │  │ AnalyzerService          │  │ │
│                     │  └────────┬────────┘  └────────────┬─────────────┘  │ │
│                     │           │                        │                │ │
│                     │           ▼                        ▼                │ │
│                     │  ┌─────────────────────────────────────────────┐    │ │
│                     │  │     lib/rules/C002_no_duplicate_code.dart   │    │ │
│                     │  └─────────────────────────────────────────────┘    │ │
│                     └─────────────────────────────────────────────────────┘ │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

---

## 2. Chi tiết từng bước

### Step 1: CLI Command

```bash
node cli.js --rule=C002 \
  --input="examples/rule-test-fixtures/dart-rules/C002_no_duplicate_code/violations" \
  --engine=heuristic \
  --languages=dart \
  --include="**/*.dart"
```

**File:** `cli.js`

**Chức năng:**
- Parse command line arguments
- Load configuration
- Initialize engines
- Dispatch to appropriate engine

---

### Step 2: Heuristic Engine

**File:** `engines/heuristic-engine.js`

#### 2.1. Detect Dart Support

```javascript
// Line 27-51
function detectDartSupport(ruleId) {
  const rulesBasePath = path.join(__dirname, '../rules');
  const categories = ['common', 'security', 'typescript'];

  for (const category of categories) {
    const categoryPath = path.join(rulesBasePath, category);
    // Scan for: rules/common/C002_no_duplicate_code/dart/
    for (const folder of ruleFolders) {
      if (folder.startsWith(ruleId + '_')) {
        const dartPath = path.join(categoryPath, folder, 'dart');
        if (fs.existsSync(dartPath)) {
          return true;  // ✅ C002 supports Dart
        }
      }
    }
  }
  return false;
}
```

#### 2.2. Check DartAnalyzer Availability

```javascript
// Line 922-927
const isDartRule = detectDartSupport(rule.id);      // true for C002
const hasDartFiles = filesByLanguage['dart'].length > 0;
const dartAnalyzer = this.semanticEngineManager?.getAnalyzer('dart');
const useDartAnalyzer = isDartRule && hasDartFiles && dartAnalyzer?.isReady();
```

#### 2.3. Call DartAnalyzer

```javascript
// Line 948-972
if (useDartAnalyzer) {
  const dartFiles = filesByLanguage['dart'];
  const rules = [{ id: rule.id, config: rule.config || {} }];

  for (const filePath of dartFiles) {
    const violations = await dartAnalyzer.analyzeFile(filePath, rules, options);
    ruleViolations.push(...violations);
  }
}
```

---

### Step 3: Dart Analyzer Client (JavaScript)

**File:** `core/adapters/dart-analyzer.js`

#### 3.1. Resolve Binary

```javascript
// Line 288-337
async resolveBinary() {
  const platform = process.platform;
  const binaryName = platform === 'darwin'
    ? 'sunlint-dart-macos'
    : `sunlint-dart-${platform}`;

  // Priority 1: Bundled binary
  const bundledPath = path.join(__dirname, '../../dart_analyzer/bin', binaryName);
  if (fs.existsSync(bundledPath)) {
    return bundledPath;
  }
  // ... fallback options
}
```

#### 3.2. Start Subprocess

```javascript
// Line 35-81
async start(binaryPath) {
  this.process = spawn(binaryPath, [], {
    stdio: ['pipe', 'pipe', 'pipe'],
    env: { ...process.env }
  });

  // Handle stdout (JSON-RPC responses)
  this.process.stdout.on('data', (data) => {
    this.handleData(data.toString());
  });
}
```

#### 3.3. Send JSON-RPC Request

```javascript
// Line 368-405
async analyzeFile(filePath, rules, options = {}) {
  if (this.client && this.client.isConnected()) {
    // Send JSON-RPC request
    const result = await this.client.sendRequest('analyze', {
      filePath,
      rules: rules.map(r => ({
        id: r.id || r.ruleId,
        config: r.config || {}
      }))
    });
    violations = result.violations || [];
  }
}
```

**JSON-RPC Request Format:**
```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "analyze",
  "params": {
    "filePath": "/path/to/file.dart",
    "rules": [{ "id": "C002", "config": {} }]
  }
}
```

---

### Step 4: Dart Binary - JSON-RPC Server

**File:** `dart_analyzer/lib/json_rpc_server.dart`

#### 4.1. Receive Request

```dart
// Line 48-64
Future<void> _handleLine(String line) async {
  final request = jsonDecode(line) as Map<String, dynamic>;
  final response = await _processRequest(request);
  _sendResponse(response);
}
```

#### 4.2. Route to Handler

```dart
// Line 106-122
Future<dynamic> _invokeMethod(String method, Map<String, dynamic> params) async {
  switch (method) {
    case 'initialize':
      return await _handleInitialize(params);
    case 'analyze':
      return await _handleAnalyze(params);  // ← C002 goes here
    case 'getSymbolTable':
      return await _handleGetSymbolTable(params);
    // ...
  }
}
```

#### 4.3. Handle Analyze Request

```dart
// Line 156-174
Future<Map<String, dynamic>> _handleAnalyze(Map<String, dynamic> params) async {
  final filePath = params['filePath'] as String?;
  final rulesData = params['rules'] as List<dynamic>?;

  final violations = await _analyzerService.analyzeFile(
    filePath: filePath!,
    rulesData: rulesData,
  );

  return {
    'violations': violations.map((v) => v.toJson()).toList(),
    'fileAnalyzed': filePath,
  };
}
```

---

### Step 5: Analyzer Service

**File:** `dart_analyzer/lib/analyzer_service.dart`

#### 5.1. Register Analyzers

```dart
// Line 36-46
void _registerAnalyzers() {
  // Common rules (C-series)
  _analyzers['C002'] = C002NoDuplicateCodeAnalyzer();
  _analyzers['C003'] = C003NoVagueAbbreviationsAnalyzer();

  // Security rules (S-series)
  _analyzers['S003'] = S003OpenRedirectProtectionAnalyzer();
  _analyzers['S004'] = S004SensitiveDataLoggingAnalyzer();
}
```

#### 5.2. Analyze File

```dart
// Line 204-257
Future<List<Violation>> analyzeFile({
  required String filePath,
  List<dynamic>? rulesData,
}) async {
  // Get resolved AST from Dart Analyzer package
  final context = _contextCollection!.contextFor(absolutePath);
  final result = await context.currentSession.getResolvedUnit(absolutePath);

  final unit = result.unit;
  final violations = <Violation>[];

  // Run each applicable analyzer
  for (final rule in rules) {
    final analyzer = _analyzers[rule.id];  // Get C002 analyzer
    if (analyzer == null) continue;

    final ruleViolations = analyzer.analyze(
      unit: unit,
      filePath: absolutePath,
      rule: rule,
      lineInfo: result.lineInfo,
    );
    violations.addAll(ruleViolations);
  }

  return violations;
}
```

---

### Step 6: C002 Rule Analyzer

**File:** `dart_analyzer/lib/rules/C002_no_duplicate_code.dart`

#### 6.1. Analyzer Class

```dart
class C002NoDuplicateCodeAnalyzer extends BaseAnalyzer {
  @override
  String get ruleId => 'C002';

  static const int minDuplicateLines = 10;
  static const double similarityThreshold = 0.85;

  final Map<String, List<_CodeBlock>> _codeBlocks = {};
}
```

#### 6.2. Analyze Method

```dart
// Line 33-51
@override
List<Violation> analyze({
  required CompilationUnit unit,
  required String filePath,
  required Rule rule,
  required LineInfo lineInfo,
}) {
  final violations = <Violation>[];

  // Create AST visitor
  final visitor = _DuplicateCodeVisitor(
    filePath: filePath,
    lineInfo: lineInfo,
    violations: violations,
    analyzer: this,
    codeBlocks: _codeBlocks,
  );

  // Traverse AST
  unit.accept(visitor);

  // Detect duplicates
  _detectDuplicates(violations);

  return violations;
}
```

#### 6.3. AST Visitor

```dart
class _DuplicateCodeVisitor extends RecursiveAstVisitor<void> {
  @override
  void visitFunctionDeclaration(FunctionDeclaration node) {
    final body = node.functionExpression.body;
    if (body is BlockFunctionBody) {
      _processBlock(body.block, 'function', node.name.lexeme);
    }
    super.visitFunctionDeclaration(node);
  }

  @override
  void visitMethodDeclaration(MethodDeclaration node) {
    final body = node.body;
    if (body is BlockFunctionBody) {
      _processBlock(body.block, 'method', node.name.lexeme);
    }
    super.visitMethodDeclaration(node);
  }
}
```

#### 6.4. Process Code Block

```dart
void _processBlock(Block block, String type, String name) {
  final startLine = analyzer.getLine(lineInfo, block.offset);
  final endLine = analyzer.getLine(lineInfo, block.end);
  final lineCount = endLine - startLine + 1;

  // Skip if less than 10 lines
  if (lineCount < C002NoDuplicateCodeAnalyzer.minDuplicateLines) return;

  // Normalize code for comparison
  final normalizedLines = _normalizeBlock(block);
  final hash = _hashLines(normalizedLines);

  // Store for comparison
  codeBlocks.putIfAbsent(filePath, () => []).add(_CodeBlock(
    filePath: filePath,
    startLine: startLine,
    endLine: endLine,
    hash: hash,
    normalizedLines: normalizedLines,
    type: '$type:$name',
  ));
}
```

#### 6.5. Detect Duplicates

```dart
void _detectDuplicates(List<Violation> violations) {
  final allBlocks = _codeBlocks.values.expand((b) => b).toList();

  for (var i = 0; i < allBlocks.length; i++) {
    for (var j = i + 1; j < allBlocks.length; j++) {
      final block1 = allBlocks[i];
      final block2 = allBlocks[j];

      // Check for exact match via hash
      if (block1.hash == block2.hash) {
        violations.add(createViolation(
          filePath: block2.filePath,
          line: block2.startLine,
          column: 1,
          message: 'Duplicate code block found (${block2.lineCount} lines)...',
          severity: 'warning',
        ));
      }
      // Check for similar code (LCS algorithm)
      else {
        final similarity = _calculateSimilarity(block1, block2);
        if (similarity >= similarityThreshold) {
          violations.add(createViolation(...));
        }
      }
    }
  }
}
```

---

### Step 7: Response Flow

```
C002 Analyzer
    ↓ List<Violation>
AnalyzerService
    ↓ violations.map((v) => v.toJson())
JsonRpcServer
    ↓ JSON-RPC Response
DartAnalyzerClient (JS)
    ↓ result.violations
HeuristicEngine
    ↓ ruleViolations
CLI Output
```

**JSON-RPC Response Format:**
```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "violations": [
      {
        "ruleId": "C002",
        "filePath": "/path/to/file.dart",
        "line": 31,
        "column": 1,
        "message": "Duplicate code block found (19 lines) - same as line 10",
        "severity": "warning",
        "analysisMethod": "ast",
        "metadata": {
          "duplicateOf": "/path/to/file.dart:10",
          "lineCount": 19,
          "similarity": 1.0
        }
      }
    ],
    "fileAnalyzed": "/path/to/file.dart"
  }
}
```

---

## 3. File Structure

### Rule Definition (TypeScript side)

```
rules/common/C002_no_duplicate_code/
├── config.json              # Rule configuration
├── index.js                 # Router (multi-language)
├── typescript/
│   └── analyzer.js          # TypeScript analyzer
├── dart/
│   └── analyzer.js          # JS wrapper (delegates to binary)
└── test-cases/              # TypeScript test cases
```

### Rule Implementation (Dart side)

```
dart_analyzer/
├── bin/
│   ├── sunlint_dart_analyzer.dart  # Entry point
│   └── sunlint-dart-macos          # Compiled binary
├── lib/
│   ├── analyzer_service.dart       # Main service
│   ├── json_rpc_server.dart        # JSON-RPC handler
│   ├── models/
│   │   ├── rule.dart
│   │   └── violation.dart
│   └── rules/
│       ├── base_analyzer.dart      # Base class
│       ├── C002_no_duplicate_code.dart
│       ├── C003_no_vague_abbreviations.dart
│       ├── S003_open_redirect_protection.dart
│       └── S004_sensitive_data_logging.dart
└── pubspec.yaml
```

### Test Fixtures

```
examples/rule-test-fixtures/dart-rules/
├── C002_no_duplicate_code/
│   ├── clean/                      # ✅ No violations expected
│   │   ├── data_validator.dart
│   │   └── payment_service.dart
│   └── violations/                 # ❌ Violations expected
│       ├── data_processor.dart
│       └── user_service.dart
├── C003_no_vague_abbreviations/
│   ├── clean/
│   └── violations/
├── S003_open_redirect_protection/
│   ├── clean/
│   └── violations/
└── S004_sensitive_data_logging/
    ├── clean/
    └── violations/
```

---

## 4. Commands

### Run Single Rule on Violations

```bash
# From sunlint directory
cd /path/to/sunlint

# Run C002 on violations folder
node cli.js --rule=C002 \
  --input="examples/rule-test-fixtures/dart-rules/C002_no_duplicate_code/violations" \
  --languages=dart \
  --include="**/*.dart"
```

### Run Single Rule on Clean Code

```bash
node cli.js --rule=C002 \
  --input="examples/rule-test-fixtures/dart-rules/C002_no_duplicate_code/clean" \
  --languages=dart \
  --include="**/*.dart"
```

### Run All Dart Rules

```bash
node cli.js --rule=C002,C003,S003,S004 \
  --input="examples/rule-test-fixtures/dart-rules" \
  --languages=dart \
  --include="**/*.dart"
```

### Verbose Mode

```bash
node cli.js --rule=C002 \
  --input="examples/rule-test-fixtures/dart-rules/C002_no_duplicate_code/violations" \
  --languages=dart \
  --include="**/*.dart" \
  --verbose
```

---

## 5. Adding a New Dart Rule

### Step 1: Create TypeScript Rule Structure

```bash
mkdir -p rules/common/CXXX_rule_name/{typescript,dart}
```

### Step 2: Create config.json

```json
{
  "id": "CXXX",
  "name": "Rule Name",
  "description": "Rule description",
  "category": "common",
  "severity": "warning",
  "languages": ["typescript", "javascript", "dart"]
}
```

### Step 3: Create index.js Router

```javascript
// rules/common/CXXX_rule_name/index.js
const path = require('path');

class CXXXRouter {
  getAnalyzer(language) {
    const normalizedLang = this.normalizeLanguage(language);
    const analyzerPath = path.join(__dirname, normalizedLang, 'analyzer.js');
    return require(analyzerPath);
  }
  // ...
}

module.exports = new CXXXRouter();
```

### Step 4: Create Dart Analyzer

```dart
// dart_analyzer/lib/rules/CXXX_rule_name.dart
class CXXXRuleAnalyzer extends BaseAnalyzer {
  @override
  String get ruleId => 'CXXX';

  @override
  List<Violation> analyze({
    required CompilationUnit unit,
    required String filePath,
    required Rule rule,
    required LineInfo lineInfo,
  }) {
    final violations = <Violation>[];
    // Implementation
    return violations;
  }
}
```

### Step 5: Register in AnalyzerService

```dart
// dart_analyzer/lib/analyzer_service.dart
void _registerAnalyzers() {
  // ...
  _analyzers['CXXX'] = CXXXRuleAnalyzer();
}
```

### Step 6: Create Test Fixtures

```
examples/rule-test-fixtures/dart-rules/CXXX_rule_name/
├── clean/
│   └── good_example.dart
└── violations/
    └── bad_example.dart
```

### Step 7: Rebuild Dart Binary

```bash
cd dart_analyzer
dart pub get
dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
```

---

## 6. Troubleshooting

### No files found

```bash
# Ensure --languages=dart and --include="**/*.dart" are set
node cli.js --rule=C002 --input=path --languages=dart --include="**/*.dart"
```

### DartAnalyzer not initialized

```bash
# Check if binary exists
ls -la dart_analyzer/bin/sunlint-dart-macos

# Rebuild if needed
cd dart_analyzer && dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
```

### Rule not supported

```bash
# Check if dart/ folder exists in rule directory
ls -la rules/common/C002_no_duplicate_code/dart/

# Check if rule is registered in analyzer_service.dart
grep "C002" dart_analyzer/lib/analyzer_service.dart
```

---

## 7. Summary Diagram

```
┌────────────────────────────────────────────────────────────────────────────┐
│                           C002 EXECUTION FLOW                              │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│  1. CLI Command                                                            │
│     └── node cli.js --rule=C002 --input=... --languages=dart              │
│                          │                                                 │
│  2. Heuristic Engine     ▼                                                 │
│     ├── detectDartSupport('C002') → true                                   │
│     ├── Get DartAnalyzer from SemanticEngineManager                        │
│     └── Call dartAnalyzer.analyzeFile(filePath, rules)                     │
│                          │                                                 │
│  3. DartAnalyzer (JS)    ▼                                                 │
│     ├── Connect to sunlint-dart-macos binary                               │
│     └── Send JSON-RPC: { method: 'analyze', params: {...} }                │
│                          │                                                 │
│  4. JSON-RPC Server      ▼  (Dart Binary)                                  │
│     ├── Receive request on stdin                                           │
│     └── Route to _handleAnalyze()                                          │
│                          │                                                 │
│  5. AnalyzerService      ▼                                                 │
│     ├── _analyzers['C002'] → C002NoDuplicateCodeAnalyzer                   │
│     ├── Get ResolvedUnitResult from Dart Analyzer package                  │
│     └── Call analyzer.analyze(unit, filePath, rule, lineInfo)              │
│                          │                                                 │
│  6. C002 Analyzer        ▼                                                 │
│     ├── Create _DuplicateCodeVisitor                                       │
│     ├── Traverse AST: unit.accept(visitor)                                 │
│     ├── Collect code blocks ≥ 10 lines                                     │
│     ├── Normalize & hash blocks                                            │
│     ├── Compare for duplicates (exact match or similarity)                 │
│     └── Return List<Violation>                                             │
│                          │                                                 │
│  7. Response             ▼                                                 │
│     └── Violations → JSON → stdout → JS client → CLI output               │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘
```
