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
42bool Sema::tryMergeAttrs(Expr *X, Expr *Y) {
43 if (!X || X->kind() != Node::NK_ExprAttrs || !Y ||
44 Y->kind() != Node::NK_ExprAttrs)
45 return false;
46
47 auto *XAttrs = static_cast<ExprAttrs *>(X);
48 auto *YAttrs = static_cast<ExprAttrs *>(Y);
49 checkAttrRecursiveForMerge(*XAttrs, *YAttrs);
50 mergeAttrSets(XAttrs->SA, YAttrs->SA);
51 return true;
52}
53
54void Sema::mergeAttrSets(SemaAttrs &XAttrs, const SemaAttrs &YAttrs) {
55 for (const auto &[K, Y] : YAttrs.Static) {
56 auto [It, Inserted] = XAttrs.Static.insert({K, Y});
57 if (Inserted)
58 continue;
59
60 const auto &[_, X] = *It;
61 if (!tryMergeAttrs(X.value(), Y.value()))
62 dupAttr(K, Y.key().range(), X.key().range());
63 }
64 for (const auto &DAttr : YAttrs.Dynamic) {
65 XAttrs.Dynamic.emplace_back(DAttr);
66 }
67}
68
69void Sema::insertAttr(SemaAttrs &SA, std::shared_ptr<AttrName> Name,
70 std::shared_ptr<Expr> E, Attribute::AttributeKind Kind) {
71 // In this function we accept nullptr "E".
72 //
73 // e.g. { a = ; }
74 // ^ nullptr
75 //
76 // Duplicate checking will be performed on this in-complete attrset,
77 // however it will not be placed in the final Sema node.
78 assert(Name);
79 if (!Name->isStatic()) {
80 if (E)
81 SA.Dynamic.emplace_back(std::move(Name), std::move(E), Kind);
82 return;
83 }
84 auto &Attrs = SA.Static;
85 std::string StaticName = Name->staticName();
86 if (auto Nested = Attrs.find(StaticName); Nested != Attrs.end()) {
87 const auto &[K, V] = *Nested;
88 if (!tryMergeAttrs(V.value(), E.get()))
89 dupAttr(StaticName, Name->range(), V.key().range());
90 return;
91 }
92 if (!E)
93 return;
94 Attrs.insert({StaticName, Attribute(std::move(Name), std::move(E), Kind)});
95}
96
99 const std::vector<std::shared_ptr<AttrName>> &Path) {
100 assert(!Path.empty() && "AttrPath has at least 1 name");
101 SemaAttrs *Inner = &SA;
102 // Firstly perform a lookup to see if the attribute already exists.
103 // And do selection if it exists.
104 for (std::size_t I = 0; I + 1 < Path.size(); I++) {
105 const auto &Name = Path[I];
106 assert(Inner && "Attr is not null");
107 // Name might be nullptr, e.g.
108 // { a..b = 1; }
109 if (!Name)
110 continue;
111 if (Name->isStatic()) {
112 std::map<std::string, Attribute> &StaticAttrs = Inner->Static;
113 const std::string &StaticName = Name->staticName();
114 if (auto Nested = StaticAttrs.find(StaticName);
115 Nested != StaticAttrs.end()) {
116 // Find another attr, with the same name.
117 const auto &[K, V] = *Nested;
118 if (V.fromInherit() || !V.value() ||
119 V.value()->kind() != Node::NK_ExprAttrs) {
120 dupAttr(StaticName, Name->range(), V.key().range());
121 return nullptr;
122 }
123 Inner = &static_cast<ExprAttrs *>(V.value())->SA;
124 } else {
125 // There is no existing one, let's create a new attribute.
126 // These attributes are implicitly created, and to match default ctor
127 // in C++ nix implementation, they are all non-recursive.
128 auto NewNested = std::make_shared<ExprAttrs>(
129 Name->range(), nullptr, nullptr, SemaAttrs(/*Recursive=*/nullptr));
130 Inner = &NewNested->SA;
131 StaticAttrs.insert(
132 {StaticName, Attribute(Name, std::move(NewNested),
134 }
135 } else {
136 // Create a dynamic attribute.
137 std::vector<Attribute> &DynamicAttrs = Inner->Dynamic;
138 auto NewNested = std::make_shared<ExprAttrs>(
139 Name->range(), nullptr, nullptr, SemaAttrs(/*Recursive=*/nullptr));
140 Inner = &NewNested->SA;
141 DynamicAttrs.emplace_back(Name,
142 std::shared_ptr<Expr>(std::move(NewNested)),
144 }
145 }
146 return Inner;
147}
148
149void Sema::addAttr(SemaAttrs &Attr, const AttrPath &Path,
150 std::shared_ptr<Expr> E) {
151 // Select until the inner-most attr.
152 SemaAttrs *Inner = selectOrCreate(Attr, Path.names());
153 if (!Inner)
154 return;
155
156 // Insert the attribute.
157 std::shared_ptr<AttrName> Name = Path.names().back();
158 if (!Name)
159 return;
160 insertAttr(*Inner, std::move(Name), std::move(E),
162}
163
164void Sema::removeFormal(Fix &F, const FormalVector::const_iterator &Rm,
165 const FormalVector &FV) {
166 const Formal &Fm = **Rm;
168 // If it is the first formal, remove second formal's comma.
169 // { ..., foo } -> { foo, ... }
170 if (Rm != FV.begin() || Rm + 1 == FV.end())
171 return;
172
173 Formal &SecondF = **(Rm + 1);
174 if (SecondF.comma())
175 F.edit(TextEdit::mkRemoval(SecondF.comma()->range()));
176}
177
179 if (FV.empty())
180 return;
181
182 Formal &LastF = *FV.back().get(); // Last Formal
183
184 for (auto It = FV.begin(); It + 1 != FV.end(); It++) {
185 Formal &CurF = **It; // Current Formal
186 if (!CurF.isEllipsis())
187 continue;
188
189 if (LastF.isEllipsis()) {
190 // extra "formal", suggest remove it.
191 Diagnostic &D =
192 Diags.emplace_back(Diagnostic::DK_FormalExtraEllipsis, CurF.range());
193 removeFormal(D.fix("remove `...`"), It, FV);
194 } else {
195 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_FormalMisplacedEllipsis,
196 CurF.range());
197 Fix &Fx = D.fix("move ellipsis to the tail");
198 removeFormal(Fx, It, FV);
199 std::string NewText(CurF.src(Src));
200 // If current formal does not contain the seperator ", "
201 // Insert a new comma to seperate it from the last formal
202 if (!CurF.comma())
203 NewText = std::string(", ").append(NewText);
204 Fx.edit(TextEdit::mkInsertion(LastF.rCur(), std::move(NewText)));
205 }
206 }
207}
208
210 if (FV.empty())
211 return;
212 for (auto It = FV.begin(); It != FV.end(); It++) {
213 const Formal &F = **It;
214 // All formals must begins with "," except the first.
215 if (It != FV.begin() && !F.comma()) {
216 Diagnostic &D =
217 Diags.emplace_back(Diagnostic::DK_FormalMissingComma, F.range());
218 D.fix("insert ,").edit(TextEdit::mkInsertion(F.lCur(), ","));
219 }
220 }
221}
222
224 for (auto It = FV.cbegin(); It != FV.cend(); It++) {
225 // Special case, ignore the last one.
226 // Because the nix formatting guide explicitly place a "comma" at the
227 // trailing of formals.
228 if (It + 1 == FV.cend())
229 break;
230
231 const std::shared_ptr<Formal> &FPtr = *It;
232 const Formal &F = *FPtr;
233 // Check if the formal is emtpy, e.g.
234 // { , }
235 // ^ empty formal
236 if (F.comma() && !F.id() && !F.isEllipsis()) {
237 Diagnostic &D = Diags.emplace_back(Diagnostic::DK_EmptyFormal, F.range());
238 D.fix("remove empty formal").edit(TextEdit::mkRemoval(F.range()));
240 continue;
241 }
242 }
243}
244
245void Sema::dedupFormal(std::map<std::string, const Formal *> &Dedup,
246 const FormalVector &FV) {
247 for (const std::shared_ptr<Formal> &FPtr : FV) {
248 const Formal &F = *FPtr;
249 if (!F.id())
250 continue;
251 const Identifier &ID = *F.id();
252 if (Dedup.contains(ID.name())) {
253 // Report duplicated formals.
254 // All warning ranges should be placed at "Identifiers".
255 const Formal &DupF = *Dedup[ID.name()];
256 Identifier &DupID = *DupF.id();
257 Diagnostic &D =
258 Diags.emplace_back(Diagnostic::DK_DuplicatedFormal, ID.range());
259 D.note(Note::NK_DuplicateFormal, DupID.range());
260 } else {
261 Dedup[ID.name()] = &F;
262 }
263 }
264}
265
266std::shared_ptr<Formals> Sema::onFormals(LexerCursorRange Range,
267 FormalVector FV) {
268 std::map<std::string, const Formal *> Dedup;
269 checkFormalSep(FV);
272 dedupFormal(Dedup, FV);
273 return std::make_shared<Formals>(Range, std::move(FV), std::move(Dedup));
274}
275
276void Sema::lowerInheritName(SemaAttrs &SA, std::shared_ptr<AttrName> Name,
277 std::shared_ptr<Expr> E,
278 Attribute::AttributeKind InheritKind) {
279 assert(InheritKind != Attribute::AttributeKind::Plain);
280 if (!Name)
281 return;
282 if (!Name->isStatic()) {
283 // Not allowed to have dynamic attrname in inherit.
284 Diagnostic &D =
285 Diags.emplace_back(Diagnostic::DK_DynamicInherit, Name->range());
286 D.fix("remove dynamic attrname").edit(TextEdit::mkRemoval(Name->range()));
288 return;
289 }
290 // Check duplicated attrname.
291 if (SA.Static.contains(Name->staticName())) {
292 dupAttr(Name->staticName(), Name->range(),
293 SA.Static.at(Name->staticName()).key().range());
294 return;
295 }
296 // Insert the attr.
297 std::string StaticName = Name->staticName();
298 SA.Static.insert(
299 {StaticName, Attribute(std::move(Name), std::move(E), InheritKind)});
300}
301
303 for (const std::shared_ptr<AttrName> &Name : Inherit.names()) {
304 assert(Name);
305 auto [Desugar, Kind] = desugarInheritExpr(Name, Inherit.expr());
306 lowerInheritName(Attr, Name, std::move(Desugar), Kind);
307 }
308}
309
310void Sema::lowerBinds(SemaAttrs &SA, const Binds &B) {
311 for (const std::shared_ptr<Node> &Bind : B.bindings()) {
312 assert(Bind && "Bind is not null");
313 switch (Bind->kind()) {
314 case Node::NK_Inherit: {
315 auto *N = static_cast<Inherit *>(Bind.get());
316 lowerInherit(SA, *N);
317 break;
318 }
319 case Node::NK_Binding: {
320 auto *B = static_cast<Binding *>(Bind.get());
321 addAttr(SA, B->path(), B->value());
322 break;
323 }
324 default:
325 assert(false && "Bind should be either Inherit or Binding");
326 }
327 }
328}
329
330std::pair<std::shared_ptr<Expr>, Attribute::AttributeKind>
331Sema::desugarInheritExpr(std::shared_ptr<AttrName> Name,
332 std::shared_ptr<Expr> E) {
333 auto Range = Name->range();
334 if (!E)
335 return {std::make_shared<ExprVar>(Range, Name->id()),
337
338 auto Path = std::make_shared<AttrPath>(
339 Range, std::vector<std::shared_ptr<AttrName>>{std::move(Name)},
340 std::vector<std::shared_ptr<Dot>>{});
341 return {std::make_shared<ExprSelect>(Range, std::move(E), nullptr,
342 std::move(Path), nullptr, E.get()),
344}
345
346std::shared_ptr<ExprAttrs> Sema::onExprAttrs(LexerCursorRange Range,
347 std::shared_ptr<Binds> Binds,
348 std::shared_ptr<Misc> Rec) {
349 SemaAttrs ESA(Rec.get());
350 if (Binds)
351 lowerBinds(ESA, *Binds);
352 return std::make_shared<ExprAttrs>(Range, std::move(Binds), std::move(Rec),
353 std::move(ESA));
354}
355
356std::shared_ptr<LambdaArg> Sema::onLambdaArg(LexerCursorRange Range,
357 std::shared_ptr<Identifier> ID,
358 std::shared_ptr<Formals> F) {
359 // Check that if lambda arguments duplicated to it's formal
360
361 if (ID && F) {
362 if (F->dedup().contains(ID->name())) {
363 // Report duplicated.
364 Diagnostic &D =
365 Diags.emplace_back(Diagnostic::DK_DuplicatedFormalToArg, ID->range());
366 D.note(Note::NK_DuplicateFormal, F->dedup().at(ID->name())->range());
367 }
368 }
369 return std::make_shared<LambdaArg>(Range, std::move(ID), std::move(F));
370}
371
372} // namespace nixf
Semantic Actions while building the AST.
@ InheritFrom
inherit (expr) a b c
Definition Attrs.h:202
@ Inherit
inherit a b c;
Definition Attrs.h:200
const std::vector< std::shared_ptr< Node > > & bindings() const
Definition Attrs.h:180
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:287
const Misc * rec() const
Definition Attrs.h:285
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:160
const std::vector< std::shared_ptr< AttrName > > & names() const
Definition Attrs.h:154
Misc node, used for parentheses, keywords, etc.
Definition Basic.h:106
LexerCursor lCur() const
Definition Basic.h:37
NodeKind kind() const
Definition Basic.h:34
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:236
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:27
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