Skip to content

[IDE][Parse] Fix syntax coloring ranges for type attributes #26418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ class TypeAttributes {
AttrLocs[A] = L;
}

void getAttrRanges(SmallVectorImpl<SourceRange> &Ranges) const {
void getAttrLocs(SmallVectorImpl<SourceLoc> &Locs) const {
for (auto Loc : AttrLocs) {
if (Loc.isValid())
Ranges.push_back(Loc);
Locs.push_back(Loc);
}
}

Expand Down
2 changes: 1 addition & 1 deletion include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ class Parser {
bool parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier,
SourceLoc &SpecifierLoc,
TypeAttributes &Attributes);
bool parseTypeAttribute(TypeAttributes &Attributes,
bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
bool justChecking = false);


Expand Down
1 change: 0 additions & 1 deletion lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
}

bool visitPatternBindingDecl(PatternBindingDecl *PBD) {
// If there is a single variable, walk it's attributes.
bool isPropertyWrapperBackingProperty = false;
if (auto singleVar = PBD->getSingleVar()) {
isPropertyWrapperBackingProperty =
Expand Down
93 changes: 61 additions & 32 deletions lib/IDE/SyntaxModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,13 @@ class ModelASTWalker : public ASTWalker {
/// Like ExcludeNodeAtLocation, and skip past any node at the location.
DisplaceNodeAtLocation
};
bool passTokenNodesUntil(SourceLoc Loc, PassNodesBehavior Behavior);
struct PassUntilResult {
bool shouldContinue;
Optional<SyntaxNode> MatchedToken;
};

PassUntilResult
passTokenNodesUntil(SourceLoc Loc, PassNodesBehavior Pass);
bool passNonTokenNode(const SyntaxNode &Node);
bool passNode(const SyntaxNode &Node);
bool pushStructureNode(const SyntaxStructureNode &Node,
Expand Down Expand Up @@ -433,8 +439,7 @@ std::pair<bool, Expr *> ModelASTWalker::walkToExprPre(Expr *E) {
if (NR.isValid()) {
SN.Range = charSourceRangeFromSourceRange(SM, SourceRange(NR.getStart(),
Elem->getEndLoc()));
passTokenNodesUntil(NR.getStart(),
PassNodesBehavior::ExcludeNodeAtLocation);
passTokenNodesUntil(NR.getStart(), ExcludeNodeAtLocation);
}
else
SN.Range = SN.BodyRange;
Expand Down Expand Up @@ -972,7 +977,8 @@ bool ModelASTWalker::walkToTypeReprPre(TypeRepr *T) {
return false;

} else if (auto IdT = dyn_cast<ComponentIdentTypeRepr>(T)) {
if (!passTokenNodesUntil(IdT->getIdLoc(), ExcludeNodeAtLocation))
if (!passTokenNodesUntil(IdT->getIdLoc(),
ExcludeNodeAtLocation).shouldContinue)
return false;
if (TokenNodes.empty() ||
TokenNodes.front().Range.getStart() != IdT->getIdLoc())
Expand Down Expand Up @@ -1025,7 +1031,7 @@ bool ModelASTWalker::handleSpecialDeclAttribute(const DeclAttribute *D,
return false;
if (isa<CustomAttr>(D) || isa<AvailableAttr>(D)) {
if (!passTokenNodesUntil(D->getRangeWithAt().Start,
PassNodesBehavior::ExcludeNodeAtLocation))
ExcludeNodeAtLocation).shouldContinue)
return false;
if (auto *CA = dyn_cast<CustomAttr>(D)) {
if (auto *Repr = CA->getTypeLoc().getTypeRepr()) {
Expand All @@ -1048,7 +1054,8 @@ bool ModelASTWalker::handleSpecialDeclAttribute(const DeclAttribute *D,
} else {
assert(0 && "No TokenNodes?");
}
if (!passTokenNodesUntil(D->getRange().End, PassNodesBehavior::IncludeNodeAtLocation))
if (!passTokenNodesUntil(D->getRange().End,
IncludeNodeAtLocation).shouldContinue)
return false;
return true;
}
Expand All @@ -1067,11 +1074,11 @@ bool ModelASTWalker::handleAttrs(const DeclAttributes &Attrs) {
}

bool ModelASTWalker::handleAttrs(const TypeAttributes &Attrs) {
SmallVector<SourceRange, 4> Ranges;
Attrs.getAttrRanges(Ranges);
SmallVector<SourceLoc, 4> AttrLocs;
Attrs.getAttrLocs(AttrLocs);
SmallVector<DeclAttributeAndRange, 4> DeclRanges;
for (auto R : Ranges) {
DeclRanges.push_back(std::make_pair(nullptr, R));
for (auto AttrLoc : AttrLocs) {
DeclRanges.push_back(std::make_pair(nullptr, SourceRange(AttrLoc)));
}
return handleAttrRanges(DeclRanges);
}
Expand Down Expand Up @@ -1099,20 +1106,34 @@ bool ModelASTWalker::handleAttrRanges(ArrayRef<DeclAttributeAndRange> DeclRanges
DeclRanges = SortedRanges;

SourceLoc BeginLoc = DeclRanges.front().second.Start;

auto Toks = slice_token_array(AllTokensInFile, BeginLoc,
DeclRanges.back().second.End);

auto passAttrNode = [&](SourceRange AttrRange) -> bool {
SourceRange Range = AttrRange;
if (!passNonTokenNode({SyntaxNodeKind::AttributeBuiltin,
charSourceRangeFromSourceRange(SM, Range)}))
auto PassUntilResult = passTokenNodesUntil(Range.Start,
ExcludeNodeAtLocation);
if (!PassUntilResult.shouldContinue)
return false;

while (!TokenNodes.empty() &&
SM.rangeContainsTokenLoc(AttrRange,
TokenNodes.front().Range.getStart()))
TokenNodes = TokenNodes.slice(1);
if (PassUntilResult.MatchedToken) {
// Type attribute ranges don't have the correct end location (it only
// covers the @ itself), so use matched token's range instead.
CharSourceRange AdjustedRange = charSourceRangeFromSourceRange(SM, Range);
AdjustedRange.widen(PassUntilResult.MatchedToken->Range);
if (!passNode({SyntaxNodeKind::AttributeBuiltin, AdjustedRange}))
return false;
TokenNodes = TokenNodes.drop_while([&](SyntaxNode TokenNode) {
return AdjustedRange.contains(TokenNode.Range.getStart());
});
} else {
// Make sure we're revisiting something, rather than dealing with bad
// source locations
assert((TokenNodes.empty() ||
SM.isBeforeInBuffer(AttrRange.End,
TokenNodes.front().Range.getStart())) &&
"AttrRange doesn't align with any TokenNode?");
}
return true;
};

Expand Down Expand Up @@ -1146,31 +1167,36 @@ bool ModelASTWalker::shouldPassBraceStructureNode(BraceStmt *S) {
S->getSourceRange().isValid());
}

bool ModelASTWalker::passTokenNodesUntil(SourceLoc Loc,
PassNodesBehavior Behavior) {
ModelASTWalker::PassUntilResult
ModelASTWalker::passTokenNodesUntil(SourceLoc Loc,
PassNodesBehavior Behavior) {
assert(Loc.isValid());
unsigned I = 0;
Optional<SyntaxNode> MatchedToken;
for (unsigned E = TokenNodes.size(); I != E; ++I) {
SourceLoc TokLoc = TokenNodes[I].Range.getStart();
if (SM.isBeforeInBuffer(Loc, TokLoc)) {
SourceLoc StartLoc = TokenNodes[I].Range.getStart();
if (SM.isBeforeInBuffer(Loc, StartLoc)) {
break;
}
if (TokLoc == Loc && Behavior != IncludeNodeAtLocation) {
if (Behavior == DisplaceNodeAtLocation) {
// Skip past the node directly at the specified location, allowing the
// caller to effectively replace it.
++I;
if (StartLoc == Loc) {
MatchedToken = TokenNodes[I];
if (Behavior != IncludeNodeAtLocation) {
if (Behavior == DisplaceNodeAtLocation) {
// Skip past the node directly at the specified location, allowing the
// caller to effectively replace it.
++I;
}
break;
}
break;
}
if (!AvoidPassingSyntaxToken) {
if (!passNode(TokenNodes[I]))
return false;
return {false, None};
}
}

TokenNodes = TokenNodes.slice(I);
return true;
return {true, MatchedToken};
}

bool ModelASTWalker::passNonTokenNode(const SyntaxNode &Node) {
Expand All @@ -1181,7 +1207,8 @@ bool ModelASTWalker::passNonTokenNode(const SyntaxNode &Node) {
if (!SM.isBeforeInBuffer(LastLoc, Node.Range.getStart()))
return true;

if (!passTokenNodesUntil(Node.Range.getStart(), DisplaceNodeAtLocation))
if (!passTokenNodesUntil(Node.Range.getStart(),
DisplaceNodeAtLocation).shouldContinue)
return false;
if (!passNode(Node))
return false;
Expand Down Expand Up @@ -1223,7 +1250,8 @@ bool ModelASTWalker::pushStructureNode(const SyntaxStructureNode &Node,
if (shouldAvoidPssingSyntaxToken(Node))
AvoidPassingSyntaxToken ++;

if (!passTokenNodesUntil(Node.Range.getStart(), ExcludeNodeAtLocation))
if (!passTokenNodesUntil(Node.Range.getStart(),
ExcludeNodeAtLocation).shouldContinue)
return false;
if (!Walker.walkToSubStructurePre(Node))
return false;
Expand All @@ -1245,7 +1273,8 @@ bool ModelASTWalker::popStructureNode() {
// VarDecls are popped before we see their TypeRepr, so if we pass the token
// nodes now they will not change from identifier to a type-identifier.
if (!Node.hasSubstructure()) {
if (!passTokenNodesUntil(Node.Range.getEnd(), IncludeNodeAtLocation))
if (!passTokenNodesUntil(Node.Range.getEnd(),
IncludeNodeAtLocation).shouldContinue)
return false;
}
if (!Walker.walkToSubStructurePost(Node))
Expand Down
26 changes: 14 additions & 12 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1967,7 +1967,8 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At

bool Parser::canParseTypeAttribute() {
TypeAttributes attrs; // ignored
return !parseTypeAttribute(attrs, /*justChecking*/ true);
return !parseTypeAttribute(attrs, /*atLoc=*/SourceLoc(),
/*justChecking*/ true);
}

/// \verbatim
Expand All @@ -1978,7 +1979,8 @@ bool Parser::canParseTypeAttribute() {
/// \param justChecking - if true, we're just checking whether we
/// canParseTypeAttribute; don't emit any diagnostics, and there's
/// no need to actually record the attribute
bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
bool justChecking) {
// If this not an identifier, the attribute is malformed.
if (Tok.isNot(tok::identifier) &&
// These are keywords that we accept as attribute names.
Expand Down Expand Up @@ -2040,7 +2042,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {

// Ok, it is a valid attribute, eat it, and then process it.
StringRef Text = Tok.getText();
SourceLoc Loc = consumeToken();
consumeToken();

StringRef conventionName;
StringRef witnessMethodProtocol;
Expand Down Expand Up @@ -2100,7 +2102,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {

// Diagnose duplicated attributes.
if (Attributes.has(attr)) {
diagnose(Loc, diag::duplicate_attribute, /*isModifier=*/false);
diagnose(AtLoc, diag::duplicate_attribute, /*isModifier=*/false);
return false;
}

Expand All @@ -2122,7 +2124,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
case TAK_callee_guaranteed:
case TAK_objc_metatype:
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, Text);
diagnose(AtLoc, diag::only_allowed_in_sil, Text);
return false;
}
break;
Expand All @@ -2131,27 +2133,27 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
case TAK_sil_weak:
case TAK_sil_unowned:
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, Text);
diagnose(AtLoc, diag::only_allowed_in_sil, Text);
return false;
}

if (Attributes.hasOwnership()) {
diagnose(Loc, diag::duplicate_attribute, /*isModifier*/false);
diagnose(AtLoc, diag::duplicate_attribute, /*isModifier*/false);
return false;
}
break;

// 'inout' attribute.
case TAK_inout:
if (!isInSILMode()) {
diagnose(Loc, diag::inout_not_attribute);
diagnose(AtLoc, diag::inout_not_attribute);
return false;
}
break;

case TAK_opened: {
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, "opened");
diagnose(AtLoc, diag::only_allowed_in_sil, "opened");
return false;
}

Expand Down Expand Up @@ -2231,7 +2233,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
}
}

Attributes.setAttr(attr, Loc);
Attributes.setAttr(attr, AtLoc);
return false;
}

Expand Down Expand Up @@ -2448,8 +2450,8 @@ bool Parser::parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier,
if (Attributes.AtLoc.isInvalid())
Attributes.AtLoc = Tok.getLoc();
SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute);
consumeToken();
if (parseTypeAttribute(Attributes))
SourceLoc AtLoc = consumeToken();
if (parseTypeAttribute(Attributes, AtLoc))
return true;
}

Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2040,8 +2040,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
TypeResolutionOptions options) {
// Convenience to grab the source range of a type attribute.
auto getTypeAttrRangeWithAt = [](ASTContext &ctx, SourceLoc attrLoc) {
return SourceRange(attrLoc.getAdvancedLoc(-1),
Lexer::getLocForEndOfToken(ctx.SourceMgr, attrLoc));
return SourceRange(attrLoc, attrLoc.getAdvancedLoc(1));

};

Expand Down
9 changes: 9 additions & 0 deletions test/IDE/coloring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -454,3 +454,12 @@ func acceptBuilder<T>(
// CHECK: @<type>SomeBuilder</type><<type>Element</type>> label param: () -> <type>T</type>
@SomeBuilder<Element> label param: () -> T
) {}

// CHECK: <kw>func</kw> typeAttr(a: <attr-builtin>@escaping</attr-builtin> () -> <type>Int</type>) {}
func typeAttr(a: @escaping () -> Int) {}

// CHECK: <kw>func</kw> typeAttr3(a: <attr-builtin>@ escaping</attr-builtin> () -> <type>Int</type>) {}
func typeAttr3(a: @ escaping () -> Int) {}

// 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>) {}
func typeAttr2(a: @ /*this is fine...*/ escaping () -> Int, b: @ escaping () -> Int) {}
3 changes: 3 additions & 0 deletions test/IDE/structure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,6 @@ enum FooEnum {
// CHECK: }</brace></closure></param>)</name></enum-elem></enum-case>
}
// CHECK: }</enum>

fourthCall(a: @escaping () -> Int)
// CHECK: <call><name>fourthCall</name>(<arg><name>a</name>: @escaping () -> Int</arg>)</call>
7 changes: 1 addition & 6 deletions test/SourceKit/InterfaceGen/gen_clang_module.swift.response
Original file line number Diff line number Diff line change
Expand Up @@ -1610,15 +1610,10 @@ public class FooOverlayClassDerived : Foo.FooOverlayClassBase {
key.length: 4
},
{
key.kind: source.lang.swift.syntaxtype.attribute.id,
key.kind: source.lang.swift.syntaxtype.attribute.builtin,
key.offset: 2465,
key.length: 11
},
{
key.kind: source.lang.swift.syntaxtype.attribute.builtin,
key.offset: 2466,
key.length: 10
},
{
key.kind: source.lang.swift.syntaxtype.identifier,
key.offset: 2477,
Expand Down
Loading