materializeFile

Walk file, materialize every metamodel-entity declaration we can construct, and return the resulting list. Side effect: each constructed entity is also registered in ModelScope.current (via MetamodelEntity's init block).

The materializer keeps a per-call cache keyed by KtProperty, so a property referenced from multiple constructor args is materialized exactly once and the same instance is shared. A separate in-flight set detects cycles during recursive reference resolution — if a property's materialization depends transitively on itself, the recursive call sees it in the in-flight set and returns null rather than infinite-looping. Deferred relation lists are back-patched once the cycle members exist.

Caller is responsible for setting up the scope:

val scope = ModelScope().apply { skipDeclaringClassCapture = true }
ModelScope.with(scope) {
val entities = PsiMaterializer().materializeFile(file)
// … run rules against scope …
}