Feature Request: Add `@global_scope` and `@using_scope` annotations for organizing global variable visibility
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- rust
- Ambito
- developer-experience
Direzione di ricerca
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.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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.
- Lingua principale
- Rust
- Stelle
- 527
- Fork
- 110
- Merge medio
- 2g 6h
- PR unite (30g)
- 32
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di EmmyLuaLs/emmylua-analyzer-rust
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
EmmyLuaLs/emmylua-analyzer-rust#1363 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
EmmyLuaLs/emmylua-analyzer-rust#1276 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
EmmyLuaLs/emmylua-analyzer-rust#1249 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
EmmyLuaLs/emmylua-analyzer-rust#1247 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
EmmyLuaLs/emmylua-analyzer-rust#1016 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di EmmyLuaLs/emmylua-analyzer-rust
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
RustCrypto/utils#1597 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
Non-`main` default branchesAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
max-sixty/worktrunk#4727 · 7 commenti · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
I maintainer di solito rispondono entro 1 giorno
-
follow-up
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
carrick-sh/carrick#1859 ·
I maintainer di solito rispondono entro 1 giorno