feat: improve HTML reports and health score calculation

## HTML Report Quality Improvements
- Fix dead code table displaying actual data instead of empty rows
- Correct file paths showing real paths instead of "unknown"
- Fix clone type display showing "Type-1", "Type-2" etc. instead of blank cells
- Improve template loop logic for proper item limiting

## Report Format Standardization
- Create shared FormatUtils for consistent formatting across all analysis types
- Standardize header widths, label alignment, and section structures
- Unify color schemes and risk level representations
- Add comprehensive summary statistics and warning sections

## Health Score Algorithm Enhancement
- Add project size normalization using logarithmic scaling for large projects
- Implement penalty caps: max 25 points per category (Complexity, Dead Code, Clones, CBO)
- Set minimum score threshold of 10 points to avoid complete failure
- Adjust grade thresholds: A(85+), B(70+), C(55+), D(40+), F(<40)
- Fix scoring issue where large projects always got 0/100 (Grade: F)

## Test Coverage Expansion
- Add comprehensive dead code test cases in testdata/python/
- Create simple, edge cases, and complex dead code pattern examples
- Improve test coverage for various unreachable code scenarios

## Results
- Large projects: 0/100 (F) → 50/100 (D)
- Small projects: appropriate scores (60-70 range)
- HTML reports now show detailed, accurate information
- Consistent professional formatting across all analysis types

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DaisukeYoda
2025-09-10 20:10:37 +09:00
parent 8dd877bce1
commit b951910740
27 changed files with 41701 additions and 218 deletions

View File

@@ -0,0 +1,855 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 11:27:37</p>
<div class="score-badge grade-c">
Health Score: 75/100 (Grade: C)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">11</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">11</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1.91</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">1442</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">40.7%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">36</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.19</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">147</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">1.91</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">33</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>AbstractClass.abstract_method</td>
<td>testdata/python/simple/classes.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AbstractClass.concrete_method</td>
<td>testdata/python/simple/classes.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncClass.async_class_method</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncClass.async_method</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncClass.async_static_method</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncContextManager.__aenter__</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncContextManager.__aexit__</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncRange.__aiter__</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncRange.__anext__</td>
<td>testdata/python/complex/async_await.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>AsyncRange.__init__</td>
<td>testdata/python/complex/async_await.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Warnings</div>
</div>
</div>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1442</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">2</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.73</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">36</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.19</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">2</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
<tr>
<td>MultipleInheritance</td>
<td>testdata/python/simple/classes.py</td>
<td>2</td>
<td class="risk-low">low</td>
<td>SimpleClass, EmptyClass</td>
</tr>
<tr>
<td>InnerClass</td>
<td>testdata/python/edge_cases/nested_structures.py</td>
<td>1</td>
<td class="risk-low">low</td>
<td>MethodClass</td>
</tr>
<tr>
<td>ValidationError</td>
<td>testdata/python/complex/exceptions.py</td>
<td>1</td>
<td class="risk-low">low</td>
<td>CustomError</td>
</tr>
<tr>
<td>ClassWithMagicMethods</td>
<td>testdata/python/simple/classes.py</td>
<td>1</td>
<td class="risk-low">low</td>
<td>ClassWithMagicMethods</td>
</tr>
<tr>
<td>InheritedClass</td>
<td>testdata/python/simple/classes.py</td>
<td>1</td>
<td class="risk-low">low</td>
<td>SimpleClass</td>
</tr>
<tr>
<td>ComplexClass</td>
<td>testdata/python/edge_cases/nested_structures.py</td>
<td>1</td>
<td class="risk-low">low</td>
<td>MethodClass</td>
</tr>
<tr>
<td>AsyncRange</td>
<td>testdata/python/complex/async_await.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
<tr>
<td>SimpleClass</td>
<td>testdata/python/simple/classes.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
<tr>
<td>MethodDecorators</td>
<td>testdata/python/complex/decorators.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
<tr>
<td>PropertyExample</td>
<td>testdata/python/complex/decorators.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

View File

3906
analyze_20250910_123615.html Normal file

File diff suppressed because it is too large Load Diff

7952
analyze_20250910_124033.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,616 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 13:36:56</p>
<div class="score-badge grade-f">
Health Score: 54/100 (Grade: F)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">80.3%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">6</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>__main__</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>conditional_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>exception_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>loop_with_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>nested_return_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>simple_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Warnings</div>
</div>
</div>
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td>unknown</td>
<td>simple_dead_code</td>
<td>12-13</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>unknown</td>
<td>loop_with_dead_code</td>
<td>34-34</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_break</td>
</tr>
<tr>
<td>unknown</td>
<td>nested_return_dead_code</td>
<td>60-60</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
</tbody>
</table>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.88</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>40-49</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>40-49</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>16-25</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>40-49</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>52-66</td>
<td>0.920</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>52-66</td>
<td>0.918</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>30-35</td>
<td>56-63</td>
<td>0.860</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>28-37</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>40-49</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>28-37</td>
<td>0.843</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>30-35</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>56-63</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
</tbody>
</table>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,615 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 13:37:42</p>
<div class="score-badge grade-f">
Health Score: 54/100 (Grade: F)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">80.3%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">6</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>__main__</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>conditional_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>exception_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>loop_with_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>nested_return_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>simple_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Warnings</div>
</div>
</div>
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>nested_return_dead_code</td>
<td>60-60</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>simple_dead_code</td>
<td>12-13</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>loop_with_dead_code</td>
<td>34-34</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_break</td>
</tr>
</tbody>
</table>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.88</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>40-49</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>40-49</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>16-25</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>40-49</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>52-66</td>
<td>0.920</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>52-66</td>
<td>0.918</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>30-35</td>
<td>56-63</td>
<td>0.860</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>28-37</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>40-49</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>28-37</td>
<td>0.843</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>30-35</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>56-63</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
</tbody>
</table>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,791 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 13:57:46</p>
<div class="score-badge grade-f">
Health Score: 47/100 (Grade: F)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">2.46</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">4</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">58</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">82.2%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">2.46</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">4</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>DeadCodeClass.__init__</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>DeadCodeClass.method_with_dead_code</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>DeadCodeClass.setup</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>__main__</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>early_exit_pattern</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>infinite_loop_with_break</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>4</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>multiple_returns_with_dead_code</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>nested_dead_code</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>unreachable_after_break</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>unreachable_after_continue</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
<p style="color: #666; margin-top: 10px;">Showing top 10 of 13 functions</p>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">4</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">4</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Warnings</div>
</div>
</div>
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>nested_dead_code</td>
<td>101-101</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>__init__</td>
<td>117-118</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>unreachable_after_break</td>
<td>54-55</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_break</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>unreachable_after_continue</td>
<td>69-70</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_continue</td>
</tr>
</tbody>
</table>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">58</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.88</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>29-43</td>
<td>60-73</td>
<td>0.984</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>76-90</td>
<td>157-167</td>
<td>0.983</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>60-73</td>
<td>125-133</td>
<td>0.978</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>7-15</td>
<td>60-73</td>
<td>0.978</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>46-57</td>
<td>93-107</td>
<td>0.978</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>18-26</td>
<td>60-73</td>
<td>0.978</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>18-26</td>
<td>125-133</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>7-15</td>
<td>125-133</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>7-15</td>
<td>18-26</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>29-43</td>
<td>76-90</td>
<td>0.959</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>76-90</td>
<td>136-154</td>
<td>0.957</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>60-73</td>
<td>76-90</td>
<td>0.954</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>29-43</td>
<td>157-167</td>
<td>0.948</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>136-154</td>
<td>157-167</td>
<td>0.945</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>29-43</td>
<td>110-133</td>
<td>0.941</td>
<td>Type-2</td>
</tr>
</tbody>
</table>
<p style="color: #666; margin-top: 10px;">Showing top 15 of 58 clone pairs</p>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
<tr>
<td>DeadCodeClass</td>
<td>testdata/python/edge_cases/dead_code_examples.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,865 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 15:07:50</p>
<div class="score-badge grade-f">
Health Score: 24/100 (Grade: F)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">2.36</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">8</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">70</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">82.7%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">2</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">11</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">2.36</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">6</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>__main__</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>async_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>class_method_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>complex_control_flow</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>6</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>comprehension_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>context_manager_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>decorator_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>exception_hierarchy_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>5</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>finally_block_patterns</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>generator_with_dead_code</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
<p style="color: #666; margin-top: 10px;">Showing top 10 of 11 functions</p>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">8</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">7</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Warnings</div>
</div>
</div>
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>finally_block_patterns</td>
<td>168-168</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>finally_block_patterns</td>
<td>172-172</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>finally_block_patterns</td>
<td>179-179</td>
<td class="severity-warning">warning</td>
<td>unreachable_branch</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>generator_with_dead_code</td>
<td>45-46</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>exception_hierarchy_dead_code</td>
<td>160-160</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>context_manager_dead_code</td>
<td>74-74</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_raise</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>complex_control_flow</td>
<td>30-31</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>async_dead_code</td>
<td>57-58</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
</tbody>
</table>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">70</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.81</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>61-77</td>
<td>98-122</td>
<td>0.987</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>80-95</td>
<td>98-122</td>
<td>0.987</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>61-77</td>
<td>80-95</td>
<td>0.984</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>38-46</td>
<td>49-58</td>
<td>0.973</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>149-160</td>
<td>163-179</td>
<td>0.964</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>9-35</td>
<td>98-122</td>
<td>0.942</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>9-35</td>
<td>61-77</td>
<td>0.939</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>61-77</td>
<td>125-135</td>
<td>0.933</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>80-95</td>
<td>125-135</td>
<td>0.930</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>14-31</td>
<td>15-25</td>
<td>0.924</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>15-25</td>
<td>102-107</td>
<td>0.923</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>49-58</td>
<td>61-77</td>
<td>0.919</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>9-35</td>
<td>80-95</td>
<td>0.919</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>49-58</td>
<td>125-135</td>
<td>0.912</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>38-46</td>
<td>125-135</td>
<td>0.907</td>
<td>Type-2</td>
</tr>
</tbody>
</table>
<p style="color: #666; margin-top: 10px;">Showing top 15 of 70 clone pairs</p>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">2</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
<tr>
<td>CustomContext</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
<tr>
<td>TestClass</td>
<td>testdata/python/complex/dead_code_complex.py</td>
<td>0</td>
<td class="risk-low">low</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

7951
analyze_20250910_164535.html Normal file

File diff suppressed because it is too large Load Diff

7951
analyze_20250910_164727.html Normal file

File diff suppressed because it is too large Load Diff

