diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f8bc7a12..e9e161fb06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,11 @@ pattern matching (e.g. if case, switch case, for case, catch). [GandaLF2006](https://github.com/GandaLF2006) +* 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..ea26f110c5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRule.swift @@ -40,7 +40,7 @@ private extension PreferSelfInStaticReferencesRule { private var variableDeclScopes = Stack() override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeClass(name: node.name.text)) + pushParentDeclScope(.likeClass(name: node.name.text), memberBlock: node.memberBlock) return .skipChildren } @@ -56,7 +56,7 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeClass(name: node.name.text)) + pushParentDeclScope(.likeClass(name: node.name.text), memberBlock: node.memberBlock) return .visitChildren } @@ -74,7 +74,7 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeStruct(node.name.text)) + pushParentDeclScope(.likeStruct(node.name.text), memberBlock: node.memberBlock) return .visitChildren } @@ -162,7 +162,7 @@ private extension PreferSelfInStaticReferencesRule { } override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { - parentDeclScopes.push(.likeStruct(node.name.text)) + pushParentDeclScope(.likeStruct(node.name.text), memberBlock: node.memberBlock) return .visitChildren } @@ -213,7 +213,8 @@ private extension PreferSelfInStaticReferencesRule { } private func addViolation(on node: TokenSyntax) { - if let parentName = parentDeclScopes.peek()?.parentName, node.tokenKind == .identifier(parentName) { + if let parentName = parentDeclScopes.peek()?.parentName, + node.tokenKind == .identifier(parentName) { violations.append( at: node.positionAfterSkippingLeadingTrivia, correction: .init( @@ -224,5 +225,25 @@ private extension PreferSelfInStaticReferencesRule { ) } } + + private func pushParentDeclScope(_ behavior: ParentDeclBehavior, memberBlock: MemberBlockSyntax) { + let hasShadowingNestedType = + if let name = behavior.parentName { + containsSameNamedNestedType(named: name, in: memberBlock) + } else { + false + } + parentDeclScopes.push(hasShadowingNestedType ? .skipReferences : behavior) + } + + private func containsSameNamedNestedType(named name: String, in memberBlock: MemberBlockSyntax) -> Bool { + memberBlock.members.contains { member in + if member.decl.isProtocol((any DeclGroupSyntax).self) || member.decl.is(TypeAliasDeclSyntax.self) { + return member.decl.asProtocol((any NamedDeclSyntax).self)?.name.text == name + } + + return false + } + } } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift index 668e3e5627..734cd8b210 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfInStaticReferencesRuleExamples.swift @@ -101,6 +101,18 @@ enum PreferSelfInStaticReferencesRuleExamples { } } """, excludeFromDocumentation: true), + Example(""" + struct S1 { + struct S1 {} + var s = S1() + } + """, excludeFromDocumentation: true), + Example(""" + struct S1 { + var s = S1() + struct S1 {} + } + """, excludeFromDocumentation: true), ] static let triggeringExamples = [