From ba9ec9ed6e703e59f17e08d0c5bf95d0b4f04391 Mon Sep 17 00:00:00 2001 From: theamodhshetty Date: Wed, 25 Mar 2026 08:14:13 +0530 Subject: [PATCH] fix(prefer_self): skip shadowed nested type refs --- CHANGELOG.md | 5 ++ .../PreferSelfInStaticReferencesRule.swift | 68 +++++++++++++++---- ...erSelfInStaticReferencesRuleExamples.swift | 6 ++ 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 612e577b77..56718f7429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#6466](https://github.com/realm/SwiftLint/issues/6466) +* Avoid false positives in `prefer_self_in_static_references` when a nested type + shadows its enclosing type name. + [theamodhshetty](https://github.com/theamodhshetty) + [#5917](https://github.com/realm/SwiftLint/issues/5917) + ## 0.63.2: High-Speed Extraction ### Breaking diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift index fd65e1e32b..2b2aee5241 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift @@ -37,15 +37,16 @@ private extension PreferSelfInStaticReferencesRule { final class Visitor: ViolationsSyntaxVisitor { private var parentDeclScopes = Stack() + private var shadowingNestedTypeScopes = Stack() private var variableDeclScopes = Stack() override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeClass(name: node.name.text)) + pushParentDeclScope(.likeClass(name: node.name.text), for: node.name.text, memberBlock: node.memberBlock) return .skipChildren } override func visitPost(_: ActorDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_: AttributeSyntax) -> SyntaxVisitorContinueKind { @@ -56,12 +57,12 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeClass(name: node.name.text)) + pushParentDeclScope(.likeClass(name: node.name.text), for: node.name.text, memberBlock: node.memberBlock) return .visitChildren } override func visitPost(_: ClassDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_: CodeBlockItemListSyntax) -> SyntaxVisitorContinueKind { @@ -74,21 +75,21 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeStruct(node.name.text)) + pushParentDeclScope(.likeStruct(node.name.text), for: node.name.text, memberBlock: node.memberBlock) return .visitChildren } override func visitPost(_: EnumDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.skipReferences) + pushParentDeclScope(.skipReferences) return .visitChildren } override func visitPost(_: ExtensionDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind { @@ -146,12 +147,12 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.skipReferences) + pushParentDeclScope(.skipReferences) return .skipChildren } override func visitPost(_: ProtocolDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_: ReturnClauseSyntax) -> SyntaxVisitorContinueKind { @@ -162,12 +163,12 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeStruct(node.name.text)) + pushParentDeclScope(.likeStruct(node.name.text), for: node.name.text, memberBlock: node.memberBlock) return .visitChildren } override func visitPost(_: StructDeclSyntax) { - parentDeclScopes.pop() + popParentDeclScope() } override func visit(_: GenericArgumentListSyntax) -> SyntaxVisitorContinueKind { @@ -213,7 +214,9 @@ private extension PreferSelfInStaticReferencesRule { } private func addViolation(on node: TokenSyntax) { - if let parentName = parentDeclScopes.peek()?.parentName, node.tokenKind == .identifier(parentName) { + if shadowingNestedTypeScopes.peek() != true, + let parentName = parentDeclScopes.peek()?.parentName, + node.tokenKind == .identifier(parentName) { violations.append( at: node.positionAfterSkippingLeadingTrivia, correction: .init( @@ -224,5 +227,44 @@ private extension PreferSelfInStaticReferencesRule { ) } } + + private func pushParentDeclScope( + _ behavior: ParentDeclBehavior, + for name: String? = nil, + memberBlock: MemberBlockSyntax? = nil + ) { + parentDeclScopes.push(behavior) + let hasShadowingNestedType: Bool + if let name, let memberBlock { + hasShadowingNestedType = containsSameNamedNestedType(named: name, in: memberBlock) + } else { + hasShadowingNestedType = false + } + shadowingNestedTypeScopes.push(hasShadowingNestedType) + } + + private func popParentDeclScope() { + parentDeclScopes.pop() + shadowingNestedTypeScopes.pop() + } + + private func containsSameNamedNestedType(named name: String, in memberBlock: MemberBlockSyntax) -> Bool { + memberBlock.members.contains { member in + if let actor = member.decl.as(ActorDeclSyntax.self) { + return actor.name.text == name + } + if let classDecl = member.decl.as(ClassDeclSyntax.self) { + return classDecl.name.text == name + } + if let enumDecl = member.decl.as(EnumDeclSyntax.self) { + return enumDecl.name.text == name + } + if let structDecl = member.decl.as(StructDeclSyntax.self) { + return structDecl.name.text == name + } + + return false + } + } } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift index 668e3e5627..7e04e7464c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift @@ -101,6 +101,12 @@ enum PreferSelfInStaticReferencesRuleExamples { } } """, excludeFromDocumentation: true), + Example(""" + struct S1 { + struct S1 {} + var s = S1() + } + """, excludeFromDocumentation: true), ] static let triggeringExamples = [