diff --git a/CHANGELOG.md b/CHANGELOG.md index e760d8671e..c3bd45e970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,10 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#1714](https://github.com/realm/SwiftLint/issues/1714) +* Make `file_header` rule ignore doc comments. + [Marcelo Fabri](https://github.com/marcelofabri) + [#1719](https://github.com/realm/SwiftLint/issues/1719) + ##### Bug Fixes * Fix false positive on `redundant_discardable_let` rule when using diff --git a/Source/SwiftLintFramework/Rules/FileHeaderRule.swift b/Source/SwiftLintFramework/Rules/FileHeaderRule.swift index 183b1d4867..0d032c11d0 100644 --- a/Source/SwiftLintFramework/Rules/FileHeaderRule.swift +++ b/Source/SwiftLintFramework/Rules/FileHeaderRule.swift @@ -42,7 +42,7 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule { var lastToken: SyntaxToken? for token in file.syntaxTokensByLines.joined() { - guard let kind = SyntaxKind(rawValue: token.type), kind.isCommentLike else { + guard let kind = SyntaxKind(rawValue: token.type), kind.isFileHeaderKind else { // found a token that is not a comment, which means it's not the top of the file // so we can just skip the remaining tokens break @@ -111,3 +111,9 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule { } } } + +private extension SyntaxKind { + var isFileHeaderKind: Bool { + return self == .comment || self == .commentURL + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 6233406b95..9842f93996 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -123,8 +123,10 @@ extension FileHeaderRuleTests { ("testFileHeaderWithDefaultConfiguration", testFileHeaderWithDefaultConfiguration), ("testFileHeaderWithRequiredString", testFileHeaderWithRequiredString), ("testFileHeaderWithRequiredPattern", testFileHeaderWithRequiredPattern), + ("testFileHeaderWithRequiredStringAndURLComment", testFileHeaderWithRequiredStringAndURLComment), ("testFileHeaderWithForbiddenString", testFileHeaderWithForbiddenString), - ("testFileHeaderWithForbiddenPattern", testFileHeaderWithForbiddenPattern) + ("testFileHeaderWithForbiddenPattern", testFileHeaderWithForbiddenPattern), + ("testFileHeaderWithForbiddenPatternAndDocComment", testFileHeaderWithForbiddenPatternAndDocComment) ] } diff --git a/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift b/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift index c6598c0a87..e1e1683cbb 100644 --- a/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift +++ b/Tests/SwiftLintFrameworkTests/FileHeaderRuleTests.swift @@ -18,13 +18,13 @@ class FileHeaderRuleTests: XCTestCase { func testFileHeaderWithRequiredString() { let nonTriggeringExamples = [ "// **Header", - "//\n // **Header" + "//\n// **Header" ] let triggeringExamples = [ "↓// Copyright\n", "let foo = \"**Header\"", "let foo = 2 // **Header", - "let foo = 2\n // **Header", + "let foo = 2\n// **Header", "let foo = 2 // **Header" ] let description = FileHeaderRule.description @@ -39,7 +39,7 @@ class FileHeaderRuleTests: XCTestCase { func testFileHeaderWithRequiredPattern() { let nonTriggeringExamples = [ "// Copyright © 2016 Realm", - "//\n // Copyright © 2016 Realm" + "//\n// Copyright © 2016 Realm" ] let triggeringExamples = [ "↓// Copyright\n", @@ -54,17 +54,33 @@ class FileHeaderRuleTests: XCTestCase { stringDoesntViolate: false, skipCommentTests: true, testMultiByteOffsets: false) } + func testFileHeaderWithRequiredStringAndURLComment() { + let nonTriggeringExamples = [ + "/* Check this url: https://github.com/realm/SwiftLint */" + ] + let triggeringExamples = [ + "/* Check this url: https://github.com/apple/swift */" + ] + let description = FileHeaderRule.description + .with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + + let config = ["required_string": "/* Check this url: https://github.com/realm/SwiftLint */"] + verifyRule(description, ruleConfiguration: config, + stringDoesntViolate: false, skipCommentTests: true, testMultiByteOffsets: false) + } + func testFileHeaderWithForbiddenString() { let nonTriggeringExamples = [ "// Copyright\n", "let foo = \"**All rights reserved.\"", "let foo = 2 // **All rights reserved.", - "let foo = 2\n // **All rights reserved.", + "let foo = 2\n// **All rights reserved.", "let foo = 2 // **All rights reserved." ] let triggeringExamples = [ "// ↓**All rights reserved.", - "//\n // ↓**All rights reserved." + "//\n// ↓**All rights reserved." ] let description = FileHeaderRule.description .with(nonTriggeringExamples: nonTriggeringExamples) @@ -84,7 +100,7 @@ class FileHeaderRuleTests: XCTestCase { ] let triggeringExamples = [ "//↓ FileHeaderRuleTests.swift", - "//\n //↓ FileHeaderRuleTests.swift" + "//\n//↓ FileHeaderRuleTests.swift" ] let description = FileHeaderRule.description .with(nonTriggeringExamples: nonTriggeringExamples) @@ -93,4 +109,21 @@ class FileHeaderRuleTests: XCTestCase { verifyRule(description, ruleConfiguration: ["forbidden_pattern": "\\s\\w+\\.swift"], skipCommentTests: true) } + + func testFileHeaderWithForbiddenPatternAndDocComment() { + let nonTriggeringExamples = [ + "/// This is great tool.\nclass GreatTool {}", + "class GreatTool {}" + ] + let triggeringExamples = [ + "↓// FileHeaderRuleTests.swift", + "↓//\n// FileHeaderRuleTests.swift" + ] + let description = FileHeaderRule.description + .with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + + verifyRule(description, ruleConfiguration: ["forbidden_pattern": ".?"], + skipCommentTests: true, skipDisableCommandTests: true, testMultiByteOffsets: false) + } } diff --git a/Tests/SwiftLintFrameworkTests/TestHelpers.swift b/Tests/SwiftLintFrameworkTests/TestHelpers.swift index f132e0fb59..922a567d9a 100644 --- a/Tests/SwiftLintFrameworkTests/TestHelpers.swift +++ b/Tests/SwiftLintFrameworkTests/TestHelpers.swift @@ -147,6 +147,7 @@ extension XCTestCase { stringDoesntViolate: Bool = true, skipCommentTests: Bool = false, skipStringTests: Bool = false, + skipDisableCommandTests: Bool = false, testMultiByteOffsets: Bool = true, testShebang: Bool = true) { guard let config = makeConfig(ruleConfiguration, ruleDescription.identifier) else { @@ -184,7 +185,12 @@ extension XCTestCase { ) } - let disableCommands = ruleDescription.allIdentifiers.map { "// swiftlint:disable \($0)\n" } + let disableCommands: [String] + if skipDisableCommandTests { + disableCommands = [] + } else { + disableCommands = ruleDescription.allIdentifiers.map { "// swiftlint:disable \($0)\n" } + } // "disable" commands doesn't violate for command in disableCommands {