Feature Request: Add `@global_scope` and `@using_scope` annotations for organizing global variable visibility
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 35/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- rust
- Área
- developer-experience
Línea de trabajo
Start by reading the analyzer's annotation parsing, workspace indexing, global name resolution, diagnostics, and completion entry points; the issue does not name specific files or tests. Trace how existing @namespace support is represented and consumed, then map the requested declaration, import, conflict, and _G behavior onto those paths. Done means the new annotations affect static visibility and type resolution, report duplicate, unknown, and ambiguous scopes, and leave Lua runtime behavior unchanged.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Propose two new annotations for EmmyLua that introduce a static-analysis-level organization layer for global variables, parallel to how @namespace organizes types:
---@global_scope <name>— declares that subsequent global variable assignments in the current file belong to the named scope.---@using_scope <name>— declares that the current file may resolve global symbols provided by the named scope.
These annotations do not affect Lua runtime behavior, do not modify _G, and are purely editor/analyzer hints for symbol visibility, completion filtering, and type resolution.
Motivation
Large Lua projects (game mods, embedded scripting environments, framework-driven UIs) frequently rely on implicit global conventions:
- A bootstrap file loads modules into
_G(e.g.,mp = require 'mp'). - A framework exposes a DSL as globals (
import,bind,Widget,Button, ...). - Downstream files assume these globals exist without any explicit declaration.
Today, EmmyLua has no way to express which globals a given file is expected to see. The consequences:
- Undefined-global diagnostics are either noisy (every DSL symbol flagged) or disabled entirely (losing all protection).
- Completion lists are polluted by every global declared anywhere in the workspace.
- There is no way to trace which file/meta provided a given global.
- Conflicting global definitions across unrelated frameworks cannot be disambiguated.
@namespace solves this for types but not for values. This proposal fills that gap.
Proposed Syntax
Declaration side
---@global_scope mp_global
mp = require 'mp'
---@global_scope ui_framework
import = function(modname) end
mod = function(modname) end
bind = function(slot, widget) end
Widget = {}
ElevatedButton = {}
Consumption side
---@using_scope mp_global
mp.get_script_name() -- resolved via mp_global
---@using_scope ui_framework
import('mp')
import('player')
bind('app.controls.right', Builder{
builder: function (context)
return ElevatedButton{
color = Theme.of(context).data.surface_color,
on_click = function () logger.info('Button click!') end,
child = Text('ElevatedButton')
}
end
})
Naming rationale
@using_scopeis introduced as a new annotation rather than reusing@using, to avoid ambiguity with@namespaceusage. Mixing a type-introduction directive and a value-scope-introduction directive under the same keyword would force the parser to disambiguate by context, which is unnecessary complexity.- An optional
@usingalias for@using_scopemay be considered only if it can be resolved unambiguously (e.g., permitted exclusively at file header level and only when no type namespace is in scope). This is a nice-to-have, not a requirement.
Semantics
1. Symbol attribution — full-file, global assignments only
@global_scope <name>applies to the entire file it appears in, regardless of position within the file.- Only global variable assignments (assignments without a preceding
localkeyword) that appear in the file are attributed to the scope. - Local variables are unaffected.
- A file may declare at most one
@global_scope. Multiple declarations should be reported as an error.
2. Injection — static only, never runtime
@using_scope <name>tells the analyzer: when resolving an unresolved global reference in this file, include the symbols attributed to<name>in the candidate set.- It does not add anything to
_G, does not change runtime behavior, and does not emit any code. - It is equivalent in spirit to "open this scope for name resolution in this file."
3. Relationship with _G
@global_scopeis a symbol-organization concept;_Gis a runtime-access concept. They are orthogonal.- However, for explicit
_G.fooaccesses, the analyzer should still consult scope information to resolvefoo's type whenfoois provided by a@using_scoped scope. This does not "interfere with_G"; it merely gives the analyzer more accurate source information for the same field. - The rule can be summarized as:
_G's runtime semantics are owned by_G; type/visibility information for its fields is owned by scopes.
4. Conflict resolution
Conflicts should follow the same intuition as ordinary global-variable shadowing, with one important addition:
- File-local definition shadows imported scope symbol. If a file has both
@using_scope Aand its ownfoo = ..., the file-localfoowins; the importedfoois shadowed. - Multiple imported scopes with the same symbol produce a diagnostic. If a file has
@using_scope Aand@using_scope Band both definefoo, the analyzer should emit an ambiguity diagnostic (e.g.,global 'foo' is provided by multiple scopes: A, B; qualify it or remove one @using_scope) rather than silently picking one. Silently picking a winner makes the scope abstraction leaky and is almost always a user mistake.
Relationship with @namespace
The two annotation families are intentionally symmetric:
| Concept | Type-level | Value-level |
|---|---|---|
| Declare a grouping | @namespace |
@global_scope |
| Open a grouping in a file | (implicit via class/alias resolution) | @using_scope |
@global_scope does not replace type information. Symbols inside a scope can (and should) still carry ---@type, ---@class, ---@field, etc. @global_scope only records ownership/visibility, not type.
Alternatives Considered
@meta+ direct global declarations — Provides types but no visibility scoping. All meta-declared globals are visible workspace-wide.@namespace— Designed for types (classes, aliases), not for value-level global visibility.---@class _G+---@field— Merges everything into one_Gview; no isolation between scopes.- Disabling
undefined-globaldiagnostics via.emmyrc.json— Hides the symptom, provides no organization. - Reusing
@using— Rejected due to ambiguity with type-namespace semantics (see Naming rationale).
Implementation Notes / Open Questions
- Scope resolution must be able to locate a scope declared in another file (typically a
@metafile). The proposal does not mandate a file-naming convention; any.luafile containing@global_scope <name>should register that scope in the workspace index. - Diagnostics to add:
- Duplicate
@global_scopein the same file. @using_scopereferencing an unknown scope.- Ambiguous symbol from multiple
@using_scoped scopes.
- Duplicate
- Completion filtering should prioritize symbols from
@using_scoped scopes and file-local globals; other workspace globals may still be offered but de-prioritized (configurable). - Should
@global_scopedeclarations themselves be permitted only in meta files in a future strict mode? Left as an open question. - Interaction with
---@meta: a meta file that declares@global_scopeshould be treated exactly like a regular file for attribution purposes.
Impact
- No runtime impact. Pure analyzer feature.
- No breaking changes. Files without the new annotations behave exactly as today.
- Significant quality-of-life improvement for projects that rely on framework-injected global DSLs and module-loading bootstraps.
- Lenguaje dominante
- Rust
- Estrellas
- 530
- Forks
- 111
- Merge medio
- 2 d 6 h
- PR fusionados (30 d)
- 32
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de EmmyLuaLs/emmylua-analyzer-rust
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
EmmyLuaLs/emmylua-analyzer-rust#1363 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
EmmyLuaLs/emmylua-analyzer-rust#1276 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
EmmyLuaLs/emmylua-analyzer-rust#1249 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
EmmyLuaLs/emmylua-analyzer-rust#1247 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
EmmyLuaLs/emmylua-analyzer-rust#1016 ·
Los mantenedores suelen responder en 1 día
Todos los issues de EmmyLuaLs/emmylua-analyzer-rust
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
madesroches/fontaine#513 ·
Los mantenedores suelen responder en 1 día
-
area:ui needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
bherbruck/solvecraft#13497 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
needs-triage tooling
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
neilsh/stunt-reel#317 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
storytold/craft-fonts#65 ·
Los mantenedores suelen responder en 2 días