7951
analyze_20250910_165913.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Analysis Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background: white;
border-radius: 10px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
margin-bottom: 10px;
}
.score-badge {
display: inline-block;
padding: 10px 20px;
border-radius: 50px;
font-size: 24px;
font-weight: bold;
margin: 10px 0;
}
.grade-a { background: #4caf50; color: white; }
.grade-b { background: #8bc34a; color: white; }
.grade-c { background: #ff9800; color: white; }
.grade-d { background: #ff5722; color: white; }
.grade-f { background: #f44336; color: white; }
.tabs {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
background: #f5f5f5;
}
.tab-button {
flex: 1;
padding: 15px;
border: none;
background: transparent;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
}
.tab-button.active {
background: white;
color: #667eea;
font-weight: bold;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 20px 0;
}
.metric-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.metric-value {
font-size: 32px;
font-weight: bold;
color: #667eea;
}
.metric-label {
color: #666;
margin-top: 5px;
}
.table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.table th, .table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background: #f8f9fa;
font-weight: 600;
}
.risk-low { color: #4caf50; }
.risk-medium { color: #ff9800; }
.risk-high { color: #f44336; }
.severity-critical { color: #f44336; }
.severity-warning { color: #ff9800; }
.severity-info { color: #2196f3; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>pyscn Analysis Report</h1>
<p>Generated: 2025-09-10 16:59:37</p>
<div class="score-badge grade-c">
Health Score: 63/100 (Grade: C)
</div>
</div>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="showTab('summary')">Summary</button>
<button class="tab-button" onclick="showTab('complexity')">Complexity</button>
<button class="tab-button" onclick="showTab('deadcode')">Dead Code</button>
<button class="tab-button" onclick="showTab('clone')">Clone Detection</button>
<button class="tab-button" onclick="showTab('cbo')">Dependency Analysis</button>
</div>
<div id="summary" class="tab-content active">
<h2>Analysis Summary</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Total Files</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Analyzed Files</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Avg Complexity</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Dead Code Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">80.3%</div>
<div class="metric-label">Code Duplication</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Avg Dependencies</div>
</div>
</div>
</div>
<div id="complexity" class="tab-content">
<h2>Complexity Analysis</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">6</div>
<div class="metric-label">Total Functions</div>
</div>
<div class="metric-card">
<div class="metric-value">2.17</div>
<div class="metric-label">Average</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Maximum</div>
</div>
</div>
<h3>Top Complex Functions</h3>
<table class="table">
<thead>
<tr>
<th>Function</th>
<th>File</th>
<th>Complexity</th>
<th>Risk</th>
</tr>
</thead>
<tbody>
<tr>
<td>__main__</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>conditional_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>2</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>exception_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>loop_with_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>nested_return_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>3</td>
<td class="risk-low">low</td>
</tr>
<tr>
<td>simple_dead_code</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>1</td>
<td class="risk-low">low</td>
</tr>
</tbody>
</table>
</div>
<div id="deadcode" class="tab-content">
<h2>Dead Code Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Total Issues</div>
</div>
<div class="metric-card">
<div class="metric-value">3</div>
<div class="metric-label">Critical</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Warnings</div>
</div>
</div>
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>simple_dead_code</td>
<td>12-13</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>loop_with_dead_code</td>
<td>34-34</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_break</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>nested_return_dead_code</td>
<td>60-60</td>
<td class="severity-critical">critical</td>
<td>unreachable_after_return</td>
</tr>
</tbody>
</table>
</div>
<div id="clone" class="tab-content">
<h2>Clone Detection</h2>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">13</div>
<div class="metric-label">Clone Pairs</div>
</div>
<div class="metric-card">
<div class="metric-value">1</div>
<div class="metric-label">Clone Groups</div>
</div>
<div class="metric-card">
<div class="metric-value">0.88</div>
<div class="metric-label">Avg Similarity</div>
</div>
</div>
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>40-49</td>
<td>0.971</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>40-49</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>16-25</td>
<td>0.969</td>
<td>Type-1</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>40-49</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>52-66</td>
<td>0.925</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>52-66</td>
<td>0.920</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>52-66</td>
<td>0.918</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>30-35</td>
<td>56-63</td>
<td>0.860</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>16-25</td>
<td>28-37</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>40-49</td>
<td>0.853</td>
<td>Type-2</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>6-13</td>
<td>28-37</td>
<td>0.843</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>30-35</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
<tr>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>testdata/python/simple/dead_code_simple.py</td>
<td>28-37</td>
<td>56-63</td>
<td>0.746</td>
<td>Type-3</td>
</tr>
</tbody>
</table>
</div>
<div id="cbo" class="tab-content">
<h2>Dependency Analysis</h2>
<p style="margin-bottom: 20px; color: #666;">Class coupling metrics (CBO - Coupling Between Objects)</p>
<div class="metric-grid">
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Total Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">High Risk Classes</div>
</div>
<div class="metric-card">
<div class="metric-value">0.00</div>
<div class="metric-label">Average Dependencies</div>
</div>
<div class="metric-card">
<div class="metric-value">0</div>
<div class="metric-label">Max Dependencies</div>
</div>
</div>
<h3>Most Dependent Classes</h3>
<table class="table">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Dependencies</th>
<th>Risk Level</th>
<th>Dependent Classes</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<script>
function showTab(tabName) {
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
const buttons = document.querySelectorAll('.tab-button');
buttons.forEach(btn => btn.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,134 @@
{
"files": [
{
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"functions": [
{
"name": "loop_with_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 34,
"end_line": 34,
"start_column": 0,
"end_column": 0
},
"function_name": "loop_with_dead_code",
"code": "Call",
"reason": "unreachable_after_break",
"severity": "critical",
"description": "Code appears after a break statement and will never be executed",
"block_id": "bb8"
}
],
"total_blocks": 10,
"dead_blocks": 1,
"reachable_ratio": 0.8,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "nested_return_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 60,
"end_line": 60,
"start_column": 0,
"end_column": 0
},
"function_name": "nested_return_dead_code",
"code": "Call",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb7"
}
],
"total_blocks": 9,
"dead_blocks": 1,
"reachable_ratio": 0.7777777777777778,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "simple_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 12,
"end_line": 13,
"start_column": 0,
"end_column": 0
},
"function_name": "simple_dead_code",
"code": "Call\nassignment",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb3"
}
],
"total_blocks": 4,
"dead_blocks": 1,
"reachable_ratio": 0.75,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
}
],
"total_findings": 3,
"total_functions": 5,
"affected_functions": 3,
"dead_code_ratio": 0.13043478260869565
}
],
"summary": {
"total_files": 1,
"total_functions": 5,
"total_findings": 3,
"files_with_dead_code": 1,
"functions_with_dead_code": 3,
"critical_findings": 3,
"warning_findings": 0,
"info_findings": 0,
"findings_by_reason": {
"unreachable_after_break": 1,
"unreachable_after_return": 2
},
"total_blocks": 23,
"dead_blocks": 3,
"overall_dead_ratio": 0.13043478260869565
},
"warnings": null,
"errors": null,
"generated_at": "2025-09-10T13:26:39+09:00",
"version": "dev",
"config": {
"context_lines": 3,
"detect_after_break": true,
"detect_after_continue": true,
"detect_after_raise": true,
"detect_after_return": true,
"detect_unreachable_branches": true,
"exclude_patterns": [
"test_*.py",
"*_test.py"
],
"ignore_patterns": [],
"include_patterns": [
"*.py"
],
"min_severity": "warning",
"show_context": false,
"sort_by": "severity"
}
}

View File

@@ -0,0 +1,220 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pyscn Code Quality Report - Python Project</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
background: white;
padding: 40px 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
color: #1a1a1a;
}
.header .timestamp {
color: #666;
font-size: 0.9em;
}
.score-section {
display: flex;
gap: 20px;
margin-bottom: 30px;
flex-wrap: wrap;
}
.score-card {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
flex: 1;
min-width: 250px;
text-align: center;
}
.score-gauge {
position: relative;
width: 120px;
height: 120px;
margin: 0 auto 20px;
}
.score-circle {
width: 120px;
height: 120px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2em;
font-weight: bold;
color: white;
position: relative;
}
.score-label {
font-size: 1.1em;
font-weight: 600;
margin-bottom: 10px;
}
.score-description {
color: #666;
font-size: 0.9em;
}
.details-section {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.details-section h2 {
margin-bottom: 20px;
color: #1a1a1a;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
}
.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.metric-item {
padding: 15px;
background: #f8f9fa;
border-radius: 4px;
}
.metric-item .value {
font-size: 1.5em;
font-weight: bold;
color: #1a1a1a;
}
.metric-item .label {
color: #666;
font-size: 0.9em;
}
.risk-bar {
height: 8px;
background: #eee;
border-radius: 4px;
overflow: hidden;
margin: 10px 0;
}
.risk-fill {
height: 100%;
transition: width 0.3s ease;
}
.risk-high { background-color: #FF5722; }
.risk-medium { background-color: #FFA500; }
.risk-low { background-color: #0CCE6B; }
@media (max-width: 768px) {
.score-section {
flex-direction: column;
}
.metric-grid {
grid-template-columns: 1fr;
}
}
.footer {
text-align: center;
padding: 20px;
color: #666;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Code Quality Report</h1>
<div class="project-name">Python Project</div>
<div class="timestamp">Generated on 2025-09-10T13:36:36&#43;09:00</div>
</div>
<div class="score-section">
<div class="score-card">
<div class="score-gauge">
<div class="score-circle" style="background-color: #FFA500;">
86
</div>
</div>
<div class="score-label">Overall Score</div>
<div class="score-description">Weighted average of all quality metrics</div>
</div>
<div class="score-card">
<div class="score-gauge">
<div class="score-circle" style="background-color: #FFA500;">
86
</div>
</div>
<div class="score-label">Dead_code Score</div>
<div class="score-description">87.0% Reachable</div>
</div>
</div>
<div class="details-section">
<h2>Analysis Details</h2>
<div class="metric-grid">
<div class="metric-item">
<div class="value">86</div>
<div class="label">Dead_code Score</div>
</div>
<div class="metric-item">
<div class="value">87.0% Reachable</div>
<div class="label">Details</div>
</div>
</div>
</div>
<div class="footer">
Generated by <strong>pyscn</strong> - Python Code Quality Analysis Tool
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,134 @@
{
"files": [
{
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"functions": [
{
"name": "simple_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 12,
"end_line": 13,
"start_column": 0,
"end_column": 0
},
"function_name": "simple_dead_code",
"code": "Call\nassignment",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb3"
}
],
"total_blocks": 4,
"dead_blocks": 1,
"reachable_ratio": 0.75,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "loop_with_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 34,
"end_line": 34,
"start_column": 0,
"end_column": 0
},
"function_name": "loop_with_dead_code",
"code": "Call",
"reason": "unreachable_after_break",
"severity": "critical",
"description": "Code appears after a break statement and will never be executed",
"block_id": "bb8"
}
],
"total_blocks": 10,
"dead_blocks": 1,
"reachable_ratio": 0.8,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "nested_return_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "unknown",
"start_line": 60,
"end_line": 60,
"start_column": 0,
"end_column": 0
},
"function_name": "nested_return_dead_code",
"code": "Call",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb7"
}
],
"total_blocks": 9,
"dead_blocks": 1,
"reachable_ratio": 0.7777777777777778,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
}
],
"total_findings": 3,
"total_functions": 5,
"affected_functions": 3,
"dead_code_ratio": 0.13043478260869565
}
],
"summary": {
"total_files": 1,
"total_functions": 5,
"total_findings": 3,
"files_with_dead_code": 1,
"functions_with_dead_code": 3,
"critical_findings": 3,
"warning_findings": 0,
"info_findings": 0,
"findings_by_reason": {
"unreachable_after_break": 1,
"unreachable_after_return": 2
},
"total_blocks": 23,
"dead_blocks": 3,
"overall_dead_ratio": 0.13043478260869565
},
"warnings": null,
"errors": null,
"generated_at": "2025-09-10T13:37:10+09:00",
"version": "dev",
"config": {
"context_lines": 3,
"detect_after_break": true,
"detect_after_continue": true,
"detect_after_raise": true,
"detect_after_return": true,
"detect_unreachable_branches": true,
"exclude_patterns": [
"test_*.py",
"*_test.py"
],
"ignore_patterns": [],
"include_patterns": [
"*.py"
],
"min_severity": "warning",
"show_context": false,
"sort_by": "severity"
}
}

