Introduce basic Stack type (#4922)
This commit is contained in:
parent
46ff727a13
commit
c241935635
|
@ -0,0 +1,37 @@
|
||||||
|
/// A basic stack type implementing the LIFO principle - only the last inserted element can be accessed and removed.
|
||||||
|
struct Stack<Element> {
|
||||||
|
private var elements = [Element]()
|
||||||
|
|
||||||
|
var isEmpty: Bool {
|
||||||
|
elements.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: Int {
|
||||||
|
elements.count
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func push(_ element: Element) {
|
||||||
|
elements.append(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
mutating func pop() -> Element? {
|
||||||
|
elements.popLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
func peek() -> Element? {
|
||||||
|
elements.last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Stack: CustomDebugStringConvertible where Element == CustomDebugStringConvertible {
|
||||||
|
var debugDescription: String {
|
||||||
|
let intermediateElements = count > 1 ? elements[1 ..< count - 1] : []
|
||||||
|
return """
|
||||||
|
Stack with \(count) elements:
|
||||||
|
first: \(elements.first?.debugDescription ?? "")
|
||||||
|
intermediate: \(intermediateElements.map(\.debugDescription).joined(separator: ", "))
|
||||||
|
last: \(peek()?.debugDescription ?? "")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,57 +59,57 @@ private class Visitor: ViolationsSyntaxVisitor {
|
||||||
case skipReferences
|
case skipReferences
|
||||||
}
|
}
|
||||||
|
|
||||||
private var parentDeclScopes = [ParentDeclBehavior]()
|
private var parentDeclScopes = Stack<ParentDeclBehavior>()
|
||||||
private var variableDeclScopes = [VariableDeclBehavior]()
|
private var variableDeclScopes = Stack<VariableDeclBehavior>()
|
||||||
private(set) var corrections = [(start: AbsolutePosition, end: AbsolutePosition)]()
|
private(set) var corrections = [(start: AbsolutePosition, end: AbsolutePosition)]()
|
||||||
|
|
||||||
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.likeClass(name: node.identifier.text))
|
parentDeclScopes.push(.likeClass(name: node.identifier.text))
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ActorDeclSyntax) {
|
override func visitPost(_ node: ActorDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.likeClass(name: node.identifier.text))
|
parentDeclScopes.push(.likeClass(name: node.identifier.text))
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ClassDeclSyntax) {
|
override func visitPost(_ node: ClassDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: CodeBlockSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: CodeBlockSyntax) -> SyntaxVisitorContinueKind {
|
||||||
variableDeclScopes.append(.handleReferences)
|
variableDeclScopes.push(.handleReferences)
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: CodeBlockSyntax) {
|
override func visitPost(_ node: CodeBlockSyntax) {
|
||||||
_ = variableDeclScopes.popLast()
|
variableDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.likeStruct(node.identifier.text))
|
parentDeclScopes.push(.likeStruct(node.identifier.text))
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: EnumDeclSyntax) {
|
override func visitPost(_ node: EnumDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.skipReferences)
|
parentDeclScopes.push(.skipReferences)
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ExtensionDeclSyntax) {
|
override func visitPost(_ node: ExtensionDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
|
||||||
if case .likeClass = parentDeclScopes.last {
|
if case .likeClass = parentDeclScopes.peek() {
|
||||||
if node.name.tokenKind == .keyword(.self) {
|
if node.name.tokenKind == .keyword(.self) {
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
@ -124,62 +124,62 @@ private class Visitor: ViolationsSyntaxVisitor {
|
||||||
!parent.is(ArrayElementSyntax.self) else {
|
!parent.is(ArrayElementSyntax.self) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if parent.is(FunctionCallExprSyntax.self), case .likeClass = parentDeclScopes.last {
|
if parent.is(FunctionCallExprSyntax.self), case .likeClass = parentDeclScopes.peek() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
addViolation(on: node.identifier)
|
addViolation(on: node.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: MemberDeclBlockSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: MemberDeclBlockSyntax) -> SyntaxVisitorContinueKind {
|
||||||
if case .likeClass = parentDeclScopes.last {
|
if case .likeClass = parentDeclScopes.peek() {
|
||||||
variableDeclScopes.append(.skipReferences)
|
variableDeclScopes.push(.skipReferences)
|
||||||
} else {
|
} else {
|
||||||
variableDeclScopes.append(.handleReferences)
|
variableDeclScopes.push(.handleReferences)
|
||||||
}
|
}
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: MemberDeclBlockSyntax) {
|
override func visitPost(_ node: MemberDeclBlockSyntax) {
|
||||||
_ = variableDeclScopes.popLast()
|
variableDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
|
||||||
if case .likeClass = parentDeclScopes.last, case .identifier("selector") = node.macro.tokenKind {
|
if case .likeClass = parentDeclScopes.peek(), case .identifier("selector") = node.macro.tokenKind {
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ParameterClauseSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ParameterClauseSyntax) -> SyntaxVisitorContinueKind {
|
||||||
if case .likeStruct = parentDeclScopes.last {
|
if case .likeStruct = parentDeclScopes.peek() {
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.skipReferences)
|
parentDeclScopes.push(.skipReferences)
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: ProtocolDeclSyntax) {
|
override func visitPost(_ node: ProtocolDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
|
||||||
parentDeclScopes.append(.likeStruct(node.identifier.text))
|
parentDeclScopes.push(.likeStruct(node.identifier.text))
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: StructDeclSyntax) {
|
override func visitPost(_ node: StructDeclSyntax) {
|
||||||
_ = parentDeclScopes.popLast()
|
parentDeclScopes.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visitPost(_ node: SimpleTypeIdentifierSyntax) {
|
override func visitPost(_ node: SimpleTypeIdentifierSyntax) {
|
||||||
guard let parent = node.parent else {
|
guard let parent = node.parent else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if case .likeClass = parentDeclScopes.last,
|
if case .likeClass = parentDeclScopes.peek(),
|
||||||
parent.is(GenericArgumentSyntax.self) || parent.is(ReturnClauseSyntax.self) {
|
parent.is(GenericArgumentSyntax.self) || parent.is(ReturnClauseSyntax.self) {
|
||||||
// Type is a generic parameter or the return type of a function.
|
// Type is a generic parameter or the return type of a function.
|
||||||
return
|
return
|
||||||
|
@ -191,7 +191,7 @@ private class Visitor: ViolationsSyntaxVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func visit(_ node: TypeAnnotationSyntax) -> SyntaxVisitorContinueKind {
|
override func visit(_ node: TypeAnnotationSyntax) -> SyntaxVisitorContinueKind {
|
||||||
guard case .likeStruct = parentDeclScopes.last else {
|
guard case .likeStruct = parentDeclScopes.peek() else {
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
if let varDecl = node.parent?.parent?.parent?.as(VariableDeclSyntax.self) {
|
if let varDecl = node.parent?.parent?.parent?.as(VariableDeclSyntax.self) {
|
||||||
|
@ -208,14 +208,14 @@ private class Visitor: ViolationsSyntaxVisitor {
|
||||||
// Variable declaration is a computed property.
|
// Variable declaration is a computed property.
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
if case .handleReferences = variableDeclScopes.last {
|
if case .handleReferences = variableDeclScopes.peek() {
|
||||||
return .visitChildren
|
return .visitChildren
|
||||||
}
|
}
|
||||||
return .skipChildren
|
return .skipChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addViolation(on node: TokenSyntax) {
|
private func addViolation(on node: TokenSyntax) {
|
||||||
if let parentName = parentDeclScopes.last?.parentName, node.tokenKind == .identifier(parentName) {
|
if let parentName = parentDeclScopes.peek()?.parentName, node.tokenKind == .identifier(parentName) {
|
||||||
violations.append(node.positionAfterSkippingLeadingTrivia)
|
violations.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
corrections.append(
|
corrections.append(
|
||||||
(start: node.positionAfterSkippingLeadingTrivia, end: node.endPositionBeforeTrailingTrivia)
|
(start: node.positionAfterSkippingLeadingTrivia, end: node.endPositionBeforeTrailingTrivia)
|
||||||
|
|
Loading…
Reference in New Issue