Skip to content

Commit

Permalink
nixd/Sema: lowering for inherited attrs in binds
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Sep 19, 2023
1 parent 0287144 commit e814ed2
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixd/include/nixd/Sema/Lowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct Lowering {
nix::Expr *lower(syntax::Node *Root);
nix::ExprLambda *lowerFunction(syntax::Function *Fn);
nix::Formal lowerFormal(const syntax::Formal &Formal);
nix::AttrPath lowerAttrPath(const syntax::AttrPath &Path);
nix::ExprAttrs *lowerBinds(const syntax::Binds &Binds);
};

} // namespace nixd
122 changes: 122 additions & 0 deletions nixd/lib/Sema/Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#include "nixd/Syntax/Diagnostic.h"
#include "nixd/Syntax/Nodes.h"

#include <nix/nixexpr.hh>

#include <llvm/Support/FormatVariadic.h>

namespace nixd {

nix::Formal Lowering::lowerFormal(const syntax::Formal &Formal) {
Expand Down Expand Up @@ -109,6 +113,113 @@ nix::ExprLambda *Lowering::lowerFunction(syntax::Function *Fn) {
return NewLambda;
}

static syntax::Diagnostic mkDiagDup(std::string Name, RangeIdx Range,
RangeIdx Prev) {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Warning;
Diag.Msg = llvm::formatv("duplicated attr `{0}`", Name);
Diag.Range = Range;

syntax::Note Note;
Note.Msg = llvm::formatv("previously defined here");
Note.Range = Prev;
Diag.Notes.emplace_back(std::move(Note));
return Diag;
}

nix::ExprAttrs *Lowering::lowerBinds(const syntax::Binds &Binds) {
// The official parser does this work during the lowering process, via
// @addAttr in Parser.y

using syntax::Node;

auto *Result = new nix::ExprAttrs;
Ctx.Pool.record(Result);

// Use a map to detect duplicated fields
// Not using the map inside nix::ExprAttrs because we want to preserve the
// range
std::map<nix::Symbol, Node *> Fields;

for (auto *Attr : Binds.Attributes) {
switch (Attr->getKind()) {
case Node::NK_Attribute: {
// TODO
break;
}
case Node::NK_InheritedAttribute: {
auto *IA = dynamic_cast<syntax::InheritedAttribute *>(Attr);
if (IA->InheritedAttrs->Names.empty()) {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Warning;
Diag.Msg = "empty inherit expression";
Diag.Range = Attr->Range;
Diags.emplace_back(std::move(Diag));
}

// inherit (expr) attr1 attr2
// lowering `(expr)` to nix expression
nix::Expr *Subject = nullptr;

if (IA->E)
Subject = lower(IA->E);

for (Node *Name : IA->InheritedAttrs->Names) {

// Extract nix::Symbol, report error if we encountered dynamic fields
nix::Symbol Sym;
switch (Name->getKind()) {
case Node::NK_Identifier: {
Sym = dynamic_cast<syntax::Identifier *>(Name)->Symbol;
break;
}
case Node::NK_String: {
Sym = STable.create(dynamic_cast<syntax::String *>(Name)->S);
break;
}
default: {
syntax::Diagnostic Diag;
Diag.Kind = syntax::Diagnostic::Error;
Diag.Msg = "dynamic attributes are not allowed in inherit";
Diag.Range = Name->Range;
Diags.emplace_back(std::move(Diag));
}
}

// Check if it is duplicated
if (Fields.contains(Sym)) {
std::string SymStr = STable[Sym];
auto Diag = mkDiagDup(SymStr, Name->Range, Fields[Sym]->Range);
Diags.emplace_back(std::move(Diag));
continue;
}

// Insert an AttrDef to the result

Fields.insert({Sym, Name});
nix::Expr *AttrBody;
if (Subject) {
// inherit (expr) attr1 attr2
AttrBody = new nix::ExprSelect(Name->Range.Begin, Subject, Sym);
} else {
// inherit attr1 attr2 ...
AttrBody = new nix::ExprVar(Name->Range.Begin, Sym);
}
Ctx.Pool.record(AttrBody);
nix::ExprAttrs::AttrDef Def(AttrBody, Name->Range.Begin, true);
Result->attrs.insert({Sym, Def});
}
break; // case Node::NK_InheritedAttribute
}
default:
llvm_unreachable(
"Attr in Binds.Attributes must be Attribute or InheritedAttribute!");
}
}

return Result;
}

nix::Expr *Lowering::lower(nixd::syntax::Node *Root) {
if (!Root)
return nullptr;
Expand Down Expand Up @@ -136,6 +247,17 @@ nix::Expr *Lowering::lower(nixd::syntax::Node *Root) {
Ctx.Pool.record(new nix::ExprWith(With->Range.Begin, Attrs, Body));
return NixWith;
}
case Node::NK_Binds: {
auto *Binds = dynamic_cast<syntax::Binds *>(Root);
return lowerBinds(*Binds);
}
case Node::NK_AttrSet: {
auto *AttrSet = dynamic_cast<syntax::AttrSet *>(Root);
assert(AttrSet->AttrBinds && "null AttrBinds of the AttrSet!");
nix::ExprAttrs *Binds = lowerBinds(*AttrSet->AttrBinds);
Binds->recursive = AttrSet->Recursive;
return Binds;
}
}

return nullptr;
Expand Down
9 changes: 9 additions & 0 deletions nixd/tools/nixd-lint/test/bind-inherit-dup.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# RUN: nixd-lint %s | FileCheck %s

{
inherit aaa bbb;

# CHECK: duplicated attr `aaa`
# CHECK: previously defined here
inherit "aaa";
}
6 changes: 6 additions & 0 deletions nixd/tools/nixd-lint/test/bind-inherit-dynamic.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# RUN: nixd-lint %s | FileCheck %s

{
# CHECK: error: dynamic attributes are not allowed in inherit
inherit "foo${a}bar";
}
10 changes: 10 additions & 0 deletions nixd/tools/nixd-lint/test/bind-inherit.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# RUN: nixd-lint -dump-nix-ast %s | FileCheck %s

# CHECK: ExprAttrs: { inherit a ; inherit b ; inherit bar ; inherit c ; inherit foo ; }
{
# CHECK: ExprVar: a
inherit a b c bar;

# CHECK: ExprVar: foo
inherit "foo";
}

0 comments on commit e814ed2

Please sign in to comment.