View File

@@ -0,0 +1,134 @@
{
"files": [
{
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"functions": [
{
"name": "nested_return_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"start_line": 60,
"end_line": 60,
"start_column": 0,
"end_column": 0
},
"function_name": "nested_return_dead_code",
"code": "Call",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb7"
}
],
"total_blocks": 9,
"dead_blocks": 1,
"reachable_ratio": 0.7777777777777778,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "simple_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"start_line": 12,
"end_line": 13,
"start_column": 0,
"end_column": 0
},
"function_name": "simple_dead_code",
"code": "Call\nassignment",
"reason": "unreachable_after_return",
"severity": "critical",
"description": "Code appears after a return statement and will never be executed",
"block_id": "bb3"
}
],
"total_blocks": 4,
"dead_blocks": 1,
"reachable_ratio": 0.75,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
},
{
"name": "loop_with_dead_code",
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"findings": [
{
"location": {
"file_path": "/Users/daisukeyoda/projects/pyqol/testdata/python/simple/dead_code_simple.py",
"start_line": 34,
"end_line": 34,
"start_column": 0,
"end_column": 0
},
"function_name": "loop_with_dead_code",
"code": "Call",
"reason": "unreachable_after_break",
"severity": "critical",
"description": "Code appears after a break statement and will never be executed",
"block_id": "bb8"
}
],
"total_blocks": 10,
"dead_blocks": 1,
"reachable_ratio": 0.8,
"critical_count": 1,
"warning_count": 0,
"info_count": 0
}
],
"total_findings": 3,
"total_functions": 5,
"affected_functions": 3,
"dead_code_ratio": 0.13043478260869565
}
],
"summary": {
"total_files": 1,
"total_functions": 5,
"total_findings": 3,
"files_with_dead_code": 1,
"functions_with_dead_code": 3,
"critical_findings": 3,
"warning_findings": 0,
"info_findings": 0,
"findings_by_reason": {
"unreachable_after_break": 1,
"unreachable_after_return": 2
},
"total_blocks": 23,
"dead_blocks": 3,
"overall_dead_ratio": 0.13043478260869565
},
"warnings": null,
"errors": null,
"generated_at": "2025-09-10T13:37:32+09:00",
"version": "v0.1.0-beta.14-6-g8dd877b-dirty",
"config": {
"context_lines": 3,
"detect_after_break": true,
"detect_after_continue": true,
"detect_after_raise": true,
"detect_after_return": true,
"detect_unreachable_branches": true,
"exclude_patterns": [
"test_*.py",
"*_test.py"
],
"ignore_patterns": [],
"include_patterns": [
"*.py"
],
"min_severity": "warning",
"show_context": false,
"sort_by": "severity"
}
}

View File

