nixd
Loading...
Searching...
No Matches
VariableLookup.cpp
Go to the documentation of this file.
7
8#include <ranges>
9#include <set>
10
11using namespace nixf;
12
13namespace {
14
15std::set<std::string> Constants{
16 "true", "false", "null",
17 "__currentTime", "__currentSystem", "__nixVersion",
18 "__storeDir", "__langVersion", "__importNative",
19 "__traceVerbose", "__nixPath", "derivation",
20};
21
22/// Builder a map of definitions. If there are something overlapped, maybe issue
23/// a diagnostic.
24class DefBuilder {
26 std::vector<Diagnostic> &Diags;
27
28 std::shared_ptr<Definition> addSimple(std::string Name, const Node *Entry,
30 assert(!Def.contains(Name));
31 auto NewDef = std::make_shared<Definition>(Entry, Source);
32 Def.insert({std::move(Name), NewDef});
33 return NewDef;
34 }
35
36public:
37 DefBuilder(std::vector<Diagnostic> &Diags) : Diags(Diags) {}
38
39 void addBuiltin(std::string Name) {
40 // Don't need to record def map for builtins.
41 auto _ = addSimple(std::move(Name), nullptr, Definition::DS_Builtin);
42 }
43
44 [[nodiscard("Record ToDef Map!")]] std::shared_ptr<Definition>
45 add(std::string Name, const Node *Entry, Definition::DefinitionSource Source,
46 bool IsInheritFromBuiltin) {
47 auto PrimOpLookup = lookupGlobalPrimOpInfo(Name);
48 if (PrimOpLookup == PrimopLookupResult::Found && !IsInheritFromBuiltin) {
49 // Overriding a builtin primop is discouraged.
50 Diagnostic &D =
51 Diags.emplace_back(Diagnostic::DK_PrimOpOverridden, Entry->range());
52 D << Name;
53 }
54
55 // Lookup constants
56 if (Constants.contains(Name)) {
57 Diagnostic &D =
58 Diags.emplace_back(Diagnostic::DK_ConstantOverridden, Entry->range());
59 D << Name;
60 }
61
62 return addSimple(std::move(Name), Entry, Source);
63 }
64
65 EnvNode::DefMap finish() { return std::move(Def); }
66};
67
68/// Special check for inherited from builtins attribute.
69/// e.g. inherit (builtins) foo bar;
70/// Returns true if the attribute is inherited from builtins.
71///
72/// Suppress warnings for overriding primops in this case.
73bool checkInheritedFromBuiltin(const Attribute &Attr) {
75 return false;
76
77 assert(Attr.value() &&
78 "select expr desugared from inherit should not be null");
79 assert(Attr.value()->kind() == Node::NK_ExprSelect &&
80 "desugared inherited from should be a select expr");
81 const auto &Select = static_cast<const ExprSelect &>(*Attr.value());
82 if (Select.expr().kind() == Node::NK_ExprVar) {
83 const auto &Var = static_cast<const ExprVar &>(Select.expr());
84 return Var.id().name() == "builtins";
85 }
86 return false;
87}
88
89bool isBuiltinConstant(const std::string &Name) {
90 if (Name.starts_with("_"))
91 return false;
92 return Constants.contains(Name) || Constants.contains("__" + Name);
93}
94
95} // namespace
96
97bool EnvNode::isLive() const {
98 for (const auto &[_, D] : Defs) {
99 if (!D->uses().empty())
100 return true;
101 }
102 return false;
103}
104
105void VariableLookupAnalysis::emitEnvLivenessWarning(
106 const std::shared_ptr<EnvNode> &NewEnv) {
107 for (const auto &[Name, Def] : NewEnv->defs()) {
108 // If the definition comes from lambda arg, omit the diagnostic
109 // because there is no elegant way to "fix" this trivially & keep
110 // the lambda signature.
111 if (Def->source() == Definition::DS_LambdaArg)
112 continue;
113 // call-flake always passes these
114 if (Def->source() == Definition::DS_FlakeInjectedFormal)
115 continue;
116 // Ignore builtins usage.
117 if (!Def->syntax())
118 continue;
119 if (Def->uses().empty()) {
120 Diagnostic::DiagnosticKind Kind = [&]() {
121 switch (Def->source()) {
123 return Diagnostic::DK_UnusedDefLet;
125 return Diagnostic::DK_UnusedDefLambdaNoArg_Formal;
127 return Diagnostic::DK_UnusedDefLambdaWithArg_Formal;
129 return Diagnostic::DK_UnusedDefLambdaWithArg_Arg;
130 default:
131 assert(false && "liveness diagnostic encountered an unknown source!");
132 __builtin_unreachable();
133 }
134 }();
135 Diagnostic &D = Diags.emplace_back(Kind, Def->syntax()->range());
136 D << Name;
138
139 // Add fix for unused let bindings
140 if (Def->source() == Definition::DS_Let) {
141 const Node *LetNode = NewEnv->syntax();
142 if (LetNode && LetNode->kind() == Node::NK_ExprLet) {
143 const auto &Let = static_cast<const ExprLet &>(*LetNode);
144 if (Let.binds()) {
145 for (const auto &BindNode : Let.binds()->bindings()) {
146 // Skip non-Binding nodes (Inherit handled separately)
147 if (BindNode->kind() != Node::NK_Binding)
148 continue;
149
150 const auto &Bind = static_cast<const Binding &>(*BindNode);
151 const auto &PathNames = Bind.path().names();
152 if (PathNames.empty())
153 continue;
154
155 const auto &FirstName = PathNames[0];
156 if (!FirstName)
157 continue;
158
159 // Match by comparing first attrname range with Def->syntax()
160 // range
161 if (D.range().lCur() == FirstName->range().lCur() &&
162 D.range().rCur() == FirstName->range().rCur()) {
163 D.fix("remove unused binding")
164 .edit(TextEdit::mkRemoval(Bind.range()));
165 break;
166 }
167 }
168 }
169 }
170 }
171 }
172 }
173}
174
175void VariableLookupAnalysis::lookupVar(const ExprVar &Var,
176 const std::shared_ptr<EnvNode> &Env) {
177 const auto &Name = Var.id().name();
178 const auto *CurEnv = Env.get();
179 std::shared_ptr<Definition> Def;
180 std::vector<const EnvNode *> WithEnvs;
181 for (; CurEnv; CurEnv = CurEnv->parent()) {
182 if (CurEnv->defs().contains(Name)) {
183 Def = CurEnv->defs().at(Name);
184 break;
185 }
186 // Find all nested "with" expression, variables potentially come from those.
187 // For example
188 // with lib;
189 // with builtins;
190 // generators <--- this variable may come from "lib" | "builtins"
191 //
192 // We cannot determine where it precisely come from, thus mark all Envs
193 // alive.
194 if (CurEnv->isWith()) {
195 WithEnvs.emplace_back(CurEnv);
196 }
197 }
198
199 if (Def) {
200 Def->usedBy(Var);
201 Results.insert({&Var, LookupResult{LookupResultKind::Defined, Def}});
202 } else if (!WithEnvs.empty()) { // comes from enclosed "with" expressions.
203 // Collect all `with` expressions that could provide this variable's
204 // binding. This is stored for later queries (e.g., by code actions that
205 // need to determine if converting a `with` to `let/inherit` is safe).
206 std::vector<const ExprWith *> WithScopes;
207 for (const auto *WithEnv : WithEnvs) {
208 Def = WithDefs.at(WithEnv->syntax());
209 Def->usedBy(Var);
210 WithScopes.push_back(static_cast<const ExprWith *>(WithEnv->syntax()));
211 }
212 VarWithScopes.insert({&Var, std::move(WithScopes)});
213 Results.insert({&Var, LookupResult{LookupResultKind::FromWith, Def}});
214 } else {
215 // Check if this is a primop.
216 switch (lookupGlobalPrimOpInfo(Name)) {
218 assert(false && "primop name should be defined");
219 break;
221 Diagnostic &D =
222 Diags.emplace_back(Diagnostic::DK_PrimOpNeedsPrefix, Var.range());
223 D.fix("use `builtins.` prefix")
224 .edit(TextEdit::mkInsertion(Var.range().lCur(), "builtins."));
225 Results.insert(
226 {&Var, LookupResult{LookupResultKind::Undefined, nullptr}});
227 break;
228 }
230 // Otherwise, this variable is undefined.
231 Results.insert(
232 {&Var, LookupResult{LookupResultKind::Undefined, nullptr}});
233 Diagnostic &Diag =
234 Diags.emplace_back(Diagnostic::DK_UndefinedVariable, Var.range());
235 Diag << Var.id().name();
236 break;
237 }
238 }
239}
240
241void VariableLookupAnalysis::dfs(const ExprLambda &Lambda,
242 const std::shared_ptr<EnvNode> &Env) {
243 // Early exit for in-complete lambda.
244 if (!Lambda.body())
245 return;
246
247 // Create a new EnvNode, as lambdas may have formal & arg.
248 DefBuilder DBuilder(Diags);
249 assert(Lambda.arg());
250 const LambdaArg &Arg = *Lambda.arg();
251
252 // foo: body
253 // ^~~<------- add function argument.
254 if (Arg.id()) {
255 if (!Arg.formals()) {
256 ToDef.insert_or_assign(Arg.id(),
257 DBuilder.add(Arg.id()->name(), Arg.id(),
259 /*IsInheritFromBuiltin=*/false));
260 // Function arg cannot duplicate to it's formal.
261 // If it this unluckily happens, we would like to skip this definition.
262 } else if (!Arg.formals()->dedup().contains(Arg.id()->name())) {
263 ToDef.insert_or_assign(Arg.id(),
264 DBuilder.add(Arg.id()->name(), Arg.id(),
266 /*IsInheritFromBuiltin=*/false));
267 }
268 }
269
270 // { foo, bar, ... } : body
271 // ^~~~~~~~~<-------------- add function formals.
272
273 // This section differentiates between formal parameters with an argument and
274 // without. Example:
275 //
276 // { foo }@arg : use arg
277 //
278 // In this case, the definition of `foo` is not used directly; however, it
279 // might be accessed via arg.foo. Therefore, the severity of an unused formal
280 // parameter is reduced in this scenario.
281 if (Arg.formals()) {
282 for (const auto &[Name, Formal] : Arg.formals()->dedup()) {
284 if (&Lambda == OutputsLambda && FlakeInjected.contains(Name))
286 else
289 ToDef.insert_or_assign(Formal->id(),
290 DBuilder.add(Name, Formal->id(), Source,
291 /*IsInheritFromBuiltin=*/false));
292 }
293 }
294
295 auto NewEnv = std::make_shared<EnvNode>(Env, DBuilder.finish(), &Lambda);
296
297 if (Arg.formals()) {
298 for (const auto &Formal : Arg.formals()->members()) {
299 if (const Expr *Def = Formal->defaultExpr()) {
300 dfs(*Def, NewEnv);
301 }
302 }
303 }
304
305 dfs(*Lambda.body(), NewEnv);
306
307 emitEnvLivenessWarning(NewEnv);
308}
309
310void VariableLookupAnalysis::dfsDynamicAttrs(
311 const std::vector<Attribute> &DynamicAttrs,
312 const std::shared_ptr<EnvNode> &Env) {
313 for (const auto &Attr : DynamicAttrs) {
314 if (!Attr.value())
315 continue;
316 dfs(Attr.key(), Env);
317 dfs(*Attr.value(), Env);
318 }
319}
320
321std::shared_ptr<EnvNode> VariableLookupAnalysis::dfsAttrs(
322 const SemaAttrs &SA, const std::shared_ptr<EnvNode> &Env,
323 const Node *Syntax, Definition::DefinitionSource Source) {
324 if (SA.isRecursive()) {
325 // rec { }, or let ... in ...
326 DefBuilder DB(Diags);
327 // For each static names, create a name binding.
328 for (const auto &[Name, Attr] : SA.staticAttrs()) {
329 ToDef.insert_or_assign(
330 &Attr.key(),
331 DB.add(Name, &Attr.key(), Source, checkInheritedFromBuiltin(Attr)));
332 }
333
334 auto NewEnv = std::make_shared<EnvNode>(Env, DB.finish(), Syntax);
335
336 for (const auto &[_, Attr] : SA.staticAttrs()) {
337 if (!Attr.value())
338 continue;
341 dfs(*Attr.value(), NewEnv);
342 } else {
343 assert(Attr.kind() == Attribute::AttributeKind::Inherit);
344 dfs(*Attr.value(), Env);
345 }
346 }
347
348 dfsDynamicAttrs(SA.dynamicAttrs(), NewEnv);
349 return NewEnv;
350 }
351
352 // Non-recursive. Dispatch nested node with old Env
353 for (const auto &[_, Attr] : SA.staticAttrs()) {
354 if (!Attr.value())
355 continue;
356 dfs(*Attr.value(), Env);
357 }
358
359 dfsDynamicAttrs(SA.dynamicAttrs(), Env);
360 return Env;
361};
362
363void VariableLookupAnalysis::dfs(const ExprAttrs &Attrs,
364 const std::shared_ptr<EnvNode> &Env) {
365 const SemaAttrs &SA = Attrs.sema();
366 std::shared_ptr<EnvNode> NewEnv =
367 dfsAttrs(SA, Env, &Attrs, Definition::DS_Rec);
368 if (NewEnv != Env) {
369 assert(Attrs.isRecursive() &&
370 "NewEnv must be created for recursive attrset");
371 if (!NewEnv->isLive()) {
372 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_ExtraRecursive,
373 Attrs.rec()->range());
374 D.fix("remove `rec` keyword")
375 .edit(TextEdit::mkRemoval(Attrs.rec()->range()));
377 }
378 }
379}
380
381void VariableLookupAnalysis::dfs(const ExprLet &Let,
382 const std::shared_ptr<EnvNode> &Env) {
383
384 // Obtain the env object suitable for "in" expression.
385 auto GetLetEnv = [&Env, &Let, this]() -> std::shared_ptr<EnvNode> {
386 // This is an empty let ... in ... expr, definitely anti-pattern in
387 // nix language. Create a trivial env and return.
388 if (!Let.attrs()) {
389 auto NewEnv = std::make_shared<EnvNode>(Env, EnvNode::DefMap{}, &Let);
390 return NewEnv;
391 }
392
393 // If there are some attributes actually, create a new env.
394 const SemaAttrs &SA = Let.attrs()->sema();
395 assert(SA.isRecursive() && "let ... in ... attrset must be recursive");
396 checkLetInheritBuiltins(SA);
397 return dfsAttrs(SA, Env, &Let, Definition::DS_Let);
398 };
399
400 auto LetEnv = GetLetEnv();
401
402 if (Let.expr())
403 dfs(*Let.expr(), LetEnv);
404 emitEnvLivenessWarning(LetEnv);
405}
406
407void VariableLookupAnalysis::dfs(const ExprLegacyLet &LegacyLet,
408 const std::shared_ptr<EnvNode> &Env) {
409 if (LegacyLet.attrs())
410 dfs(*LegacyLet.attrs(), Env);
411}
412
413void VariableLookupAnalysis::trivialDispatch(
414 const Node &Root, const std::shared_ptr<EnvNode> &Env) {
415 for (const Node *Ch : Root.children()) {
416 if (!Ch)
417 continue;
418 dfs(*Ch, Env);
419 }
420}
421
422void VariableLookupAnalysis::dfs(const ExprWith &With,
423 const std::shared_ptr<EnvNode> &Env) {
424 auto NewEnv = std::make_shared<EnvNode>(Env, EnvNode::DefMap{}, &With);
425 if (!WithDefs.contains(&With)) {
426 auto NewDef =
427 std::make_shared<Definition>(&With.kwWith(), Definition::DS_With);
428 ToDef.insert_or_assign(&With.kwWith(), NewDef);
429 WithDefs.insert_or_assign(&With, NewDef);
430 }
431
432 if (With.with())
433 dfs(*With.with(), Env);
434
435 if (With.expr())
436 dfs(*With.expr(), NewEnv);
437
438 if (WithDefs.at(&With)->uses().empty()) {
439 Diagnostic &D =
440 Diags.emplace_back(Diagnostic::DK_ExtraWith, With.kwWith().range());
441 Fix &F = D.fix("remove `with` expression")
443 if (With.tokSemi())
445 if (With.with())
446 F.edit(TextEdit::mkRemoval(With.with()->range()));
447 }
448}
449
450void VariableLookupAnalysis::checkBuiltins(const ExprSelect &Sel) {
451 if (!Sel.path())
452 return;
453
454 // Don't emit diagnostics for select expressions desugared from inherit.
455 if (Sel.desugaredFrom())
456 return;
457
458 if (Sel.expr().kind() != Node::NK_ExprVar)
459 return;
460
461 const auto &Builtins = static_cast<const ExprVar &>(Sel.expr());
462 if (Builtins.id().name() != "builtins")
463 return;
464
465 const auto &AP = *Sel.path();
466
467 if (AP.names().size() != 1)
468 return;
469
470 AttrName &First = *AP.names()[0];
471 if (!First.isStatic())
472 return;
473
474 const auto &Name = First.staticName();
475
476 switch (lookupGlobalPrimOpInfo(Name)) {
478 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_PrimOpRemovablePrefix,
479 Builtins.range());
480 Fix &F =
481 D.fix("remove `builtins.` prefix")
482 .edit(TextEdit::mkRemoval(Builtins.range())); // remove `builtins`
483
484 if (Sel.dot()) {
485 // remove the dot also.
486 F.edit(TextEdit::mkRemoval(Sel.dot()->range()));
487 }
488 return;
489 }
491 return;
493 if (!isBuiltinConstant(Name)) {
494 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_PrimOpUnknown,
495 AP.names()[0]->range());
496 D << Name;
497 return;
498 }
499 }
500}
501
502void VariableLookupAnalysis::checkLetInheritBuiltins(const SemaAttrs &SA) {
503 for (const auto &[Name, Attr] : SA.staticAttrs()) {
504 if (!checkInheritedFromBuiltin(Attr))
505 continue;
506
507 // Check if the inherited name is a prelude builtin
509 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_PrimOpRemovablePrefix,
510 Attr.key().range());
511 D.fix("remove unnecessary inherit")
512 .edit(TextEdit::mkRemoval(Attr.key().range()));
513 }
514 }
515}
516
517void VariableLookupAnalysis::dfs(const Node &Root,
518 const std::shared_ptr<EnvNode> &Env) {
519 Envs.insert({&Root, Env});
520 switch (Root.kind()) {
521 case Node::NK_ExprVar: {
522 const auto &Var = static_cast<const ExprVar &>(Root);
523 lookupVar(Var, Env);
524 break;
525 }
526 case Node::NK_ExprLambda: {
527 const auto &Lambda = static_cast<const ExprLambda &>(Root);
528 dfs(Lambda, Env);
529 break;
530 }
531 case Node::NK_ExprAttrs: {
532 const auto &Attrs = static_cast<const ExprAttrs &>(Root);
533 dfs(Attrs, Env);
534 break;
535 }
536 case Node::NK_ExprLet: {
537 const auto &Let = static_cast<const ExprLet &>(Root);
538 dfs(Let, Env);
539 break;
540 }
541 case Node::NK_ExprLegacyLet: {
542 const auto &LegacyLet = static_cast<const ExprLegacyLet &>(Root);
543 dfs(LegacyLet, Env);
544 break;
545 }
546 case Node::NK_ExprWith: {
547 const auto &With = static_cast<const ExprWith &>(Root);
548 dfs(With, Env);
549 break;
550 }
551 case Node::NK_ExprSelect: {
552 trivialDispatch(Root, Env);
553 const auto &Sel = static_cast<const ExprSelect &>(Root);
554 checkBuiltins(Sel);
555 break;
556 }
557 default:
558 trivialDispatch(Root, Env);
559 }
560}
561
562void VariableLookupAnalysis::runOnAST(const Node &Root, bool IsFlake) {
563 // Create a basic env
564 DefBuilder DB(Diags);
565
566 for (const auto &[Name, Info] : PrimOpsInfo) {
567 if (!Info.Internal) {
568 // Only add non-internal primops without "__" prefix.
569 DB.addBuiltin(Name);
570 }
571 }
572
573 for (const auto &Builtin : Constants)
574 DB.addBuiltin(Builtin);
575
576 DB.addBuiltin("builtins");
577 // This is an undocumented keyword actually.
578 DB.addBuiltin(std::string("__curPos"));
579
580 auto Env = std::make_shared<EnvNode>(nullptr, DB.finish(), nullptr);
581
582 if (IsFlake)
583 collectFlakeInjected(Root);
584
585 dfs(Root, Env);
586}
587
589 : Diags(Diags) {}
590
592 if (!Envs.contains(N))
593 return nullptr;
594 return Envs.at(N).get();
595}
596
597/// \brief Identify formals that the Nix flake evaluator unconditionally injects
598/// into the `outputs` function.
599void VariableLookupAnalysis::collectFlakeInjected(const Node &Root) {
600 if (Root.kind() != Node::NK_ExprAttrs)
601 return;
602 const auto &S = static_cast<const ExprAttrs &>(Root).sema().staticAttrs();
603
604 FlakeInjected.insert("self");
605 if (auto It = S.find("inputs"); It != S.end())
606 if (const auto *V = It->second.value();
607 V && V->kind() == Node::NK_ExprAttrs) {
608 const auto &Keys =
609 static_cast<const ExprAttrs &>(*V).sema().staticAttrs();
610 std::ranges::copy(std::views::keys(Keys),
611 std::inserter(FlakeInjected, FlakeInjected.end()));
612 }
613
614 auto It = S.find("outputs");
615 if (It == S.end())
616 return;
617 const auto *V = It->second.value();
618 if (!V || V->kind() != Node::NK_ExprLambda)
619 return;
620 const auto &L = static_cast<const ExprLambda &>(*V);
621 if (L.arg() && L.arg()->formals())
622 OutputsLambda = &L;
623}
Lookup variable names, from it's parent scope.
const std::string & staticName() const
Definition Attrs.h:50
bool isStatic() const
Definition Attrs.h:40
const std::vector< std::shared_ptr< AttrName > > & names() const
Definition Attrs.h:100
Node & key() const
Definition Attrs.h:217
Expr * value() const
Definition Attrs.h:219
AttributeKind kind() const
Definition Attrs.h:221
@ InheritFrom
inherit (expr) a b c
Definition Attrs.h:202
@ Inherit
inherit a b c;
Definition Attrs.h:200
DefinitionSource
"Source" information so we can know where the def comes from.
@ DS_FlakeInjectedFormal
Formal in flake outputs injected by call-flake.nix.
@ DS_Rec
From recursive attribute set. e.g. rec { }.
@ DS_LambdaArg
From ambda arg e.g. a: a + 1.
@ DS_LambdaNoArg_Formal
From lambda (noarg) formal, e.g. { a }: a + 1.
@ DS_Builtin
Builtin names.
@ DS_LambdaWithArg_Arg
From lambda (with @arg) arg, e.g. a in { foo }@a: foo + 1.
@ DS_With
From with <expr>;.
@ DS_LambdaWithArg_Formal
From lambda (with @arg) formal, e.g. foo in { foo }@a: foo + 1.
@ DS_Let
From let ... in ...
Fix & fix(std::string Message)
Definition Diagnostic.h:203
A set of variable definitions, which may inherit parent environment.
bool isLive() const
std::map< std::string, std::shared_ptr< Definition > > DefMap
bool isRecursive() const
Definition Attrs.h:287
const SemaAttrs & sema() const
Definition Attrs.h:289
const Misc * rec() const
Definition Attrs.h:285
Expr * body() const
Definition Lambda.h:119
LambdaArg * arg() const
Definition Lambda.h:118
const ExprAttrs * attrs() const
Definition Expr.h:178
const ExprAttrs * attrs() const
Definition Expr.h:155
const Expr * expr() const
Definition Expr.h:156
const Expr * desugaredFrom() const
Definition Expr.h:29
Expr & expr() const
Definition Expr.h:24
AttrPath * path() const
Definition Expr.h:35
Dot * dot() const
Definition Expr.h:31
const Identifier & id() const
Definition Simple.h:200
const Misc & kwWith() const
Definition Expr.h:198
Expr * with() const
Definition Expr.h:200
const Misc * tokSemi() const
Definition Expr.h:199
Expr * expr() const
Definition Expr.h:201
Fix & edit(TextEdit Edit)
Definition Diagnostic.h:65
const FormalVector & members() const
Definition Lambda.h:71
const std::map< std::string, const Formal * > & dedup()
Deduplicated formals.
Definition Lambda.h:74
const std::string & name() const
Definition Basic.h:120
Formals * formals() const
Definition Lambda.h:101
Identifier * id() const
Definition Lambda.h:99
LexerCursor lCur() const
Definition Range.h:116
LexerCursor rCur() const
Definition Range.h:117
NodeKind kind() const
Definition Basic.h:34
LexerCursorRange range() const
Definition Basic.h:35
virtual ChildVector children() const =0
void tag(DiagnosticTag Tag)
Definition Diagnostic.h:96
LexerCursorRange range() const
Definition Diagnostic.h:100
Attribute set after deduplication.
Definition Attrs.h:236
bool isRecursive() const
If the attribute set is rec.
Definition Attrs.h:269
const std::vector< Attribute > & dynamicAttrs() const
Dynamic attributes, require evaluation to get the key.
Definition Attrs.h:264
const std::map< std::string, Attribute > & staticAttrs() const
Static attributes, do not require evaluation to get the key.
Definition Attrs.h:257
static TextEdit mkRemoval(LexerCursorRange RemovingRange)
Definition Diagnostic.h:39
static TextEdit mkInsertion(LexerCursor P, std::string NewText)
Definition Diagnostic.h:35
const EnvNode * env(const Node *N) const
void runOnAST(const Node &Root, bool IsFlake=false)
Perform variable lookup analysis (def-use) on AST.
VariableLookupAnalysis(std::vector< Diagnostic > &Diags)
PrimopLookupResult lookupGlobalPrimOpInfo(const std::string &Name)
Look up information about a global primop by name.
std::map< std::string, nixf::PrimOpInfo > PrimOpsInfo
@ PrefixedFound
The primop was found, but needs "builtin." prefix.
Definition PrimOpInfo.h:44
@ NotFound
The primop was not found.
Definition PrimOpInfo.h:46
@ Found
The primop was found with an exact match.
Definition PrimOpInfo.h:42