nixd
Loading...
Searching...
No Matches
SemaActions.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Semantic Actions of AST nodes.
3///
4/// This file implements semantic actions for AST nodes.
5
6#include <memory>
7
12
13namespace nixf {
14
15void Sema::dupAttr(std::string Name, LexerCursorRange Range,
16 LexerCursorRange Prev) {
17 auto &Diag = Diags.emplace_back(Diagnostic::DK_DuplicatedAttrName, Range);
18 Diag << std::move(Name);
19 Diag.note(Note::NK_PrevDeclared, Prev);
20}
21
23 const ExprAttrs &YAttrs) {
24 bool XAttrsRec = XAttrs.isRecursive();
25 bool YAttrsRec = YAttrs.isRecursive();
26 if (XAttrsRec == YAttrsRec)
27 return;
28
29 // Different "rec" modifier!
30 const Misc *Pointer = XAttrsRec ? XAttrs.rec() : YAttrs.rec();
31 auto &D = Diags.emplace_back(Diagnostic::DK_MergeDiffRec, Pointer->range());
32
33 auto XRange = XAttrsRec ? XAttrs.rec()->range() : XAttrs.range();
34 D.note(Note::NK_ThisRecursive, XRange) << (XAttrsRec ? "" : "non-");
35
36 auto YRange = YAttrsRec ? YAttrs.rec()->range() : YAttrs.range();
37 D.note(Note::NK_RecConsider, YRange)
38 << /* Marked as ?recursive */ (YAttrsRec ? "" : "non-")
39 << /* Considered as ?recursive */ (XAttrsRec ? "" : "non-");
40}
41
42void Sema::mergeAttrSets(SemaAttrs &XAttrs, const SemaAttrs &YAttrs) {
43 for (const auto &[K, V] : YAttrs.Static) {
44 if (XAttrs.Static.contains(K)) {
45 // Don't perform recursively merging
46 // e.g.
47 /*
48
49 {
50 p = { x = { y = 1; }; };
51 ^<---------------------- this is duplicated!
52 p = { x = { z = 1; }; };
53 ^~~~~~~~<----------- don't merge nested attrs recursively.
54 }
55
56 */
57 dupAttr(K, V.key().range(), XAttrs.Static.at(K).key().range());
58 continue;
59 }
60 XAttrs.Static.insert({K, V});
61 }
62 for (const auto &DAttr : YAttrs.Dynamic) {
63 XAttrs.Dynamic.emplace_back(DAttr);
64 }
65}
66
67void Sema::insertAttr(SemaAttrs &SA, std::shared_ptr<AttrName> Name,
68 std::shared_ptr<Expr> E, Attribute::AttributeKind Kind) {
69 // In this function we accept nullptr "E".
70 //
71 // e.g. { a = ; }
72 // ^ nullptr
73 //
74 // Duplicate checking will be performed on this in-complete attrset,
75 // however it will not be placed in the final Sema node.
76 assert(Name);
77 if (!Name->isStatic()) {
78 if (E)
79 SA.Dynamic.emplace_back(std::move(Name), std::move(E), Kind);
80 return;
81 }
82 auto &Attrs = SA.Static;
83 std::string StaticName = Name->staticName();
84 if (auto Nested = Attrs.find(StaticName); Nested != Attrs.end()) {
85 const auto &[K, V] = *Nested;
86 if (V.value() && V.value()->kind() == Node::NK_ExprAttrs && E &&
87 E->kind() == Node::NK_ExprAttrs) {
88 // If this is also an attrset, we want to merge them.
89 auto *XAttrSet = static_cast<ExprAttrs *>(V.value());
90 auto *YAttrSet = static_cast<ExprAttrs *>(E.get());
91 checkAttrRecursiveForMerge(*XAttrSet, *YAttrSet);
92 mergeAttrSets(XAttrSet->SA, YAttrSet->SA);
93 return;
94 }
95 dupAttr(StaticName, Name->range(), V.key().range());
96 return;
97 }
98 if (!E)
99 return;
100 Attrs.insert({StaticName, Attribute(std::move(Name), std::move(E), Kind)});
101}
102
103SemaAttrs *
105 const std::vector<std::shared_ptr<AttrName>> &Path) {
106 assert(!Path.empty() && "AttrPath has at least 1 name");
107 SemaAttrs *Inner = &SA;
108 // Firstly perform a lookup to see if the attribute already exists.
109 // And do selection if it exists.
110 for (std::size_t I = 0; I + 1 < Path.size(); I++) {
111 const auto &Name = Path[I];
112 assert(Inner && "Attr is not null");
113 // Name might be nullptr, e.g.
114 // { a..b = 1; }
115 if (!Name)
116 continue;
117 if (Name->isStatic()) {
118 std::map<std::string, Attribute> &StaticAttrs = Inner->Static;
119 const std::string &StaticName = Name->staticName();
120 if (auto Nested = StaticAttrs.find(StaticName);
121 Nested != StaticAttrs.end()) {
122 // Find another attr, with the same name.
123 const auto &[K, V] = *Nested;
124 if (V.fromInherit() || !V.value() ||
125 V.value()->kind() != Node::NK_ExprAttrs) {
126 dupAttr(StaticName, Name->range(), V.key().range());
127 return nullptr;
128 }
129 Inner = &static_cast<ExprAttrs *>(V.value())->SA;
130 } else {
131 // There is no existing one, let's create a new attribute.
132 // These attributes are implicitly created, and to match default ctor
133 // in C++ nix implementation, they are all non-recursive.
134 auto NewNested = std::make_shared<ExprAttrs>(
135 Name->range(), nullptr, nullptr, SemaAttrs(/*Recursive=*/nullptr));
136 Inner = &NewNested->SA;
137 StaticAttrs.insert(
138 {StaticName, Attribute(Name, std::move(NewNested),
140 }
141 } else {
142 // Create a dynamic attribute.
143 std::vector<Attribute> &DynamicAttrs = Inner->Dynamic;
144 auto NewNested = std::make_shared<ExprAttrs>(
145 Name->range(), nullptr, nullptr, SemaAttrs(/*Recursive=*/nullptr));
146 Inner = &NewNested->SA;
147 DynamicAttrs.emplace_back(Name,
148 std::shared_ptr<Expr>(std::move(NewNested)),
150 }
151 }
152 return Inner;
153}
154
155void Sema::addAttr(SemaAttrs &Attr, const AttrPath &Path,
156 std::shared_ptr<Expr> E) {
157 // Select until the inner-most attr.
158 SemaAttrs *Inner = selectOrCreate(Attr, Path.names());
159 if (!Inner)
160 return;
161
162 // Insert the attribute.
163 std::shared_ptr<AttrName> Name = Path.names().back();
164 if (!Name)
165 return;
166 insertAttr(*Inner, std::move(Name), std::move(E),
168}
169
170void Sema::removeFormal(Fix &F, const FormalVector::const_iterator &Rm,
171 const FormalVector &FV) {
172 const Formal &Fm = **Rm;
174 // If it is the first formal, remove second formal's comma.
175 // { ..., foo } -> { foo, ... }
176 if (Rm != FV.begin() || Rm + 1 == FV.end())
177 return;
178
179 Formal &SecondF = **(Rm + 1);
180 if (SecondF.comma())
181 F.edit(TextEdit::mkRemoval(SecondF.comma()->range()));
182}
183
185 if (FV.empty())
186 return;
187
188 Formal &LastF = *FV.back().get(); // Last Formal
189
190 for (auto It = FV.begin(); It + 1 != FV.end(); It++) {
191 Formal &CurF = **It; // Current Formal
192 if (!CurF.isEllipsis())
193 continue;
194
195 if (LastF.isEllipsis()) {
196 // extra "formal", suggest remove it.
197 Diagnostic &D =
198 Diags.emplace_back(Diagnostic::DK_FormalExtraEllipsis, CurF.range());
199 removeFormal(D.fix("remove `...`"), It, FV);
200 } else {
201 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_FormalMisplacedEllipsis,
202 CurF.range());
203 Fix &Fx = D.fix("move ellipsis to the tail");
204 removeFormal(Fx, It, FV);
205 std::string NewText(CurF.src(Src));
206 // If current formal does not contain the seperator ", "
207 // Insert a new comma to seperate it from the last formal
208 if (!CurF.comma())
209 NewText = std::string(", ").append(NewText);
210 Fx.edit(TextEdit::mkInsertion(LastF.rCur(), std::move(NewText)));
211 }
212 }
213}
214
216 if (FV.empty())
217 return;
218 for (auto It = FV.begin(); It != FV.end(); It++) {
219 const Formal &F = **It;
220 // All formals must begins with "," except the first.
221 if (It != FV.begin() && !F.comma()) {
222 Diagnostic &D =
223 Diags.emplace_back(Diagnostic::DK_FormalMissingComma, F.range());
224 D.fix("insert ,").edit(TextEdit::mkInsertion(F.lCur(), ","));
225 }
226 }
227}
228
230 for (auto It = FV.cbegin(); It != FV.cend(); It++) {
231 // Special case, ignore the last one.
232 // Because the nix formatting guide explicitly place a "comma" at the
233 // trailing of formals.
234 if (It + 1 == FV.cend())
235 break;
236
237 const std::shared_ptr<Formal> &FPtr = *It;
238 const Formal &F = *FPtr;
239 // Check if the formal is emtpy, e.g.
240 // { , }
241 // ^ empty formal
242 if (F.comma() && !F.id() && !F.isEllipsis()) {
243 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_EmptyFormal, F.range());
244 D.fix("remove empty formal").edit(TextEdit::mkRemoval(F.range()));
246 continue;
247 }
248 }
249}
250
251void Sema::dedupFormal(std::map<std::string, const Formal *> &Dedup,
252 const FormalVector &FV) {
253 for (const std::shared_ptr<Formal> &FPtr : FV) {
254 const Formal &F = *FPtr;
255 if (!F.id())
256 continue;
257 const Identifier &ID = *F.id();
258 if (Dedup.contains(ID.name())) {
259 // Report duplicated formals.
260 // All warning ranges should be placed at "Identifiers".
261 const Formal &DupF = *Dedup[ID.name()];
262 Identifier &DupID = *DupF.id();
263 Diagnostic &D =
264 Diags.emplace_back(Diagnostic::DK_DuplicatedFormal, ID.range());
265 D.note(Note::NK_DuplicateFormal, DupID.range());
266 } else {
267 Dedup[ID.name()] = &F;
268 }
269 }
270}
271
272std::shared_ptr<Formals> Sema::onFormals(LexerCursorRange Range,
273 FormalVector FV) {
274 std::map<std::string, const Formal *> Dedup;
275 checkFormalSep(FV);
278 dedupFormal(Dedup, FV);
279 return std::make_shared<Formals>(Range, std::move(FV), std::move(Dedup));
280}
281
282void Sema::lowerInheritName(SemaAttrs &SA, std::shared_ptr<AttrName> Name,
283 std::shared_ptr<Expr> E,
284 Attribute::AttributeKind InheritKind) {
285 assert(InheritKind != Attribute::AttributeKind::Plain);
286 if (!Name)
287 return;
288 if (!Name->isStatic()) {
289 // Not allowed to have dynamic attrname in inherit.
290 Diagnostic &D =
291 Diags.emplace_back(Diagnostic::DK_DynamicInherit, Name->range());
292 D.fix("remove dynamic attrname").edit(TextEdit::mkRemoval(Name->range()));
294 return;
295 }
296 // Check duplicated attrname.
297 if (SA.Static.contains(Name->staticName())) {
298 dupAttr(Name->staticName(), Name->range(),
299 SA.Static.at(Name->staticName()).key().range());
300 return;
301 }
302 // Insert the attr.
303 std::string StaticName = Name->staticName();
304 SA.Static.insert(
305 {StaticName, Attribute(std::move(Name), std::move(E), InheritKind)});
306}
307
309 for (const std::shared_ptr<AttrName> &Name : Inherit.names()) {
310 assert(Name);
311 auto [Desugar, Kind] = desugarInheritExpr(Name, Inherit.expr());
312 lowerInheritName(Attr, Name, std::move(Desugar), Kind);
313 }
314}
315
316void Sema::lowerBinds(SemaAttrs &SA, const Binds &B) {
317 for (const std::shared_ptr<Node> &Bind : B.bindings()) {
318 assert(Bind && "Bind is not null");
319 switch (Bind->kind()) {
320 case Node::NK_Inherit: {
321 auto *N = static_cast<Inherit *>(Bind.get());
322 lowerInherit(SA, *N);
323 break;
324 }
325 case Node::NK_Binding: {
326 auto *B = static_cast<Binding *>(Bind.get());
327 addAttr(SA, B->path(), B->value());
328 break;
329 }
330 default:
331 assert(false && "Bind should be either Inherit or Binding");
332 }
333 }
334}
335
336std::pair<std::shared_ptr<Expr>, Attribute::AttributeKind>
337Sema::desugarInheritExpr(std::shared_ptr<AttrName> Name,
338 std::shared_ptr<Expr> E) {
339 auto Range = Name->range();
340 if (!E)
341 return {std::make_shared<ExprVar>(Range, Name->id()),
343
344 auto Path = std::make_shared<AttrPath>(
345 Range, std::vector<std::shared_ptr<AttrName>>{std::move(Name)},
346 std::vector<std::shared_ptr<Dot>>{});
347 return {std::make_shared<ExprSelect>(Range, std::move(E), std::move(Path),
348 nullptr),
350}
351
352std::shared_ptr<ExprAttrs> Sema::onExprAttrs(LexerCursorRange Range,
353 std::shared_ptr<Binds> Binds,
354 std::shared_ptr<Misc> Rec) {
355 SemaAttrs ESA(Rec.get());
356 if (Binds)
357 lowerBinds(ESA, *Binds);
358 return std::make_shared<ExprAttrs>(Range, std::move(Binds), std::move(Rec),
359 std::move(ESA));
360}
361
362std::shared_ptr<LambdaArg> Sema::onLambdaArg(LexerCursorRange Range,
363 std::shared_ptr<Identifier> ID,
364 std::shared_ptr<Formals> F) {
365 // Check that if lambda arguments duplicated to it's formal
366
367 if (ID && F) {
368 if (F->dedup().contains(ID->name())) {
369 // Report duplicated.
370 Diagnostic &D =
371 Diags.emplace_back(Diagnostic::DK_DuplicatedFormalToArg, ID->range());
372 D.note(Note::NK_DuplicateFormal, F->dedup().at(ID->name())->range());
373 }
374 }
375 return std::make_shared<LambdaArg>(Range, std::move(ID), std::move(F));
376}
377
378} // namespace nixf
Semantic Actions while building the AST.
@ InheritFrom
inherit (expr) a b c
const std::vector< std::shared_ptr< Node > > & bindings() const
Definition Attrs.h:175
Note & note(Note::NoteKind Kind, LexerCursorRange Range)
Definition Diagnostic.h:197
Fix & fix(std::string Message)
Definition Diagnostic.h:203
bool isRecursive() const
Definition Attrs.h:282
const Misc * rec() const
Definition Attrs.h:280
Fix & edit(TextEdit Edit)
Definition Diagnostic.h:65
Misc * comma() const
Definition Lambda.h:39
bool isEllipsis() const
Definition Lambda.h:35
Identifier * id() const
Definition Lambda.h:37
Identifier. Variable names, attribute names, etc.
Definition Basic.h:114
const std::string & name() const
Definition Basic.h:120
const std::shared_ptr< Expr > & expr() const
Definition Attrs.h:155
const std::vector< std::shared_ptr< AttrName > > & names() const
Definition Attrs.h:149
Misc node, used for parentheses, keywords, etc.
Definition Basic.h:106
LexerCursor lCur() const
Definition Basic.h:37
std::string_view src(std::string_view Src) const
Definition Basic.h:63
LexerCursor rCur() const
Definition Basic.h:38
LexerCursorRange range() const
Definition Basic.h:35
void tag(DiagnosticTag Tag)
Definition Diagnostic.h:96
Attribute set after deduplication.
Definition Attrs.h:231
SemaAttrs * selectOrCreate(SemaAttrs &SA, const std::vector< std::shared_ptr< AttrName > > &Path)
void insertAttr(SemaAttrs &SA, std::shared_ptr< AttrName > Name, std::shared_ptr< Expr > E, Attribute::AttributeKind Kind)
void lowerInherit(SemaAttrs &Attr, const Inherit &Inherit)
std::shared_ptr< ExprAttrs > onExprAttrs(LexerCursorRange Range, std::shared_ptr< Binds > Binds, std::shared_ptr< Misc > Rec)
std::shared_ptr< LambdaArg > onLambdaArg(LexerCursorRange Range, std::shared_ptr< Identifier > ID, std::shared_ptr< Formals > F)
void dedupFormal(std::map< std::string, const Formal * > &Dedup, const FormalVector &FV)
Deduplicate formals.
void lowerInheritName(SemaAttrs &SA, std::shared_ptr< AttrName > Name, std::shared_ptr< Expr > E, Attribute::AttributeKind InheritKind)
void mergeAttrSets(SemaAttrs &XAttrs, const SemaAttrs &YAttrs)
Perform attrsets merging while duplicated fields are both attrsets.
void checkFormalEllipsis(const FormalVector &FV)
Check if ellipsis "...".
void lowerBinds(SemaAttrs &SA, const Binds &B)
void addAttr(SemaAttrs &Attr, const AttrPath &Path, std::shared_ptr< Expr > E)
Insert the binding: AttrPath = E; into Attr.
void checkFormalSep(const FormalVector &FV)
Check if there is a seperator "," between formals.
void dupAttr(std::string Name, LexerCursorRange Range, LexerCursorRange Prev)
Formals::FormalVector FormalVector
Definition SemaActions.h:24
void checkAttrRecursiveForMerge(const ExprAttrs &XAttrs, const ExprAttrs &YAttrs)
Check if these two attrsets has the same "recursive" modifier.
void checkFormalEmpty(const FormalVector &FV)
Diagnose empty formal i.e. single comma.
std::shared_ptr< Formals > onFormals(LexerCursorRange Range, FormalVector FV)
static void removeFormal(Fix &F, const FormalVector::const_iterator &Rm, const FormalVector &FV)
Make text edits to remove a formal.
static std::pair< std::shared_ptr< Expr >, Attribute::AttributeKind > desugarInheritExpr(std::shared_ptr< AttrName > Name, std::shared_ptr< Expr > E)
Desugar inherit (expr) a, inherit a, into select, or variable.
static TextEdit mkRemoval(LexerCursorRange RemovingRange)
Definition Diagnostic.h:39
static TextEdit mkInsertion(LexerCursor P, std::string NewText)
Definition Diagnostic.h:35