@@ -1,6 +1,7 @@
package domain package domain
import ( import (
"math"
"time" "time"
) )
@@ -59,64 +60,85 @@ type AnalyzeSummary struct {
func (s *AnalyzeSummary) CalculateHealthScore() { func (s *AnalyzeSummary) CalculateHealthScore() {
score := 100 score := 100
// Deduct points for high complexity // Calculate normalization factor for large projects
normalizationFactor := 1.0
if s.TotalFiles > 10 {
// Use logarithmic scaling to reduce penalty impact for large projects
normalizationFactor = math.Log10(float64(s.TotalFiles)) / 2.0
}
// Complexity penalty (max 25 points)
complexityPenalty := 0
if s.AverageComplexity > 20 { if s.AverageComplexity > 20 {
score -= 30 complexityPenalty = 25
} else if s.AverageComplexity > 10 { } else if s.AverageComplexity > 10 {
score -= 20 complexityPenalty = 15
} else if s.AverageComplexity > 5 { } else if s.AverageComplexity > 5 {
score -= 10 complexityPenalty = 8
} }
score -= complexityPenalty
// Deduct points for dead code // Dead code penalty (max 25 points, normalized for project size)
deadCodePenalty := 0
if s.DeadCodeCount > 0 { if s.DeadCodeCount > 0 {
deduction := (s.DeadCodeCount * 2) rawPenalty := float64(s.DeadCodeCount) / normalizationFactor
if deduction > 20 { deadCodePenalty = int(math.Min(25, rawPenalty))
deduction = 20
}
score -= deduction
} }
// Additional penalty for critical dead code (max 15 points, normalized)
criticalPenalty := 0
if s.CriticalDeadCode > 0 {
rawCriticalPenalty := float64(s.CriticalDeadCode*3) / normalizationFactor
criticalPenalty = int(math.Min(15, rawCriticalPenalty))
}
totalDeadCodePenalty := deadCodePenalty + criticalPenalty
if totalDeadCodePenalty > 25 {
totalDeadCodePenalty = 25
}
score -= totalDeadCodePenalty
// Deduct points for critical dead code // Clone penalty (max 25 points, based on percentage)
score -= s.CriticalDeadCode * 5 clonePenalty := 0
if s.CodeDuplication > 40 {
// Deduct points for code duplication clonePenalty = 25
if s.CodeDuplication > 30 { } else if s.CodeDuplication > 25 {
score -= 25 clonePenalty = 15
} else if s.CodeDuplication > 20 {
score -= 15
} else if s.CodeDuplication > 10 { } else if s.CodeDuplication > 10 {
score -= 10 clonePenalty = 8
} }
score -= clonePenalty
// Deduct points for high coupling // CBO penalty (max 25 points)
cboPenalty := 0
if s.CBOClasses > 0 { if s.CBOClasses > 0 {
couplingRatio := float64(s.HighCouplingClasses) / float64(s.CBOClasses) couplingRatio := float64(s.HighCouplingClasses) / float64(s.CBOClasses)
if couplingRatio > 0.5 { if couplingRatio > 0.5 {
score -= 20 cboPenalty = 20
} else if couplingRatio > 0.3 { } else if couplingRatio > 0.3 {
score -= 15 cboPenalty = 12
} else if couplingRatio > 0.1 { } else if couplingRatio > 0.1 {
score -= 10 cboPenalty = 6
} }
} }
score -= cboPenalty
// Ensure score is within bounds // Set minimum score to 10 (never completely fail)
if score < 0 { if score < 10 {
score = 0 score = 10
} }
s.HealthScore = score s.HealthScore = score
// Assign grade based on score // Assign grade based on score (adjusted thresholds)
switch { switch {
case score >= 90: case score >= 85:
s.Grade = "A" s.Grade = "A"
case score >= 80:
s.Grade = "B"
case score >= 70: case score >= 70:
s.Grade = "B"
case score >= 55:
s.Grade = "C" s.Grade = "C"
case score >= 60: case score >= 40:
s.Grade = "D" s.Grade = "D"
default: default:
s.Grade = "F" s.Grade = "F"

View File

@@ -46,58 +46,84 @@ func (f *AnalyzeFormatter) Write(response *domain.AnalyzeResponse, format domain
// writeText formats the response as plain text // writeText formats the response as plain text
func (f *AnalyzeFormatter) writeText(response *domain.AnalyzeResponse, writer io.Writer) error { func (f *AnalyzeFormatter) writeText(response *domain.AnalyzeResponse, writer io.Writer) error {
fmt.Fprintf(writer, "pyscn Comprehensive Analysis Report\n") utils := NewFormatUtils()
fmt.Fprintf(writer, "====================================\n\n")
fmt.Fprintf(writer, "Generated: %s\n\n", response.GeneratedAt.Format(time.RFC3339))
// Summary section // Header
fmt.Fprintf(writer, "Overall Health Score: %d/100 (Grade: %s)\n", fmt.Fprint(writer, utils.FormatMainHeader("Comprehensive Analysis Report"))
response.Summary.HealthScore, response.Summary.Grade)
fmt.Fprintf(writer, "Analysis Duration: %.2fs\n\n", float64(response.Duration)/1000.0) // Overall health and duration
healthStats := map[string]interface{}{
"Health Score": fmt.Sprintf("%d/100 (%s)", response.Summary.HealthScore, response.Summary.Grade),
"Analysis Duration": fmt.Sprintf("%.2fs", float64(response.Duration)/1000.0),
"Generated": response.GeneratedAt.Format(time.RFC3339),
}
fmt.Fprint(writer, utils.FormatSummaryStats(healthStats))
// File statistics // File statistics
fmt.Fprintf(writer, "File Statistics:\n") fmt.Fprint(writer, utils.FormatFileStats(
fmt.Fprintf(writer, " Total Files: %d\n", response.Summary.TotalFiles) response.Summary.AnalyzedFiles,
fmt.Fprintf(writer, " Analyzed: %d\n", response.Summary.AnalyzedFiles) response.Summary.TotalFiles,
fmt.Fprintf(writer, " Skipped: %d\n\n", response.Summary.SkippedFiles) response.Summary.TotalFiles-response.Summary.AnalyzedFiles))
// Complexity analysis results // Analysis modules results
if response.Complexity != nil && response.Summary.ComplexityEnabled { if response.Summary.ComplexityEnabled {
fmt.Fprintf(writer, "Complexity Analysis:\n") fmt.Fprint(writer, utils.FormatSectionHeader("COMPLEXITY ANALYSIS"))
fmt.Fprintf(writer, "--------------------\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Total Functions", response.Summary.TotalFunctions))
fmt.Fprintf(writer, " Total Functions: %d\n", response.Summary.TotalFunctions) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Average Complexity", fmt.Sprintf("%.1f", response.Summary.AverageComplexity)))
fmt.Fprintf(writer, " Average Complexity: %.2f\n", response.Summary.AverageComplexity) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "High Complexity Count", response.Summary.HighComplexityCount))
fmt.Fprintf(writer, " High Complexity Count: %d\n\n", response.Summary.HighComplexityCount) fmt.Fprint(writer, utils.FormatSectionSeparator())
} }
// Dead code analysis results if response.Summary.DeadCodeEnabled {
if response.DeadCode != nil && response.Summary.DeadCodeEnabled { fmt.Fprint(writer, utils.FormatSectionHeader("DEAD CODE DETECTION"))
fmt.Fprintf(writer, "Dead Code Detection:\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Total Issues", response.Summary.DeadCodeCount))
fmt.Fprintf(writer, "-------------------\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Critical Issues", response.Summary.CriticalDeadCode))
fmt.Fprintf(writer, " Total Issues: %d\n", response.Summary.DeadCodeCount) fmt.Fprint(writer, utils.FormatSectionSeparator())
fmt.Fprintf(writer, " Critical Issues: %d\n\n", response.Summary.CriticalDeadCode)
} }
// Clone detection results if response.Summary.CloneEnabled {
if response.Clone != nil && response.Summary.CloneEnabled { fmt.Fprint(writer, utils.FormatSectionHeader("CLONE DETECTION"))
fmt.Fprintf(writer, "Clone Detection:\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Clone Pairs", response.Summary.ClonePairs))
fmt.Fprintf(writer, "---------------\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Clone Groups", response.Summary.CloneGroups))
fmt.Fprintf(writer, " Clone Pairs: %d\n", response.Summary.ClonePairs) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Code Duplication", utils.FormatPercentage(response.Summary.CodeDuplication)))
fmt.Fprintf(writer, " Clone Groups: %d\n", response.Summary.CloneGroups) fmt.Fprint(writer, utils.FormatSectionSeparator())
fmt.Fprintf(writer, " Code Duplication: %.2f%%\n\n", response.Summary.CodeDuplication) }
if response.Summary.CBOEnabled {
fmt.Fprint(writer, utils.FormatSectionHeader("DEPENDENCY ANALYSIS"))
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Classes Analyzed", response.Summary.CBOClasses))
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "High Coupling Classes", response.Summary.HighCouplingClasses))
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Average Coupling", fmt.Sprintf("%.1f", response.Summary.AverageCoupling)))
fmt.Fprint(writer, utils.FormatSectionSeparator())
} }
// Recommendations // Recommendations
fmt.Fprintf(writer, "Recommendations:\n") fmt.Fprint(writer, utils.FormatSectionHeader("RECOMMENDATIONS"))
fmt.Fprintf(writer, "---------------\n") recommendationCount := 0
if response.Summary.HighComplexityCount > 0 { if response.Summary.HighComplexityCount > 0 {
fmt.Fprintf(writer, " • Refactor %d high-complexity functions\n", response.Summary.HighComplexityCount) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "•",
fmt.Sprintf("Refactor %d high-complexity functions", response.Summary.HighComplexityCount)))
recommendationCount++
} }
if response.Summary.DeadCodeCount > 0 { if response.Summary.DeadCodeCount > 0 {
fmt.Fprintf(writer, " • Remove %d dead code segments\n", response.Summary.DeadCodeCount) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "•",
fmt.Sprintf("Remove %d dead code segments", response.Summary.DeadCodeCount)))
recommendationCount++
} }
if response.Summary.CodeDuplication > 10 { if response.Summary.CodeDuplication > 10 {
fmt.Fprintf(writer, " • Reduce code duplication (currently %.1f%%)\n", response.Summary.CodeDuplication) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "•",
fmt.Sprintf("Reduce code duplication (currently %.1f%%)", response.Summary.CodeDuplication)))
recommendationCount++
}
if response.Summary.HighCouplingClasses > 0 {
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "•",
fmt.Sprintf("Reduce coupling in %d high-dependency classes", response.Summary.HighCouplingClasses)))
recommendationCount++
}
if recommendationCount == 0 {
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Status", "No major issues detected"))
} }
return nil return nil
@@ -134,6 +160,9 @@ func (f *AnalyzeFormatter) writeHTML(response *domain.AnalyzeResponse, writer io
"join": func(elems []string, sep string) string { "join": func(elems []string, sep string) string {
return strings.Join(elems, sep) return strings.Join(elems, sep)
}, },
"add": func(a, b int) int {
return a + b
},
} }
tmpl := template.Must(template.New("analyze").Funcs(funcMap).Parse(analyzeHTMLTemplate)) tmpl := template.Must(template.New("analyze").Funcs(funcMap).Parse(analyzeHTMLTemplate))
return tmpl.Execute(writer, response) return tmpl.Execute(writer, response)
@@ -376,6 +405,9 @@ const analyzeHTMLTemplate = `<!DOCTYPE html>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
{{if gt (len .Complexity.Functions) 10}}
<p style="color: #666; margin-top: 10px;">Showing top 10 of {{len .Complexity.Functions}} functions</p>
{{end}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
@@ -398,6 +430,43 @@ const analyzeHTMLTemplate = `<!DOCTYPE html>
<div class="metric-label">Warnings</div> <div class="metric-label">Warnings</div>
</div> </div>
</div> </div>
{{if gt .DeadCode.Summary.TotalFindings 0}}
<h3>Top Dead Code Issues</h3>
<table class="table">
<thead>
<tr>
<th>File</th>
<th>Function</th>
<th>Lines</th>
<th>Severity</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
{{range $file := .DeadCode.Files}}
{{range $func := $file.Functions}}
{{range $i, $finding := $func.Findings}}
{{if lt $i 10}}
<tr>
<td>{{$finding.Location.FilePath}}</td>
<td>{{$finding.FunctionName}}</td>
<td>{{$finding.Location.StartLine}}-{{$finding.Location.EndLine}}</td>
<td class="severity-{{$finding.Severity}}">{{$finding.Severity}}</td>
<td>{{$finding.Reason}}</td>
</tr>
{{end}}
{{end}}
{{end}}
{{end}}
</tbody>
</table>
{{if gt .DeadCode.Summary.TotalFindings 10}}
<p style="color: #666; margin-top: 10px;">Showing top 10 of {{.DeadCode.Summary.TotalFindings}} dead code issues</p>
{{end}}
{{else}}
<p style="color: #4caf50; font-weight: bold; margin-top: 20px;">✓ No dead code detected</p>
{{end}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
@@ -420,6 +489,41 @@ const analyzeHTMLTemplate = `<!DOCTYPE html>
<div class="metric-label">Avg Similarity</div> <div class="metric-label">Avg Similarity</div>
</div> </div>
</div> </div>
{{if gt .Clone.Statistics.TotalClonePairs 0}}
<h3>Major Clone Pairs</h3>
<table class="table">
<thead>
<tr>
<th>File 1</th>
<th>File 2</th>
<th>Lines 1</th>
<th>Lines 2</th>
<th>Similarity</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{{range $i, $pair := .Clone.ClonePairs}}
{{if lt $i 15}}
<tr>
<td>{{$pair.Clone1.Location.FilePath}}</td>
<td>{{$pair.Clone2.Location.FilePath}}</td>
<td>{{$pair.Clone1.Location.StartLine}}-{{$pair.Clone1.Location.EndLine}}</td>
<td>{{$pair.Clone2.Location.StartLine}}-{{$pair.Clone2.Location.EndLine}}</td>
<td>{{printf "%.3f" $pair.Similarity}}</td>
<td>{{$pair.Type}}</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
{{if gt .Clone.Statistics.TotalClonePairs 15}}
<p style="color: #666; margin-top: 10px;">Showing top 15 of {{.Clone.Statistics.TotalClonePairs}} clone pairs</p>
{{end}}
{{else}}
<p style="color: #4caf50; font-weight: bold; margin-top: 20px;">✓ No clones detected</p>
{{end}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
@@ -473,6 +577,9 @@ const analyzeHTMLTemplate = `<!DOCTYPE html>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
{{if gt (len .CBO.Classes) 10}}
<p style="color: #666; margin-top: 10px;">Showing top 10 of {{len .CBO.Classes}} classes</p>
{{end}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}

View File

@@ -53,29 +53,30 @@ func (f *CBOFormatterImpl) Write(response *domain.CBOResponse, format domain.Out
// formatText formats the response as human-readable text // formatText formats the response as human-readable text
func (f *CBOFormatterImpl) formatText(response *domain.CBOResponse) (string, error) { func (f *CBOFormatterImpl) formatText(response *domain.CBOResponse) (string, error) {
var builder strings.Builder var builder strings.Builder
utils := NewFormatUtils()
// Header // Header
builder.WriteString("=== CBO (Coupling Between Objects) Analysis Results ===\n\n") builder.WriteString(utils.FormatMainHeader("CBO (Coupling Between Objects) Analysis Report"))
// Summary // Summary
builder.WriteString("📊 SUMMARY\n") stats := map[string]interface{}{
builder.WriteString(fmt.Sprintf("Total Classes: %d\n", response.Summary.TotalClasses)) "Total Classes": response.Summary.TotalClasses,
builder.WriteString(fmt.Sprintf("Files Analyzed: %d\n", response.Summary.FilesAnalyzed)) "Files Analyzed": response.Summary.FilesAnalyzed,
builder.WriteString(fmt.Sprintf("Average CBO: %.2f\n", response.Summary.AverageCBO)) "Average CBO": fmt.Sprintf("%.1f", response.Summary.AverageCBO),
builder.WriteString(fmt.Sprintf("Max CBO: %d\n", response.Summary.MaxCBO)) "Max CBO": response.Summary.MaxCBO,
builder.WriteString(fmt.Sprintf("Min CBO: %d\n", response.Summary.MinCBO)) "Min CBO": response.Summary.MinCBO,
builder.WriteString("\n") }
builder.WriteString(utils.FormatSummaryStats(stats))
// Risk distribution // Risk distribution
builder.WriteString("🚦 RISK DISTRIBUTION\n") builder.WriteString(utils.FormatRiskDistribution(
builder.WriteString(fmt.Sprintf("Low Risk: %d classes\n", response.Summary.LowRiskClasses)) response.Summary.HighRiskClasses,
builder.WriteString(fmt.Sprintf("Medium Risk: %d classes\n", response.Summary.MediumRiskClasses)) response.Summary.MediumRiskClasses,
builder.WriteString(fmt.Sprintf("High Risk: %d classes\n", response.Summary.HighRiskClasses)) response.Summary.LowRiskClasses))
builder.WriteString("\n")
// CBO distribution // CBO distribution
if len(response.Summary.CBODistribution) > 0 { if len(response.Summary.CBODistribution) > 0 {
builder.WriteString("📈 CBO DISTRIBUTION\n") builder.WriteString(utils.FormatSectionHeader("CBO DISTRIBUTION"))
// Sort ranges for consistent output // Sort ranges for consistent output
ranges := make([]string, 0, len(response.Summary.CBODistribution)) ranges := make([]string, 0, len(response.Summary.CBODistribution))
@@ -86,98 +87,121 @@ func (f *CBOFormatterImpl) formatText(response *domain.CBOResponse) (string, err
for _, rang := range ranges { for _, rang := range ranges {
count := response.Summary.CBODistribution[rang] count := response.Summary.CBODistribution[rang]
builder.WriteString(fmt.Sprintf("CBO %s: %d classes\n", rang, count)) builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, fmt.Sprintf("CBO %s", rang), fmt.Sprintf("%d classes", count)))
} }
builder.WriteString("\n") builder.WriteString(utils.FormatSectionSeparator())
} }
// Most coupled classes // Most coupled classes
if len(response.Summary.MostCoupledClasses) > 0 { if len(response.Summary.MostCoupledClasses) > 0 {
builder.WriteString("🔗 MOST COUPLED CLASSES\n") builder.WriteString(utils.FormatSectionHeader("MOST COUPLED CLASSES"))
for i, class := range response.Summary.MostCoupledClasses { for i, class := range response.Summary.MostCoupledClasses {
if i >= 10 { // Limit to top 10 if i >= 10 { // Limit to top 10
break break
} }
riskIcon := f.getRiskIcon(class.RiskLevel) // Convert domain risk level to standard risk level
builder.WriteString(fmt.Sprintf("%d. %s %s (CBO: %d) - %s:%d\n", var standardRisk RiskLevel
i+1, riskIcon, class.Name, class.Metrics.CouplingCount, class.FilePath, class.StartLine)) switch class.RiskLevel {
case "High":
standardRisk = RiskHigh
case "Medium":
standardRisk = RiskMedium
case "Low":
standardRisk = RiskLow
default:
standardRisk = RiskLow
}
coloredRisk := utils.FormatRiskWithColor(standardRisk)
builder.WriteString(fmt.Sprintf("%s%d. %s %s (CBO: %d) - %s:%d\n",
strings.Repeat(" ", SectionPadding), i+1, coloredRisk, class.Name, class.Metrics.CouplingCount, class.FilePath, class.StartLine))
} }
builder.WriteString("\n") builder.WriteString(utils.FormatSectionSeparator())
} }
// Detailed class information // Detailed class information
if len(response.Classes) > 0 { if len(response.Classes) > 0 {
builder.WriteString("📋 CLASS DETAILS\n") builder.WriteString(utils.FormatSectionHeader("CLASS DETAILS"))
for _, class := range response.Classes { for _, class := range response.Classes {
f.writeClassDetails(&builder, class) f.writeClassDetails(&builder, class, utils)
builder.WriteString("\n") builder.WriteString("\n")
} }
builder.WriteString(utils.FormatSectionSeparator())
} }
// Warnings // Warnings
if len(response.Warnings) > 0 { if len(response.Warnings) > 0 {
builder.WriteString("⚠️ WARNINGS\n") builder.WriteString(utils.FormatWarningsSection(response.Warnings))
for _, warning := range response.Warnings {
builder.WriteString(fmt.Sprintf("• %s\n", warning))
}
builder.WriteString("\n")
} }
// Errors // Errors
if len(response.Errors) > 0 { if len(response.Errors) > 0 {
builder.WriteString("ERRORS\n") builder.WriteString(utils.FormatSectionHeader("ERRORS"))
for _, err := range response.Errors { for _, err := range response.Errors {
builder.WriteString(fmt.Sprintf("• %s\n", err)) builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "❌", err))
} }
builder.WriteString("\n") builder.WriteString(utils.FormatSectionSeparator())
} }
// Footer // Footer
builder.WriteString(fmt.Sprintf("Generated at: %s\n", response.GeneratedAt)) builder.WriteString(utils.FormatSectionHeader("METADATA"))
builder.WriteString(fmt.Sprintf("Version: %s\n", response.Version)) builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "Generated at", response.GeneratedAt))
builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "Version", response.Version))
return builder.String(), nil return builder.String(), nil
} }
// writeClassDetails writes detailed information about a class // writeClassDetails writes detailed information about a class
func (f *CBOFormatterImpl) writeClassDetails(builder *strings.Builder, class domain.ClassCoupling) { func (f *CBOFormatterImpl) writeClassDetails(builder *strings.Builder, class domain.ClassCoupling, utils *FormatUtils) {
riskIcon := f.getRiskIcon(class.RiskLevel) // Convert domain risk level to standard risk level
var standardRisk RiskLevel
switch class.RiskLevel {
case "High":
standardRisk = RiskHigh
case "Medium":
standardRisk = RiskMedium
case "Low":
standardRisk = RiskLow
default:
standardRisk = RiskLow
}
coloredRisk := utils.FormatRiskWithColor(standardRisk)
builder.WriteString(fmt.Sprintf("%s %s (CBO: %d, Risk: %s)\n", builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "Class", fmt.Sprintf("%s %s (CBO: %d)",
riskIcon, class.Name, class.Metrics.CouplingCount, class.RiskLevel)) coloredRisk, class.Name, class.Metrics.CouplingCount)))
builder.WriteString(fmt.Sprintf(" Location: %s:%d-%d\n", class.FilePath, class.StartLine, class.EndLine)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding, "Location", fmt.Sprintf("%s:%d-%d", class.FilePath, class.StartLine, class.EndLine)))
if class.IsAbstract { if class.IsAbstract {
builder.WriteString(" Type: Abstract Class\n") builder.WriteString(utils.FormatLabelWithIndent(ItemPadding, "Type", "Abstract Class"))
} }
// Base classes // Base classes
if len(class.BaseClasses) > 0 { if len(class.BaseClasses) > 0 {
builder.WriteString(fmt.Sprintf(" Inherits from: %s\n", strings.Join(class.BaseClasses, ", "))) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding, "Inherits from", strings.Join(class.BaseClasses, ", ")))
} }
// Dependency breakdown // Dependency breakdown
if class.Metrics.CouplingCount > 0 { if class.Metrics.CouplingCount > 0 {
builder.WriteString(" Dependencies:\n") builder.WriteString(utils.FormatLabelWithIndent(ItemPadding, "Dependencies", ""))
if class.Metrics.InheritanceDependencies > 0 { if class.Metrics.InheritanceDependencies > 0 {
builder.WriteString(fmt.Sprintf(" Inheritance: %d\n", class.Metrics.InheritanceDependencies)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Inheritance", class.Metrics.InheritanceDependencies))
} }
if class.Metrics.TypeHintDependencies > 0 { if class.Metrics.TypeHintDependencies > 0 {
builder.WriteString(fmt.Sprintf(" Type Hints: %d\n", class.Metrics.TypeHintDependencies)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Type Hints", class.Metrics.TypeHintDependencies))
} }
if class.Metrics.InstantiationDependencies > 0 { if class.Metrics.InstantiationDependencies > 0 {
builder.WriteString(fmt.Sprintf(" Instantiation: %d\n", class.Metrics.InstantiationDependencies)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Instantiation", class.Metrics.InstantiationDependencies))
} }
if class.Metrics.AttributeAccessDependencies > 0 { if class.Metrics.AttributeAccessDependencies > 0 {
builder.WriteString(fmt.Sprintf(" Attribute Access: %d\n", class.Metrics.AttributeAccessDependencies)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Attribute Access", class.Metrics.AttributeAccessDependencies))
} }
if class.Metrics.ImportDependencies > 0 { if class.Metrics.ImportDependencies > 0 {
builder.WriteString(fmt.Sprintf(" Imports: %d\n", class.Metrics.ImportDependencies)) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Imports", class.Metrics.ImportDependencies))
} }
// List dependent classes // List dependent classes
if len(class.Metrics.DependentClasses) > 0 { if len(class.Metrics.DependentClasses) > 0 {
builder.WriteString(fmt.Sprintf(" Coupled to: %s\n", strings.Join(class.Metrics.DependentClasses, ", "))) builder.WriteString(utils.FormatLabelWithIndent(ItemPadding+2, "Coupled to", strings.Join(class.Metrics.DependentClasses, ", ")))
} }
} }
} }

View File

@@ -60,94 +60,92 @@ func (f *CloneOutputFormatter) formatAsText(response *domain.CloneResponse, writ
return nil return nil
} }
// Print header utils := NewFormatUtils()
fmt.Fprintf(writer, "Clone Detection Results\n")
fmt.Fprintf(writer, "======================\n\n")
// Print statistics // Header
fmt.Fprint(writer, utils.FormatMainHeader("Clone Detection Analysis Report"))
// Summary
if response.Statistics != nil { if response.Statistics != nil {
fmt.Fprintf(writer, "Summary:\n") stats := map[string]interface{}{
fmt.Fprintf(writer, " Files analyzed: %d\n", response.Statistics.FilesAnalyzed) "Files Analyzed": response.Statistics.FilesAnalyzed,
fmt.Fprintf(writer, " Lines analyzed: %d\n", response.Statistics.LinesAnalyzed) "Lines Analyzed": response.Statistics.LinesAnalyzed,
fmt.Fprintf(writer, " Clone pairs found: %d\n", response.Statistics.TotalClonePairs) "Clone Pairs": response.Statistics.TotalClonePairs,
fmt.Fprintf(writer, " Clone groups found: %d\n", response.Statistics.TotalCloneGroups) "Clone Groups": response.Statistics.TotalCloneGroups,
"Average Similarity": fmt.Sprintf("%.3f", response.Statistics.AverageSimilarity),
if response.Statistics.AverageSimilarity > 0 { "Analysis Duration": utils.FormatDuration(response.Duration),
fmt.Fprintf(writer, " Average similarity: %.3f\n", response.Statistics.AverageSimilarity)
} }
fmt.Fprint(writer, utils.FormatSummaryStats(stats))
fmt.Fprintf(writer, " Analysis duration: %dms\n\n", response.Duration)
} }
// Print clone types breakdown // Clone Types breakdown
if response.Statistics != nil && len(response.Statistics.ClonesByType) > 0 { if response.Statistics != nil && len(response.Statistics.ClonesByType) > 0 {
fmt.Fprintf(writer, "Clone Types:\n") fmt.Fprint(writer, utils.FormatSectionHeader("CLONE TYPES"))
for cloneType, count := range response.Statistics.ClonesByType { for cloneType, count := range response.Statistics.ClonesByType {
fmt.Fprintf(writer, " %s: %d pairs\n", cloneType, count) fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, cloneType, fmt.Sprintf("%d pairs", count)))
} }
fmt.Fprintf(writer, "\n") fmt.Fprint(writer, utils.FormatSectionSeparator())
} }
if len(response.ClonePairs) == 0 { if len(response.ClonePairs) == 0 {
fmt.Fprintf(writer, "No clones detected.\n") fmt.Fprint(writer, utils.FormatSectionHeader("RESULTS"))
fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Status", "No clones detected"))
return nil return nil
} }
// Print detailed clone pairs // Detailed clone information
if response.Request != nil && response.Request.GroupClones && len(response.CloneGroups) > 0 { if response.Request != nil && response.Request.GroupClones && len(response.CloneGroups) > 0 {
fmt.Fprintf(writer, "Clone Groups:\n") fmt.Fprint(writer, utils.FormatSectionHeader("CLONE GROUPS"))
fmt.Fprintf(writer, "=============\n\n")
for _, group := range response.CloneGroups { for _, group := range response.CloneGroups {
if group == nil { if group == nil {
continue continue
} }
fmt.Fprintf(writer, "Group %d (%s, %d clones, similarity: %.3f):\n", fmt.Fprint(writer, utils.FormatLabelWithIndent(0, "Group", fmt.Sprintf("%d (%s, %d clones, similarity: %.3f)",
group.ID, group.Type.String(), group.Size, group.Similarity) group.ID, group.Type.String(), group.Size, group.Similarity)))
for i, clone := range group.Clones { for i, clone := range group.Clones {
if clone == nil || clone.Location == nil { if clone == nil || clone.Location == nil {
continue continue
} }
fmt.Fprintf(writer, " %d. %s (%d lines, %d nodes)\n", fmt.Fprint(writer, utils.FormatLabelWithIndent(ItemPadding, fmt.Sprintf("Clone %d", i+1),
i+1, clone.Location.String(), clone.LineCount, clone.Size) fmt.Sprintf("%s (%d lines, %d nodes)", clone.Location.String(), clone.LineCount, clone.Size)))
} }
fmt.Fprintf(writer, "\n") fmt.Fprint(writer, "\n")
} }
} else { } else {
fmt.Fprintf(writer, "Clone Pairs:\n") fmt.Fprint(writer, utils.FormatSectionHeader("CLONE PAIRS"))
fmt.Fprintf(writer, "============\n\n")
for i, pair := range response.ClonePairs { for i, pair := range response.ClonePairs {
if pair == nil { if pair == nil {
continue continue
} }
fmt.Fprintf(writer, "%d. %s (similarity: %.3f, confidence: %.3f)\n", fmt.Fprint(writer, utils.FormatLabelWithIndent(0, fmt.Sprintf("Pair %d", i+1),
i+1, pair.Type.String(), pair.Similarity, pair.Confidence) fmt.Sprintf("%s (similarity: %.3f, confidence: %.3f)", pair.Type.String(), pair.Similarity, pair.Confidence)))
if pair.Clone1 != nil && pair.Clone1.Location != nil { if pair.Clone1 != nil && pair.Clone1.Location != nil {
fmt.Fprintf(writer, " Clone 1: %s (%d lines, %d nodes)\n", fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Clone 1",
pair.Clone1.Location.String(), pair.Clone1.LineCount, pair.Clone1.Size) fmt.Sprintf("%s (%d lines, %d nodes)", pair.Clone1.Location.String(), pair.Clone1.LineCount, pair.Clone1.Size)))
} }
if pair.Clone2 != nil && pair.Clone2.Location != nil { if pair.Clone2 != nil && pair.Clone2.Location != nil {
fmt.Fprintf(writer, " Clone 2: %s (%d lines, %d nodes)\n", fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Clone 2",
pair.Clone2.Location.String(), pair.Clone2.LineCount, pair.Clone2.Size) fmt.Sprintf("%s (%d lines, %d nodes)", pair.Clone2.Location.String(), pair.Clone2.LineCount, pair.Clone2.Size)))
} }
if response.Request != nil && response.Request.ShowContent && pair.Clone1 != nil && pair.Clone1.Content != "" { if response.Request != nil && response.Request.ShowContent && pair.Clone1 != nil && pair.Clone1.Content != "" {
fmt.Fprintf(writer, " Content preview:\n") fmt.Fprint(writer, utils.FormatLabelWithIndent(SectionPadding, "Preview", ""))
lines := strings.Split(pair.Clone1.Content, "\n") lines := strings.Split(pair.Clone1.Content, "\n")
for j, line := range lines { for j, line := range lines {
if j >= 5 { // Limit preview to 5 lines if j >= 5 { // Limit preview to 5 lines
fmt.Fprintf(writer, " ...\n") fmt.Fprintf(writer, "%s...\n", strings.Repeat(" ", ItemPadding+2))
break break
} }
fmt.Fprintf(writer, " %s\n", line) fmt.Fprintf(writer, "%s%s\n", strings.Repeat(" ", ItemPadding+2), line)
} }
} }
fmt.Fprintf(writer, "\n") fmt.Fprint(writer, "\n")
} }
} }

