Repository: carthage-software/mago (Rust)

Issue:
False positive for `invalid-method-access`
### 🐞 Describe the Bug

Mago reports `invalid-method-access` if method is declared in a trait that is not directly used in class.

### 🔄 Steps to Reproduce

Run `mago analyze` on sample code.

### ⚙️ Configuration (mago.toml)

```toml
.
```

### 📜 Command Output

```shell
error[invalid-method-access]: Cannot access private method `TraitWithPrivateMethod::privateInTraitLevel2`.
   ┌─ test.php:24:15
   │  
 8 │ ╭     private static function privateInTraitLevel2(): void {
 9 │ │         echo __METHOD__, PHP_EOL;
10 │ │     }
   │ ╰─────' Member is defined as `private` here
   ·  
24 │           self::privateInTraitLevel2();
   │           ------^^^^^^^^^^^^^^^^^^^^--
   │           │     │
   │           │     This member is private and cannot be accessed here
   │           Invalid access occurs here, from within `SomeClass`
   │  
   = Help: Change the visibility of method `privateInTraitLevel2` to `public`, or call it from an allowed scope.

error: found 1 issues: 1 error(s)
```

### 📂 PHP Code Sample (If Applicable)

```php
<?php

trait TraitWithPrivateMethod {
    private static function privateInTraitLevel2(): void {
        echo __METHOD__, PHP_EOL;
    }
}
trait TraitWithTrait {
    use TraitWithPrivateMethod;
    private static function privateInTraitLevel1(): void {
        echo __METHOD__, PHP_EOL;
    }
}

final class SomeClass {
    use TraitWithTrait;

    public function __construct()
    {
        self::privateInTraitLevel2();
        self::privateInTraitLevel1();
    }
}

$nonEmptyList = new SomeClass();
```

### 🖥️ Operating System

Linux

### 📦 How did you install Mago?

Composer (`composer require carthage-software/mago`)

### 📝 Additional Context

_No response_


Discussion:

Patch:
diff --git a/crates/codex/src/populator.rs b/crates/codex/src/populator.rs
index f75426cb..40f12c9b 100644
--- a/crates/codex/src/populator.rs
+++ b/crates/codex/src/populator.rs
@@ -708,6 +708,9 @@ fn populate_metadata_from_trait(
     // Also inherit invalid dependencies from the trait
     metadata.invalid_dependencies.extend(trait_metadata.invalid_dependencies.iter().copied());
 
+    // Inherit nested trait usages from the trait
+    metadata.add_used_traits(trait_metadata.used_traits.iter().copied());
+
     // Extend template parameters based on the trait's templates
     extend_template_parameters(metadata, trait_metadata);
 


Test patch:
diff --git a/crates/analyzer/tests/cases/issue_474.php b/crates/analyzer/tests/cases/issue_474.php
new file mode 100644
index 00000000..9235856f
--- /dev/null
+++ b/crates/analyzer/tests/cases/issue_474.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+trait TraitWithPrivateMethod {
+    private static function privateInTraitLevel2(): void {
+        echo __METHOD__, PHP_EOL;
+    }
+}
+trait TraitWithTrait {
+    use TraitWithPrivateMethod;
+    private static function privateInTraitLevel1(): void {
+        echo __METHOD__, PHP_EOL;
+    }
+}
+
+final class SomeClass {
+    use TraitWithTrait;
+
+    public function __construct()
+    {
+        self::privateInTraitLevel2();
+        self::privateInTraitLevel1();
+    }
+}
+
+$nonEmptyList = new SomeClass();
diff --git a/crates/analyzer/tests/mod.rs b/crates/analyzer/tests/mod.rs
index 3fd2cc3f..71760da5 100644
--- a/crates/analyzer/tests/mod.rs
+++ b/crates/analyzer/tests/mod.rs
@@ -232,6 +232,7 @@ test_case!(issue_520);
 test_case!(issue_521);
 test_case!(issue_523);
 test_case!(issue_528);
+test_case!(issue_474);
 
 #[test]
 fn test_all_test_cases_are_ran() {