Skip to content

Commit 5e1985c

Browse files
author
Nathan Hawes
committed
[SourceKit] Fix SyntaxModel assertion failures due to consuming tokens twice or prematurely
The SyntaxModel walker would end up visiting the attributes attached to a PatternBindingDecl twice if it contained more than one VarDecl, hitting the below assertion on the second visit because the tokens corresponding to the attribute had already been consumed the first time around: ``` Assertion failed: (0 && "Attribute's TokenNodes already consumed?"), function handleSpecialDeclAttribute ``` It would also hit the same assertion for attributes on an EnumCaseDecl, but even when it only had a single child EnumElementDecl. This because when we visited the EnumCaseDecl and pushed its structure node, we'd consume and emit any tokens before it's start position. This meant that when we tried to process the attributes attached to the child EnumElementDecl its tokens had already been consumed, triggering the assertion. In both cases the attributes syntactically attach to the parent PatternBindingDecl or EnumCaseDecl, but in the AST they're accessed via their child VarDecls or EnumElementDecls. Resolves rdar://problem/53747546
1 parent d5cb71a commit 5e1985c

File tree

2 files changed

+60
-12
lines changed

2 files changed

+60
-12
lines changed

lib/IDE/SyntaxModel.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,13 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
670670
// from the `VarDecl` and the `PatternBindingDecl` entries.
671671
// We take over visitation here to avoid walking the `PatternBindingDecl` ones.
672672
for (auto c : CLE->getCaptureList()) {
673-
if (auto *VD = c.Var)
673+
if (auto *VD = c.Var) {
674+
// We're skipping over the PatternBindingDecl so we need to handle the
675+
// the VarDecl's attributes that we'd normally process visiting the PBD.
676+
if (!handleAttrs(VD->getAttrs()))
677+
return { false, nullptr };
674678
VD->walk(*this);
679+
}
675680
}
676681
if (auto *CE = CLE->getClosureBody())
677682
CE->walk(*this);
@@ -862,8 +867,14 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) {
862867
if (D->isImplicit())
863868
return false;
864869

865-
if (!handleAttrs(D->getAttrs()))
866-
return false;
870+
// The attributes of EnumElementDecls and VarDecls are handled when visiting
871+
// their parent EnumCaseDecl/PatternBindingDecl (which the attributes are
872+
// attached to syntactically).
873+
if (!isa<EnumElementDecl>(D) &&
874+
!(isa<VarDecl>(D) && cast<VarDecl>(D)->getParentPatternBinding())) {
875+
if (!handleAttrs(D->getAttrs()))
876+
return false;
877+
}
867878

868879
if (isa<AccessorDecl>(D)) {
869880
// Don't push structure nodes for accessors.
@@ -948,6 +959,21 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) {
948959
SN.TypeRange = charSourceRangeFromSourceRange(SM,
949960
PD->getTypeSourceRangeForDiagnostics());
950961
pushStructureNode(SN, PD);
962+
} else if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
963+
// Process the attributes of one of the contained VarDecls. Attributes that
964+
// are syntactically attached to the PatternBindingDecl end up on the
965+
// contained VarDecls.
966+
VarDecl *Contained = nullptr;
967+
for (auto idx : range(PBD->getNumPatternEntries())) {
968+
PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) -> void {
969+
Contained = VD;
970+
});
971+
if (Contained) {
972+
if (!handleAttrs(Contained->getAttrs()))
973+
return false;
974+
break;
975+
}
976+
}
951977
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
952978
const DeclContext *DC = VD->getDeclContext();
953979
SyntaxStructureNode SN;
@@ -1012,15 +1038,9 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) {
10121038

10131039
// We need to handle the special case where attributes semantically
10141040
// attach to enum element decls while syntactically locate before enum case decl.
1015-
for (auto *EnumElemD : EnumCaseD->getElements()) {
1016-
for (auto *Att : EnumElemD->getAttrs()) {
1017-
if (Att->isDeclModifier() &&
1018-
SM.isBeforeInBuffer(Att->getLocation(), D->getSourceRange().Start)) {
1019-
passNonTokenNode({SyntaxNodeKind::AttributeBuiltin,
1020-
charSourceRangeFromSourceRange(SM,
1021-
Att->getLocation())});
1022-
}
1023-
}
1041+
if (!EnumCaseD->getElements().empty()) {
1042+
if (!handleAttrs(EnumCaseD->getElements().front()->getAttrs()))
1043+
return false;
10241044
}
10251045
if (pushStructureNode(SN, D)) {
10261046
// FIXME: ASTWalker walks enum elements as members of the enum decl, not

test/IDE/coloring.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,31 @@ func typeAttr3(a: @ escaping () -> Int) {}
494494

495495
// CHECK: <kw>func</kw> typeAttr2(a: @ <comment-block>/*this is fine...*/</comment-block> escaping () -> <type>Int</type>, b: <attr-builtin>@ escaping</attr-builtin> () -> <type>Int</type>) {}
496496
func typeAttr2(a: @ /*this is fine...*/ escaping () -> Int, b: @ escaping () -> Int) {}
497+
498+
// CHECK: <attr-builtin>@available</attr-builtin>(<kw>iOS</kw> <int>99</int>, *)
499+
// CHECK: <kw>var</kw> iHave = <int>10</int>, multipleVars = <int>20</int>
500+
@available(iOS 99, *)
501+
var iHave = 10, multipleVars = 20
502+
503+
enum MultipleCaseElements {
504+
// CHECK: <attr-builtin>@available</attr-builtin>(<kw>iOS</kw> <int>99</int>, *)
505+
// CHECK: <kw>case</kw> foo, bar
506+
@available(iOS 99, *)
507+
case foo, bar
508+
}
509+
510+
protocol P {}
511+
enum E {
512+
// CHECK: <attr-builtin>@available</attr-builtin>(<kw>iOS</kw> <int>99</int>, *)
513+
// CHECK: <kw>case</kw> a(<type>P</type>)
514+
@available(iOS 99, *)
515+
case a(P)
516+
}
517+
518+
// Ideally this would be attr-builtin, but we don't actually have the attribute
519+
// in the AST at all.
520+
//
521+
// CHECK: <attr-id>@available</attr-id>(<kw>iOS</kw> <int>99</int>, *)
522+
// CHECK: <kw>var</kw> <kw>_</kw> = <int>10</int>
523+
@available(iOS 99, *)
524+
var _ = 10

0 commit comments

Comments
 (0)