mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 08:08:42 +00:00
Systematically resolved 30 identified potential crash vectors and established automated regression testing to prevent strict reoccurrence. Key Changes: - Fixed 11 instances of unsafe `QObject::connect` calls (missing context object) in srd/Charts/AgendaWindow.cpp, FixSpikes.cpp, src/FileIO/FixSpikes.cpp, src/Gui/Agenda.cpp, src/Gui/BatchProcessingDialog.cpp, and src/Gui/IconManager.cpp. This prevents crashes caused by signals firing after the receiver has been destroyed. - Fixed 19 instances of unsafe `QTreeWidgetItem` child access in src/Charts/LTMChartParser.cpp, src/Gui/ColorButton.cpp, src/Gui/AthletePages.cpp, and src/Gui/Pages.cpp by adding defensive `nullptr` checks before dereferencing. - Added Python detection scripts util/check_unsafe_connects.py and util/check_unsafe_tree_child.py to statically analyze the codebase for these specific unsafe patterns. - Integrated detection scripts into the regression test suite under `unittests/Core/signalSafety`, verifying the fixes and enforcing a strict zero-tolerance policy for future regressions. - Added `testSplineCrash` to cover edge cases with empty spline lookups.
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
import re
|
|
import os
|
|
import sys
|
|
|
|
def check_file(filepath):
|
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
|
content = f.read()
|
|
|
|
# Pattern: invisibleRootItem()->child(index)
|
|
# We want to catch cases where this is used directly without checking the result.
|
|
# It's hard to prove "without checking" via regex, but we can look for immediate dereference or usage strings.
|
|
# e.g. "invisibleRootItem()->child(x)->text(0)" is definitely unsafe if child(x) returns null.
|
|
# "QTreeWidgetItem *item = ...->child(x);" is okay IF followed by a check.
|
|
|
|
# Let's focus on immediate chaining which is the most dangerous:
|
|
# ->child(...)->
|
|
# or ->child(...).
|
|
|
|
warnings = []
|
|
|
|
# Regex for immediate dereference: child(...) ->
|
|
regex = re.compile(r'invisibleRootItem\(\)->child\([^)]+\)->')
|
|
|
|
for i, line in enumerate(content.splitlines()):
|
|
if regex.search(line):
|
|
warnings.append((i+1, line.strip()))
|
|
|
|
return warnings
|
|
|
|
def scan_directory(root_dir):
|
|
print(f"Scanning {root_dir}...")
|
|
count = 0
|
|
found = 0
|
|
for root, dirs, files in os.walk(root_dir):
|
|
for file in files:
|
|
if file.endswith('.cpp') or file.endswith('.h'):
|
|
filepath = os.path.join(root, file)
|
|
matches = check_file(filepath)
|
|
if matches:
|
|
print(f"File: {filepath}")
|
|
for line, text in matches:
|
|
print(f" Line {line}: UNSAFE CHAINING: {text}")
|
|
found += len(matches)
|
|
count += 1
|
|
print(f"Scanned {count} files. Found {found} potential unsafe usages.")
|
|
return found
|
|
|
|
if __name__ == "__main__":
|
|
found = 0
|
|
if len(sys.argv) > 1:
|
|
found = scan_directory(sys.argv[1])
|
|
else:
|
|
if os.path.exists("src"):
|
|
found = scan_directory("src")
|
|
elif os.path.exists("../src"):
|
|
found = scan_directory("../src")
|
|
else:
|
|
print("Could not find src directory.")
|
|
sys.exit(1)
|
|
|
|
if found > 0:
|
|
sys.exit(1)
|
|
sys.exit(0)
|