View File

@@ -54,7 +54,7 @@ func (f *DeadCodeFormatterImpl) Write(response *domain.DeadCodeResponse, format
func (f *DeadCodeFormatterImpl) FormatFinding(finding domain.DeadCodeFinding, format domain.OutputFormat) (string, error) { func (f *DeadCodeFormatterImpl) FormatFinding(finding domain.DeadCodeFinding, format domain.OutputFormat) (string, error) {
switch format { switch format {
case domain.OutputFormatText: case domain.OutputFormatText:
return f.formatFindingText(finding), nil return f.formatFindingTextLegacy(finding), nil
case domain.OutputFormatJSON: case domain.OutputFormatJSON:
return EncodeJSON(finding) return EncodeJSON(finding)
case domain.OutputFormatYAML: case domain.OutputFormatYAML:
@@ -62,7 +62,7 @@ func (f *DeadCodeFormatterImpl) FormatFinding(finding domain.DeadCodeFinding, fo
case domain.OutputFormatHTML: case domain.OutputFormatHTML:
// HTML formatting for individual findings is not typically needed // HTML formatting for individual findings is not typically needed
// Fall back to text format for individual findings // Fall back to text format for individual findings
return f.formatFindingText(finding), nil return f.formatFindingTextLegacy(finding), nil
default: default:
return "", domain.NewUnsupportedFormatError(string(format)) return "", domain.NewUnsupportedFormatError(string(format))
} }
@@ -71,60 +71,93 @@ func (f *DeadCodeFormatterImpl) FormatFinding(finding domain.DeadCodeFinding, fo
// formatText formats the response as human-readable text // formatText formats the response as human-readable text
func (f *DeadCodeFormatterImpl) formatText(response *domain.DeadCodeResponse) (string, error) { func (f *DeadCodeFormatterImpl) formatText(response *domain.DeadCodeResponse) (string, error) {
var output strings.Builder var output strings.Builder
utils := NewFormatUtils()
// Header // Header
output.WriteString("Dead Code Detection Results\n") output.WriteString(utils.FormatMainHeader("Dead Code Analysis Report"))
output.WriteString("============================\n\n")
// Summary // Summary
output.WriteString(fmt.Sprintf("Files analyzed: %d\n", response.Summary.TotalFiles)) stats := map[string]interface{}{
output.WriteString(fmt.Sprintf("Files with dead code: %d\n", response.Summary.FilesWithDeadCode)) "Total Files": response.Summary.TotalFiles,
output.WriteString(fmt.Sprintf("Total findings: %d\n", response.Summary.TotalFindings)) "Files with Dead Code": response.Summary.FilesWithDeadCode,
output.WriteString(fmt.Sprintf("Functions analyzed: %d\n", response.Summary.TotalFunctions)) "Total Findings": response.Summary.TotalFindings,
output.WriteString(fmt.Sprintf("Functions with dead code: %d\n\n", response.Summary.FunctionsWithDeadCode)) "Functions Analyzed": response.Summary.TotalFunctions,
"Functions with Issues": response.Summary.FunctionsWithDeadCode,
}
output.WriteString(utils.FormatSummaryStats(stats))
// Severity breakdown // Severity distribution (using standard risk levels)
output.WriteString("Severity Breakdown:\n") output.WriteString(utils.FormatRiskDistribution(
output.WriteString(fmt.Sprintf(" Critical: %d\n", response.Summary.CriticalFindings)) response.Summary.CriticalFindings, // Map Critical to High
output.WriteString(fmt.Sprintf(" Warning: %d\n", response.Summary.WarningFindings)) response.Summary.WarningFindings, // Map Warning to Medium
output.WriteString(fmt.Sprintf(" Info: %d\n\n", response.Summary.InfoFindings)) response.Summary.InfoFindings)) // Map Info to Low
// Files with findings // File Details
for _, file := range response.Files { if len(response.Files) > 0 && response.Summary.TotalFindings > 0 {
output.WriteString(fmt.Sprintf("File: %s\n", file.FilePath)) output.WriteString(utils.FormatSectionHeader("DETAILED FINDINGS"))
output.WriteString(strings.Repeat("=", len(file.FilePath)+6) + "\n")
for _, function := range file.Functions { for _, file := range response.Files {
output.WriteString(fmt.Sprintf("\nFunction: %s\n", function.Name)) if len(file.Functions) > 0 {
for _, finding := range function.Findings { output.WriteString(utils.FormatLabelWithIndent(0, "File", file.FilePath))
output.WriteString(f.formatFindingText(finding) + "\n") output.WriteString(strings.Repeat("-", HeaderWidth) + "\n")
for _, function := range file.Functions {
if len(function.Findings) > 0 {
output.WriteString(utils.FormatLabelWithIndent(SectionPadding, "Function", function.Name))
for _, finding := range function.Findings {
output.WriteString(f.formatFindingText(finding, utils) + "\n")
}
output.WriteString("\n")
}
}
} }
} }
output.WriteString("\n") output.WriteString(utils.FormatSectionSeparator())
} }
// Warnings and errors // Warnings
if len(response.Warnings) > 0 { if len(response.Warnings) > 0 {
output.WriteString("Warnings:\n") output.WriteString(utils.FormatWarningsSection(response.Warnings))
for _, warning := range response.Warnings {
output.WriteString(fmt.Sprintf(" - %s\n", warning))
}
output.WriteString("\n")
} }
// Errors
if len(response.Errors) > 0 { if len(response.Errors) > 0 {
output.WriteString("Errors:\n") output.WriteString(utils.FormatSectionHeader("ERRORS"))
for _, error := range response.Errors { for _, error := range response.Errors {
output.WriteString(fmt.Sprintf(" - %s\n", error)) output.WriteString(utils.FormatLabelWithIndent(SectionPadding, "❌", error))
} }
output.WriteString("\n") output.WriteString(utils.FormatSectionSeparator())
} }
return output.String(), nil return output.String(), nil
} }
// formatFindingText formats a single finding as text // formatFindingText formats a single finding as text
func (f *DeadCodeFormatterImpl) formatFindingText(finding domain.DeadCodeFinding) string { func (f *DeadCodeFormatterImpl) formatFindingText(finding domain.DeadCodeFinding, utils *FormatUtils) string {
// Convert severity to standard risk level
var standardRisk RiskLevel
switch finding.Severity {
case "critical":
standardRisk = RiskHigh
case "warning":
standardRisk = RiskMedium
case "info":
standardRisk = RiskLow
default:
standardRisk = RiskLow
}
coloredSeverity := utils.FormatRiskWithColor(standardRisk)
return fmt.Sprintf(" [%s] Line %d-%d: %s (%s)",
coloredSeverity,
finding.Location.StartLine,
finding.Location.EndLine,
finding.Description,
finding.Reason)
}
// Keep the old method for backward compatibility with FormatFinding
func (f *DeadCodeFormatterImpl) formatFindingTextLegacy(finding domain.DeadCodeFinding) string {
return fmt.Sprintf(" [%s] Line %d-%d: %s (%s)", return fmt.Sprintf(" [%s] Line %d-%d: %s (%s)",
strings.ToUpper(string(finding.Severity)), strings.ToUpper(string(finding.Severity)),
finding.Location.StartLine, finding.Location.StartLine,

View File

@@ -153,7 +153,7 @@ func (s *DeadCodeServiceImpl) analyzeFile(ctx context.Context, filePath string,
continue continue
} }
deadCodeResults := analyzer.DetectInFunction(cfg) deadCodeResults := analyzer.DetectInFunctionWithFilePath(cfg, filePath)
if deadCodeResults == nil { if deadCodeResults == nil {
warnings = append(warnings, fmt.Sprintf("[%s:%s] Failed to analyze dead code for function", filePath, functionName)) warnings = append(warnings, fmt.Sprintf("[%s:%s] Failed to analyze dead code for function", filePath, functionName))
continue continue

View File

@@ -2,7 +2,9 @@ package service
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"strings"
"github.com/ludo-technologies/pyscn/domain" "github.com/ludo-technologies/pyscn/domain"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -47,3 +49,176 @@ func WriteYAML(w io.Writer, v interface{}) error {
return nil return nil
} }
// Standard formatting constants
const (
HeaderWidth = 40
LabelWidth = 25
SectionPadding = 2
ItemPadding = 4
)
// ANSI color codes for consistent color usage
const (
ColorReset = "\x1b[0m"
ColorRed = "\x1b[31m"
ColorYellow = "\x1b[33m"
ColorGreen = "\x1b[32m"
ColorCyan = "\x1b[36m"
ColorBold = "\x1b[1m"
)
// RiskLevel represents the standard risk levels across all tools
type RiskLevel string
const (
RiskHigh RiskLevel = "High"
RiskMedium RiskLevel = "Medium"
RiskLow RiskLevel = "Low"
)
// FormatUtils provides shared formatting utilities
type FormatUtils struct{}
// NewFormatUtils creates a new format utilities instance
func NewFormatUtils() *FormatUtils {
return &FormatUtils{}
}
// FormatMainHeader creates a standardized main header
func (f *FormatUtils) FormatMainHeader(title string) string {
var builder strings.Builder
builder.WriteString(title + "\n")
builder.WriteString(strings.Repeat("=", HeaderWidth) + "\n\n")
return builder.String()
}
// FormatSectionHeader creates a standardized section header
func (f *FormatUtils) FormatSectionHeader(title string) string {
var builder strings.Builder
builder.WriteString(strings.ToUpper(title) + "\n")
builder.WriteString(strings.Repeat("-", len(title)) + "\n")
return builder.String()
}
// FormatSectionSeparator creates a section separator
func (f *FormatUtils) FormatSectionSeparator() string {
return "\n"
}
// FormatLabel creates a consistently formatted label with right alignment
func (f *FormatUtils) FormatLabel(label string, value interface{}) string {
padding := LabelWidth - len(label)
if padding < 0 {
padding = 0
}
return fmt.Sprintf("%s%s: %v\n", strings.Repeat(" ", padding), label, value)
}
// FormatLabelWithIndent creates a formatted label with specific indentation
func (f *FormatUtils) FormatLabelWithIndent(indent int, label string, value interface{}) string {
return fmt.Sprintf("%s%s: %v\n", strings.Repeat(" ", indent), label, value)
}
// FormatPercentage formats a percentage value consistently
func (f *FormatUtils) FormatPercentage(value float64) string {
return fmt.Sprintf("%.1f%%", value)
}
// FormatDuration formats duration in milliseconds consistently
func (f *FormatUtils) FormatDuration(durationMs int64) string {
return fmt.Sprintf("%dms", durationMs)
}
// GetRiskColor returns the appropriate color for a risk level
func (f *FormatUtils) GetRiskColor(risk RiskLevel) string {
switch risk {
case RiskHigh:
return ColorRed
case RiskMedium:
return ColorYellow
case RiskLow:
return ColorGreen
default:
return ColorReset
}
}
// FormatRiskWithColor formats a risk level with appropriate color
func (f *FormatUtils) FormatRiskWithColor(risk RiskLevel) string {
color := f.GetRiskColor(risk)
return fmt.Sprintf("%s%s%s", color, string(risk), ColorReset)
}
// FormatTableHeader creates a table header with consistent formatting
func (f *FormatUtils) FormatTableHeader(columns ...string) string {
header := strings.Join(columns, " ")
separator := strings.Repeat("-", len(header))
return header + "\n" + separator + "\n"
}
// FormatSummaryStats creates a standardized summary statistics section
func (f *FormatUtils) FormatSummaryStats(stats map[string]interface{}) string {
var builder strings.Builder
builder.WriteString(f.FormatSectionHeader("SUMMARY"))
for label, value := range stats {
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, label, value))
}
builder.WriteString(f.FormatSectionSeparator())
return builder.String()
}
// FormatRiskDistribution creates a standardized risk distribution section
func (f *FormatUtils) FormatRiskDistribution(high, medium, low int) string {
var builder strings.Builder
builder.WriteString(f.FormatSectionHeader("RISK DISTRIBUTION"))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "High", high))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "Medium", medium))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "Low", low))
builder.WriteString(f.FormatSectionSeparator())
return builder.String()
}
// FormatWarningsSection creates a standardized warnings section
func (f *FormatUtils) FormatWarningsSection(warnings []string) string {
if len(warnings) == 0 {
return ""
}
var builder strings.Builder
builder.WriteString(f.FormatSectionHeader("WARNINGS"))
for _, warning := range warnings {
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "⚠", warning))
}
builder.WriteString(f.FormatSectionSeparator())
return builder.String()
}
// ConvertToStandardRisk converts various risk representations to standard format
func (f *FormatUtils) ConvertToStandardRisk(risk string) RiskLevel {
switch strings.ToLower(risk) {
case "high", "critical", "error":
return RiskHigh
case "medium", "warning", "warn":
return RiskMedium
case "low", "info", "information":
return RiskLow
default:
return RiskLow
}
}
// FormatFileStats creates standardized file statistics
func (f *FormatUtils) FormatFileStats(analyzed, total, withIssues int) string {
var builder strings.Builder
builder.WriteString(f.FormatSectionHeader("FILE STATISTICS"))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "Total Files", total))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "Analyzed", analyzed))
builder.WriteString(f.FormatLabelWithIndent(SectionPadding, "With Issues", withIssues))
builder.WriteString(f.FormatSectionSeparator())
return builder.String()
}

