Framework pull: auto-revert use statement changes before update

Class override system: .upstream file handling and restore logic
Php_Fixer: Redirect use statements to correct manifest FQCN
Fix: Only match PHP files in __find_class_fqcn_in_manifest
Complete Php_Fixer use statement redirection implementation (checkpoint 2)
WIP: Php_Fixer use statement redirection for class overrides (checkpoint)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-10 07:04:05 +00:00
parent 0a479ed5a2
commit f02a04f37a
6 changed files with 307 additions and 51 deletions

View File

@@ -94,6 +94,12 @@ class Monoprogenic_CodeQualityRule extends CodeQualityRule_Abstract
// Step 1: Find all classes with Monoprogenic attribute // Step 1: Find all classes with Monoprogenic attribute
$monoprogenic_classes = []; $monoprogenic_classes = [];
foreach ($files as $file => $file_metadata) { foreach ($files as $file => $file_metadata) {
// Skip .upstream files - they are framework files overridden by rsx/
$extension = $file_metadata['extension'] ?? '';
if ($extension === 'php.upstream') {
continue;
}
if (isset($file_metadata['attributes']['Monoprogenic'])) { if (isset($file_metadata['attributes']['Monoprogenic'])) {
$fqcn = $file_metadata['fqcn'] ?? null; $fqcn = $file_metadata['fqcn'] ?? null;
if ($fqcn) { if ($fqcn) {
@@ -119,6 +125,12 @@ class Monoprogenic_CodeQualityRule extends CodeQualityRule_Abstract
{ {
// Find all classes that extend from any Monoprogenic class // Find all classes that extend from any Monoprogenic class
foreach ($files as $file => $metadata) { foreach ($files as $file => $metadata) {
// Skip .upstream files - they are framework files overridden by rsx/
$extension = $metadata['extension'] ?? '';
if ($extension === 'php.upstream') {
continue;
}
$fqcn = $metadata['fqcn'] ?? null; $fqcn = $metadata['fqcn'] ?? null;
if (!$fqcn || !class_exists($fqcn)) { if (!$fqcn || !class_exists($fqcn)) {
continue; continue;

View File

@@ -38,6 +38,7 @@ class ExtensionRegistry
*/ */
protected const CORE_EXTENSIONS = [ protected const CORE_EXTENSIONS = [
'php' => ['priority' => 100], 'php' => ['priority' => 100],
'php.upstream' => ['priority' => 101], // Framework files renamed during class override
'js' => ['priority' => 200], 'js' => ['priority' => 200],
'jsx' => ['priority' => 200], 'jsx' => ['priority' => 200],
'ts' => ['priority' => 200], 'ts' => ['priority' => 200],

View File

@@ -1355,23 +1355,17 @@ class Manifest
// but we don't need to log it as it's not an error condition // but we don't need to log it as it's not an error condition
} }
// Validate class names are unique (also handles rsx/ overriding app/RSpade/ classes)
static::__check_unique_base_class_names();
// If a class override was detected (rsx/ overriding app/RSpade/), restart manifest build
if (static::$_needs_manifest_restart) {
console_debug('MANIFEST', 'Class override detected, restarting manifest build');
goto manifest_start;
}
// ================================================================================== // ==================================================================================
// PHP FIXER INTEGRATION POINT // PHP FIXER INTEGRATION POINT
// ================================================================================== // ==================================================================================
// This is where automatic code fixes are applied before Phase 2 parsing. // CRITICAL: Php_Fixer MUST run BEFORE __check_unique_base_class_names() so that
// when a class override is detected and framework files are renamed to .upstream,
// all use statements have already been updated to point to the rsx/ location.
// This prevents autoloader failures when the manifest restarts.
// //
// WHAT PHP_FIXER DOES: // WHAT PHP_FIXER DOES:
// 1. Fixes namespaces to match file paths // 1. Fixes namespaces to match file paths
// 2. Removes/rebuilds use statements (strips Rsx\ and App\RSpade\ prefixes) // 2. Redirects use statements to correct FQCN based on manifest (rsx/ takes priority)
// 3. Replaces FQCNs like \Rsx\Models\User_Model with simple names User_Model // 3. Replaces FQCNs like \Rsx\Models\User_Model with simple names User_Model
// 4. Adds #[Relationship] attributes to model ORM methods // 4. Adds #[Relationship] attributes to model ORM methods
// 5. Removes leading backslashes from attributes: #[\Route] → #[Route] // 5. Removes leading backslashes from attributes: #[\Route] → #[Route]
@@ -1381,11 +1375,6 @@ class Manifest
// - If structure changed: Fixes ALL files (cascading updates needed) // - If structure changed: Fixes ALL files (cascading updates needed)
// - If structure unchanged: Fixes ONLY $files_to_process (incremental) // - If structure unchanged: Fixes ONLY $files_to_process (incremental)
// //
// WHY BEFORE PHASE 2:
// - Phase 2 parses metadata from file content
// - If we fix AFTER parsing, manifest would have old/incorrect metadata
// - By fixing BEFORE, we parse the corrected content
//
// RE-PARSING LOOP BELOW: // RE-PARSING LOOP BELOW:
// - If Php_Fixer modified files, we MUST re-parse them // - If Php_Fixer modified files, we MUST re-parse them
// - This updates manifest with corrected namespace/class/FQCN data // - This updates manifest with corrected namespace/class/FQCN data
@@ -1422,6 +1411,21 @@ class Manifest
} }
} }
// ==================================================================================
// CLASS OVERRIDE DETECTION
// ==================================================================================
// Check for duplicate class names. When rsx/ contains a class that also exists in
// app/RSpade/, rename the framework file to .upstream and restart the manifest.
// At this point, Php_Fixer has already updated use statements to point to rsx/.
// ==================================================================================
static::__check_unique_base_class_names();
// If a class override was detected (rsx/ overriding app/RSpade/), restart manifest build
if (static::$_needs_manifest_restart) {
console_debug('MANIFEST', 'Class override detected, restarting manifest build');
goto manifest_start;
}
// Phase 2 complete. At this point we have a list of all files, and for php and js, their class data // Phase 2 complete. At this point we have a list of all files, and for php and js, their class data
// ======================================================= // =======================================================
@@ -1706,14 +1710,25 @@ class Manifest
/** /**
* Check for duplicate base class names within the same file type * Check for duplicate base class names within the same file type
* *
* When a class exists in both rsx/ and app/RSpade/, this is a developer override. * This method handles two scenarios:
* The framework version is renamed to .upstream and removed from indexing. * 1. RESTORE: If a .upstream file exists but no active override exists, restore it
* 2. OVERRIDE: When a class exists in both rsx/ and app/RSpade/, rename framework to .upstream
* *
* Throws a fatal error if duplicates exist within the same area (both rsx/ or both app/RSpade/) * Throws a fatal error if duplicates exist within the same area (both rsx/ or both app/RSpade/)
*/ */
protected static function __check_unique_base_class_names(): void protected static function __check_unique_base_class_names(): void
{ {
// Group classes by extension first, then by class name // ==================================================================================
// STEP 1: Restore orphaned .upstream files
// ==================================================================================
// Check all php.upstream files - if their class no longer has an override in rsx/,
// restore the framework file by renaming .upstream back to .php
// ==================================================================================
static::__restore_orphaned_upstream_files();
// ==================================================================================
// STEP 2: Group classes by extension, then by class name
// ==================================================================================
$classes_by_extension = []; $classes_by_extension = [];
foreach (static::$data['data']['files'] as $file => $metadata) { foreach (static::$data['data']['files'] as $file => $metadata) {
@@ -1721,6 +1736,11 @@ class Manifest
$base_class_name = $metadata['class']; $base_class_name = $metadata['class'];
$extension = $metadata['extension'] ?? ''; $extension = $metadata['extension'] ?? '';
// Skip php.upstream files - they're tracked separately for restoration
if ($extension === 'php.upstream') {
continue;
}
// Group JavaScript-like files together // Group JavaScript-like files together
if (in_array($extension, ['js', 'jsx', 'ts', 'tsx'])) { if (in_array($extension, ['js', 'jsx', 'ts', 'tsx'])) {
$extension = 'js'; $extension = 'js';
@@ -1738,7 +1758,9 @@ class Manifest
} }
} }
// Check for duplicates within each extension type // ==================================================================================
// STEP 3: Check for duplicates and create overrides
// ==================================================================================
foreach ($classes_by_extension as $extension => $base_class_files) { foreach ($classes_by_extension as $extension => $base_class_files) {
foreach ($base_class_files as $class_name => $files) { foreach ($base_class_files as $class_name => $files) {
if (count($files) > 1) { if (count($files) > 1) {
@@ -1779,6 +1801,58 @@ class Manifest
} }
} }
/**
* Restore orphaned .upstream files when their override no longer exists
*
* Scans all php.upstream files in the manifest. For each one, checks if a .php file
* with the same class name exists. If not, the override was removed and we should
* restore the framework file.
*/
protected static function __restore_orphaned_upstream_files(): void
{
// Collect all PHP class names currently in the manifest
$active_php_classes = [];
foreach (static::$data['data']['files'] as $file => $metadata) {
if (isset($metadata['extension']) && $metadata['extension'] === 'php' &&
isset($metadata['class']) && !empty($metadata['class'])) {
$active_php_classes[$metadata['class']] = $file;
}
}
// Check each .upstream file
foreach (static::$data['data']['files'] as $file => $metadata) {
if (!isset($metadata['extension']) || $metadata['extension'] !== 'php.upstream') {
continue;
}
if (!isset($metadata['class']) || empty($metadata['class'])) {
continue;
}
$class_name = $metadata['class'];
// Check if an active .php file with this class exists
if (isset($active_php_classes[$class_name])) {
// Override still exists - keep .upstream as-is
continue;
}
// No active override - restore this framework file
$upstream_path = base_path($file);
$restored_path = preg_replace('/\.upstream$/', '', $upstream_path);
if (file_exists($upstream_path) && !file_exists($restored_path)) {
rename($upstream_path, $restored_path);
console_debug('MANIFEST', "Class restore: {$class_name} - restored {$file} to .php");
// Remove the .upstream entry from manifest
unset(static::$data['data']['files'][$file]);
static::$_needs_manifest_restart = true;
}
}
}
/** /**
* Collate files by class names and build inheritance indices * Collate files by class names and build inheritance indices
* *
@@ -2515,9 +2589,11 @@ class Manifest
$stat = stat($absolute_path); $stat = stat($absolute_path);
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); $extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
// Handle .blade.php as special case // Handle compound extensions as special cases
if (str_ends_with($file_path, '.blade.php')) { if (str_ends_with($file_path, '.blade.php')) {
$extension = 'blade.php'; $extension = 'blade.php';
} elseif (str_ends_with($file_path, '.php.upstream')) {
$extension = 'php.upstream';
} }
$data = [ $data = [
@@ -2550,6 +2626,14 @@ class Manifest
} }
break; break;
case 'php.upstream':
// Parse upstream files to extract class metadata
// These are framework files that were renamed when an rsx/ override was created
// Php_Fixer only runs on extension 'php', so upstream files are safe
$php_metadata = \App\RSpade\Core\PHP\Php_Parser::parse($absolute_path, static::$data);
$data = array_merge($data, $php_metadata);
break;
case 'js': case 'js':
console_debug('BUILD', "Parsing JS file: {$file_path}"); console_debug('BUILD', "Parsing JS file: {$file_path}");
$js_metadata = \App\RSpade\Core\JsParsers\Js_Parser::extract_metadata($absolute_path); $js_metadata = \App\RSpade\Core\JsParsers\Js_Parser::extract_metadata($absolute_path);

View File

@@ -428,11 +428,11 @@ class Php_Fixer
$tokens = token_get_all($content); $tokens = token_get_all($content);
// Extract existing non-Rsx\ use statement simple names BEFORE removing Rsx\ statements // Extract existing non-Rsx\ use statement simple names BEFORE removing Rsx\ statements
$existing_non_rsx_simple_names = self::__extract_existing_non_rsx_use_simple_names($tokens, $file_path); $existing_non_rsx_simple_names = self::__extract_existing_non_rsx_use_simple_names($tokens, $file_path, $step_2_manifest_data);
// Step 1: Remove all use statements that start with Rsx\ // Step 1: Process use statements - remove, keep, or redirect to correct FQCN
// We'll rebuild these based on actual usage // The manifest is the source of truth for where classes are defined
$content = self::__remove_rsx_use_statements($content, $tokens, $file_path); $content = self::__remove_rsx_use_statements($content, $tokens, $file_path, $step_2_manifest_data);
// Step 2: For both rsx/ and app/RSpade/ files - fix fully qualified class names // Step 2: For both rsx/ and app/RSpade/ files - fix fully qualified class names
// Replace \Rsx\Namespace\ClassName with just ClassName if the class exists uniquely // Replace \Rsx\Namespace\ClassName with just ClassName if the class exists uniquely
@@ -507,9 +507,11 @@ class Php_Fixer
* Extract simple class names from existing use statements that won't be removed * Extract simple class names from existing use statements that won't be removed
* *
* @param array $tokens Parsed tokens * @param array $tokens Parsed tokens
* @param string $file_path File being processed
* @param array $step_2_manifest_data Manifest data for class lookups
* @return array Simple class names already in use (e.g., ['Session', 'Request', 'Cache']) * @return array Simple class names already in use (e.g., ['Session', 'Request', 'Cache'])
*/ */
private static function __extract_existing_non_rsx_use_simple_names(array $tokens, string $file_path): array private static function __extract_existing_non_rsx_use_simple_names(array $tokens, string $file_path, array &$step_2_manifest_data): array
{ {
$simple_names = []; $simple_names = [];
$i = 0; $i = 0;
@@ -569,14 +571,19 @@ class Php_Fixer
); );
} }
// Check if this use statement will be kept (not removed) // Check if this use statement will be kept (not removed/redirected)
$will_be_removed = self::__should_remove_use_statement($use_fqcn, $file_path); $action = self::__should_remove_use_statement($use_fqcn, $file_path, $step_2_manifest_data);
if (!$will_be_removed) { if ($action === false) {
// Extract simple name (last part after final backslash) // Keeping as-is - extract simple name
$simple_name = substr(strrchr($use_fqcn, '\\'), 1) ?: $use_fqcn; $simple_name = substr(strrchr($use_fqcn, '\\'), 1) ?: $use_fqcn;
$simple_names[$simple_name] = true; $simple_names[$simple_name] = true;
} elseif (is_string($action)) {
// Redirecting to new FQCN - extract simple name from new FQCN
$simple_name = substr(strrchr($action, '\\'), 1) ?: $action;
$simple_names[$simple_name] = true;
} }
// If $action === true, it's being removed, don't add to simple_names
} }
$i++; $i++;
@@ -586,36 +593,44 @@ class Php_Fixer
} }
/** /**
* Determine if a use statement should be removed * Determine what action to take on a use statement
*
* Returns one of:
* - false: Keep as-is (vendor/Laravel classes, or already correct)
* - true: Remove completely (framework dev mode for Rsx\ classes, or attribute stubs)
* - string: Update to this FQCN (use statement points to wrong FQCN for this class)
*
* The manifest is the source of truth. If a class exists in the manifest,
* the use statement should point to the manifest's FQCN for that class.
* *
* @param string $use_fqcn Fully qualified class name from use statement * @param string $use_fqcn Fully qualified class name from use statement
* @return bool True if should be removed, false if should be kept * @param string $file_path File being processed
* @param array $step_2_manifest_data Manifest data for class lookups
* @return bool|string False=keep, true=remove, string=update to new FQCN
*/ */
private static function __should_remove_use_statement(string $use_fqcn, string $file_path): bool private static function __should_remove_use_statement(string $use_fqcn, string $file_path, array &$step_2_manifest_data): bool|string
{ {
// Get excluded directories from config // Get excluded directories from config
$excluded_dirs = config('rsx.manifest.excluded_dirs', ['vendor', 'node_modules', 'storage', '.git', 'public', 'resource', 'archived']); $excluded_dirs = config('rsx.manifest.excluded_dirs', ['vendor', 'node_modules', 'storage', '.git', 'public', 'resource', 'archived']);
// Extract namespace parts (everything except the final class name) // Extract simple class name and namespace
$last_backslash = strrpos($use_fqcn, '\\'); $last_backslash = strrpos($use_fqcn, '\\');
$simple_name = $last_backslash !== false ? substr($use_fqcn, $last_backslash + 1) : $use_fqcn;
if ($last_backslash !== false) { if ($last_backslash !== false) {
$namespace_part = substr($use_fqcn, 0, $last_backslash); $namespace_part = substr($use_fqcn, 0, $last_backslash);
// Check if namespace contains any excluded directory keyword (case insensitive) // Check if namespace contains any excluded directory keyword (case insensitive)
// These are protected namespaces (vendor, Laravel, etc.) - never modify
foreach ($excluded_dirs as $excluded_dir) { foreach ($excluded_dirs as $excluded_dir) {
if (stripos($namespace_part, $excluded_dir) !== false) { if (stripos($namespace_part, $excluded_dir) !== false) {
return false; // Protected - don't remove return false; // Protected - don't touch
} }
} }
} }
// Remove "Route" explicitly, this refers to the laravel Route:: helper which we just dont use. If the developer must, they should use the fqcn of the laravel route helper. The existience of this confuses the ide with how to deal with attributes. // Remove "Route" explicitly - refers to Laravel Route:: helper which conflicts with our #[Route] attribute
if ($use_fqcn == 'Route' && (substr($file_path, 0, 4) == 'rsx/' || substr($file_path, 0, 11) == 'app/RSpade/')) { if ($use_fqcn == 'Route' && (str_starts_with($file_path, 'rsx/') || str_starts_with($file_path, 'app/RSpade/'))) {
return true;
}
// Remove if starts with Rsx\ or App\RSpade\
if (str_starts_with($use_fqcn, 'Rsx\\') || str_starts_with($use_fqcn, 'App\\RSpade\\')) {
return true; return true;
} }
@@ -633,17 +648,87 @@ class Php_Fixer
} }
} }
// Look up the correct FQCN for this class in the manifest
$correct_fqcn = self::__find_class_fqcn_in_manifest($simple_name, $step_2_manifest_data);
if ($correct_fqcn !== null) {
// Class exists in manifest - check if use statement is correct
if ($use_fqcn === $correct_fqcn) {
// Already pointing to correct FQCN
$is_framework_developer = config('rsx.code_quality.is_framework_developer', false);
// In framework developer mode, remove Rsx\ use statements (autoloader finds them)
if ($is_framework_developer && str_starts_with($use_fqcn, 'Rsx\\')) {
return true;
}
return false; // Keep as-is
} else {
// Use statement points to wrong FQCN
$is_framework_developer = config('rsx.code_quality.is_framework_developer', false);
if ($is_framework_developer && str_starts_with($correct_fqcn, 'Rsx\\')) {
// Framework developer mode: remove, autoloader will find Rsx\ classes
return true;
}
// Update to correct FQCN
return $correct_fqcn;
}
}
// Check if this is a Rsx\ or App\RSpade\ use statement for a class NOT in manifest
// These should be removed (will be rebuilt based on actual usage)
if (str_starts_with($use_fqcn, 'Rsx\\') || str_starts_with($use_fqcn, 'App\\RSpade\\')) {
return true;
}
return false; // Keep all other use statements return false; // Keep all other use statements
} }
/** /**
* Remove existing Rsx\ use statements from the file * Find the correct FQCN for a PHP class by simple name in the manifest
*
* Searches the entire manifest for the class. Returns the FQCN from the manifest,
* which is the authoritative source for where classes are defined.
*
* @param string $simple_name Simple class name (e.g., "User_Model")
* @param array $step_2_manifest_data Manifest data
* @return string|null FQCN if found in manifest, null otherwise
*/
private static function __find_class_fqcn_in_manifest(string $simple_name, array &$step_2_manifest_data): ?string
{
foreach ($step_2_manifest_data['data']['files'] ?? [] as $manifest_file => $metadata) {
// Only check PHP files (not JS classes with same name)
if (!str_ends_with($manifest_file, '.php')) {
continue;
}
// Only check files in rsx/ or app/RSpade/
if (!str_starts_with($manifest_file, 'rsx/') && !str_starts_with($manifest_file, 'app/RSpade/')) {
continue;
}
// Check if this file defines the class we're looking for
if (isset($metadata['class']) && $metadata['class'] === $simple_name) {
// Generate FQCN from file path
return self::__calculate_namespace_from_path($manifest_file) . '\\' . $simple_name;
}
}
return null;
}
/**
* Process use statements - remove, keep, or redirect as needed
* *
* @param string $content File content * @param string $content File content
* @param array $tokens Parsed tokens * @param array $tokens Parsed tokens
* @return string Content with Rsx use statements removed * @param string $file_path File being processed
* @param array $step_2_manifest_data Manifest data for class lookups
* @return string Content with use statements processed
*/ */
private static function __remove_rsx_use_statements(string $content, array $tokens, string $file_path): string private static function __remove_rsx_use_statements(string $content, array $tokens, string $file_path, array &$step_2_manifest_data): string
{ {
$output = ''; $output = '';
$i = 0; $i = 0;
@@ -687,10 +772,10 @@ class Php_Fixer
$use_statement = trim($use_statement); $use_statement = trim($use_statement);
// Check if this use statement should be kept // Check what action to take on this use statement
$should_remove = self::__should_remove_use_statement($use_statement, $file_path); $action = self::__should_remove_use_statement($use_statement, $file_path, $step_2_manifest_data);
if (!$should_remove) { if ($action === false) {
// Keep it - go back and add the original use statement // Keep it - go back and add the original use statement
for ($j = $use_start; $j <= $i; $j++) { for ($j = $use_start; $j <= $i; $j++) {
if ($j < count($tokens)) { if ($j < count($tokens)) {
@@ -701,8 +786,11 @@ class Php_Fixer
} }
} }
} }
} elseif (is_string($action)) {
// Redirect - output a new use statement with the correct FQCN
$output .= "use {$action};";
} }
// Otherwise skip it (don't add to output) // If $action === true, skip it entirely (don't add to output)
} else { } else {
// Add token to output // Add token to output
if (is_array($token)) { if (is_array($token)) {

View File

@@ -3,9 +3,9 @@
namespace App\RSpade\Core\SPA; namespace App\RSpade\Core\SPA;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Session;
use App\RSpade\Core\Controller\Rsx_Controller_Abstract; use App\RSpade\Core\Controller\Rsx_Controller_Abstract;
use App\RSpade\Core\Manifest\Manifest; use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Session\Session;
/** /**
* Spa_Session_Controller - Session validation endpoint for SPA navigation * Spa_Session_Controller - Session validation endpoint for SPA navigation

View File

@@ -216,6 +216,77 @@ if [ -n "$UPSTREAM_FILES" ]; then
done done
fi fi
# Step 3: Restore files with ONLY use statement changes
# When classes are overridden in rsx/, Php_Fixer updates use statements in system/ files
# to point to the Rsx\ namespace. These changes should be reverted before framework update.
# We only auto-revert files where ALL changes are use statement modifications.
echo " Checking for auto-fixable use statement changes..."
MODIFIED_SYSTEM_FILES=$(git diff --name-only -- . ":(exclude)rsx" 2>/dev/null | grep "\.php$" || true)
USE_STMT_REVERTED=0
if [ -n "$MODIFIED_SYSTEM_FILES" ]; then
while IFS= read -r modified_file; do
if [ -z "$modified_file" ] || [ ! -f "$modified_file" ]; then
continue
fi
# Get the diff for this file, check if ALL changed lines are use statements
# Changed lines start with + or - (excluding the diff header lines +++ and ---)
DIFF_CONTENT=$(git diff -- "$modified_file" 2>/dev/null || true)
if [ -z "$DIFF_CONTENT" ]; then
continue
fi
# Extract changed lines (+ or - at start, not +++ or ---)
# Then check if all of them are use statements or empty
ALL_USE_STATEMENTS=true
while IFS= read -r line; do
# Skip diff metadata lines
case "$line" in
"+++"*|"---"*|"@@"*|"diff "*|"index "*) continue ;;
esac
# Check lines that start with + or - (actual changes)
case "$line" in
"+"*|"-"*)
# Remove the leading +/- and trim whitespace
content="${line#[+-]}"
content=$(echo "$content" | sed 's/^[[:space:]]*//')
# Empty lines are OK
if [ -z "$content" ]; then
continue
fi
# Use statements are OK (with or without leading backslash)
if echo "$content" | grep -qE "^use[[:space:]]+(\\\\)?[A-Za-z]"; then
continue
fi
# Any other change means this file has non-use-statement modifications
ALL_USE_STATEMENTS=false
break
;;
esac
done <<< "$DIFF_CONTENT"
# If all changes were use statements, revert the file
if [ "$ALL_USE_STATEMENTS" = true ]; then
if git checkout HEAD -- "$modified_file" 2>/dev/null; then
echo " ✓ Reverted use statements: $modified_file"
USE_STMT_REVERTED=$((USE_STMT_REVERTED + 1))
fi
fi
done <<< "$MODIFIED_SYSTEM_FILES"
fi
if [ "$USE_STMT_REVERTED" -gt 0 ]; then
echo " ✓ Reverted $USE_STMT_REVERTED file(s) with use statement changes"
fi
echo " ✓ Override cleanup complete" echo " ✓ Override cleanup complete"
echo "" echo ""