View File

@@ -55,66 +55,75 @@ func (f *OutputFormatterImpl) Write(response *domain.ComplexityResponse, format
// formatText formats the response as human-readable text // formatText formats the response as human-readable text
func (f *OutputFormatterImpl) formatText(response *domain.ComplexityResponse) (string, error) { func (f *OutputFormatterImpl) formatText(response *domain.ComplexityResponse) (string, error) {
var builder strings.Builder var builder strings.Builder
utils := NewFormatUtils()
// Header // Header
builder.WriteString("Complexity Analysis Report\n") builder.WriteString(utils.FormatMainHeader("Complexity Analysis Report"))
builder.WriteString("==========================\n\n")
// Summary // Summary
builder.WriteString("Summary:\n") stats := map[string]interface{}{
builder.WriteString(fmt.Sprintf(" Total Functions: %d\n", response.Summary.TotalFunctions)) "Total Functions": response.Summary.TotalFunctions,
if response.Summary.TotalFunctions > 0 { "Files Analyzed": response.Summary.FilesAnalyzed,
builder.WriteString(fmt.Sprintf(" Average Complexity: %.2f\n", response.Summary.AverageComplexity))
builder.WriteString(fmt.Sprintf(" Max Complexity: %d\n", response.Summary.MaxComplexity))
builder.WriteString(fmt.Sprintf(" Min Complexity: %d\n", response.Summary.MinComplexity))
} }
builder.WriteString("\n") if response.Summary.TotalFunctions > 0 {
stats["Average Complexity"] = fmt.Sprintf("%.1f", response.Summary.AverageComplexity)
stats["Max Complexity"] = response.Summary.MaxComplexity
stats["Min Complexity"] = response.Summary.MinComplexity
}
builder.WriteString(utils.FormatSummaryStats(stats))
// Risk Distribution // Risk Distribution
builder.WriteString("Risk Distribution:\n") builder.WriteString(utils.FormatRiskDistribution(
builder.WriteString(fmt.Sprintf(" High: %d\n", response.Summary.HighRiskFunctions)) response.Summary.HighRiskFunctions,
builder.WriteString(fmt.Sprintf(" Medium: %d\n", response.Summary.MediumRiskFunctions)) response.Summary.MediumRiskFunctions,
builder.WriteString(fmt.Sprintf(" Low: %d\n", response.Summary.LowRiskFunctions)) response.Summary.LowRiskFunctions))
builder.WriteString("\n")
// Function Details // Function Details
if len(response.Functions) > 0 { if len(response.Functions) > 0 {
builder.WriteString("Function Details:\n") builder.WriteString(utils.FormatSectionHeader("FUNCTION DETAILS"))
builder.WriteString("Function Complexity Risk\n") builder.WriteString(utils.FormatTableHeader("Function", "Complexity", "Risk"))
builder.WriteString("------------------------------------------------\n")
for _, function := range response.Functions { for _, function := range response.Functions {
color := f.getRiskColor(function.RiskLevel) // Convert domain risk level to standard risk level
builder.WriteString(fmt.Sprintf("%-30s %10d %s%8s\x1b[0m\n", var standardRisk RiskLevel
switch function.RiskLevel {
case "High":
standardRisk = RiskHigh
case "Medium":
standardRisk = RiskMedium
case "Low":
standardRisk = RiskLow
default:
standardRisk = RiskLow
}
coloredRisk := utils.FormatRiskWithColor(standardRisk)
builder.WriteString(fmt.Sprintf("%-30s %10d %s\n",
function.Name, function.Name,
function.Metrics.Complexity, function.Metrics.Complexity,
color, coloredRisk))
function.RiskLevel))
} }
builder.WriteString("\n") builder.WriteString(utils.FormatSectionSeparator())
} }
// Warnings // Warnings
if len(response.Warnings) > 0 { if len(response.Warnings) > 0 {
builder.WriteString("Warnings:\n") builder.WriteString(utils.FormatWarningsSection(response.Warnings))
for _, warning := range response.Warnings {
builder.WriteString(fmt.Sprintf(" ⚠️ %s\n", warning))
}
builder.WriteString("\n")
} }
// Errors // Errors
if len(response.Errors) > 0 { if len(response.Errors) > 0 {
builder.WriteString("Errors:\n") builder.WriteString(utils.FormatSectionHeader("ERRORS"))
for _, err := range response.Errors { for _, err := range response.Errors {
builder.WriteString(fmt.Sprintf(" ❌ %s\n", err)) builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "❌", err))
} }
builder.WriteString("\n") builder.WriteString(utils.FormatSectionSeparator())
} }
// Footer // Footer
if parsedTime, err := time.Parse(time.RFC3339, response.GeneratedAt); err == nil { if parsedTime, err := time.Parse(time.RFC3339, response.GeneratedAt); err == nil {
builder.WriteString(fmt.Sprintf("Generated at: %s\n", parsedTime.Format("2006-01-02T15:04:05-07:00"))) builder.WriteString(utils.FormatSectionHeader("METADATA"))
builder.WriteString(utils.FormatLabelWithIndent(SectionPadding, "Generated at", parsedTime.Format("2006-01-02T15:04:05-07:00")))
} }
return builder.String(), nil return builder.String(), nil

View File

@@ -0,0 +1,185 @@
"""
Complex dead code patterns for advanced testing.
"""
import sys
from typing import Optional, Union
def complex_control_flow():
"""Complex function with multiple dead code patterns."""
state = "initial"
result = []
try:
if state == "initial":
state = "processing"
for i in range(10):
if i > 5:
raise StopIteration("Processing complete")
result.append(i)
# Dead code - loop always raises exception
print("Loop completed normally")
state = "completed"
except StopIteration:
return result
# Dead code after return in exception handler
print("Exception handled")
state = "error"
# Dead code - function always returns in exception handler
print("Function end")
return []
def generator_with_dead_code():
"""Generator function with dead code."""
yield 1
yield 2
return # Early return in generator
# Dead code after return
yield 3
yield 4
async def async_dead_code():
"""Async function with dead code."""
import asyncio
await asyncio.sleep(0.1)
return "async result"
# Dead code after return
await asyncio.sleep(1.0)
return "never reached"
def context_manager_dead_code():
"""Function with dead code in context manager."""
class CustomContext:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
return True # Suppress exception
with CustomContext():
raise ValueError("Test exception")
# Dead code after raise
print("This won't execute")
# This code is reachable due to exception suppression
return "context completed"
def decorator_dead_code():
"""Function with dead code in decorator pattern."""
def early_return_decorator(func):
def wrapper(*args, **kwargs):
return "decorator result"
# Dead code in decorator
result = func(*args, **kwargs)
return f"decorated: {result}"
return wrapper
@early_return_decorator
def decorated_function():
print("Original function")
return "original result"
return decorated_function()
def class_method_dead_code():
"""Function demonstrating dead code in class methods."""
class TestClass:
def __init__(self):
self.value = 10
return # Early return in __init__
# Dead code
self.other_value = 20
self._setup()
def _setup(self):
"""This method is never called."""
self.configured = True
def method_with_dead_code(self):
if self.value > 0:
return self.value * 2
# Dead code - value is always > 0
print("Negative value handling")
return 0
obj = TestClass()
return obj.method_with_dead_code()
def lambda_dead_code():
"""Function with dead code involving lambdas."""
always_true = True
if always_true:
func = lambda x: x * 2
return func(5)
# Dead code - condition always true
func = lambda x: x * 3
return func(5)
def comprehension_dead_code():
"""Function with dead code in comprehensions."""
data = [1, 2, 3, 4, 5]
if len(data) > 0:
return [x * 2 for x in data if x > 0]
# Dead code - data always has length > 0
return [x for x in data if x < 0]
def exception_hierarchy_dead_code():
"""Function with dead code in exception hierarchy."""
try:
raise ValueError("Test error")
except Exception:
return "caught general exception"
except ValueError:
# Dead code - ValueError is already caught by Exception
return "caught specific exception"
# Dead code - exception always caught
return "no exception"
def finally_block_patterns():
"""Function demonstrating dead code with finally blocks."""
try:
return "try block result"
# Dead code after return in try block
print("After return in try")
except Exception:
return "exception result"
# Dead code after return in except block
print("After return in except")
finally:
# This code is reachable - finally always executes
print("Finally block executes")
return "finally result" # This overrides other returns
# Dead code after try-except-finally
return "end of function"
if __name__ == "__main__":
print(complex_control_flow())
print(class_method_dead_code())
print(comprehension_dead_code())

View File

@@ -0,0 +1,174 @@
"""
Examples of dead code patterns for testing dead code detection.
This file contains various types of unreachable code.
"""
def unreachable_after_return():
"""Function with code after return statement."""
x = 10
if x > 5:
return "greater than 5"
# This code is unreachable - dead code
print("This will never execute")
return "fallback"
def unreachable_after_raise():
"""Function with code after raise statement."""
value = None
if value is None:
raise ValueError("Value cannot be None")
# This code is unreachable - dead code
print("This will never execute")
return value * 2
def unreachable_conditional_branch():
"""Function with unreachable conditional branches."""
debug_mode = False
if debug_mode:
print("Debug mode enabled")
else:
print("Normal mode")
# This condition will never be True - dead code
if debug_mode and not debug_mode:
print("This is impossible")
return "impossible"
return "normal"
def unreachable_after_break():
"""Function with code after break statement."""
items = [1, 2, 3, 4, 5]
for item in items:
if item == 3:
break
# This code is unreachable - dead code
print(f"Processing {item}")
item += 1
return "done"
def unreachable_after_continue():
"""Function with code after continue statement."""
numbers = [1, 2, 3, 4, 5]
result = []
for num in numbers:
if num % 2 == 0:
continue
# This code is unreachable - dead code
print(f"Even number: {num}")
result.append(num)
result.append(num)
return result
def multiple_returns_with_dead_code():
"""Function with multiple return statements and dead code."""
status = "active"
if status == "active":
return "User is active"
elif status == "inactive":
return "User is inactive"
else:
return "Unknown status"
# This code is unreachable - dead code
print("This should never execute")
status = "processed"
return f"Status: {status}"
def nested_dead_code():
"""Function with nested dead code patterns."""
data = {"valid": True}
if data.get("valid"):
if True: # Always true condition
return "valid data"
# Dead code after return in nested block
print("Never reached in nested block")
# Dead code - the inner if always returns
print("Never reached in outer block")
# Dead code - the outer if always returns
return "invalid data"
class DeadCodeClass:
"""Class with dead code examples."""
def __init__(self):
self.active = True
return # Early return in __init__
# Dead code after return
self.inactive = False
self.setup()
def setup(self):
"""This method is never called due to dead code in __init__."""
print("Setting up...")
return True
def method_with_dead_code(self):
"""Method with unreachable code."""
if self.active:
return "active"
# Dead code - method always returns above
print("This is dead code")
self.active = False
return "inactive"
def infinite_loop_with_break():
"""Function with unreachable code after infinite loop."""
counter = 0
while True:
counter += 1
if counter > 10:
break
# This code is reachable - NOT dead code
print(f"Counter reached: {counter}")
while True:
print("Infinite loop")
return "exited"
# This code is unreachable - dead code
print("After infinite loop")
return "never reached"
def early_exit_pattern():
"""Function with early exit patterns."""
import sys
condition = False
if not condition:
sys.exit(1)
# This code is unreachable if condition is always False - dead code
print("After sys.exit")
return "completed"
if __name__ == "__main__":
# Test the functions
print(unreachable_after_return())
print(unreachable_conditional_branch())
# Note: unreachable_after_raise() would raise an exception

View File

@@ -0,0 +1,66 @@
"""
Simple examples of dead code for testing.
"""
def simple_dead_code():
"""Simple function with dead code after return."""
name = "test"
return f"Hello, {name}"
# Dead code - unreachable
print("This line will never execute")
name = "updated"
def conditional_dead_code():
"""Function with dead code in conditional."""
always_true = True
if always_true:
return "always returns here"
# Dead code - condition is always true
print("This is unreachable")
return "never reached"
def loop_with_dead_code():
"""Function with dead code after loop break."""
for i in range(5):
if i == 2:
break
# Dead code after break
print(f"Processing {i}")
print(f"Number: {i}")
return "loop completed"
def exception_dead_code():
"""Function with dead code after exception."""
data = None
if data is None:
raise ValueError("Data is required")
# Dead code - exception always raised
print("Processing data")
return len(data)
def nested_return_dead_code():
"""Function with nested returns and dead code."""
status = "ok"
if status == "ok":
if True:
return "success"
# Dead code in nested block
print("Never reached nested")
# Dead code in outer block
print("Never reached outer")
# Dead code at function level
return "failure"