From 97628a8b0068fd0977ce50915c36f3dee8c5426a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 18 Aug 2020 12:07:16 -0700 Subject: [PATCH] Merge pull request #222 from dylansturg/accurate_decltypes Specify the type of decl to rename in AlwaysUseLowerCamelCase. --- .../AlwaysUseLowerCamelCase.swift | 38 ++++++++++--- .../AlwaysUseLowerCamelCaseTests.swift | 53 +++++++++++++------ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift b/Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift index aef1eb3f..f88f6868 100644 --- a/Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift +++ b/Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift @@ -55,7 +55,8 @@ public final class AlwaysUseLowerCamelCase: SyntaxLintRule { guard let pat = binding.pattern.as(IdentifierPatternSyntax.self) else { continue } - diagnoseLowerCamelCaseViolations(pat.identifier, allowUnderscores: false) + diagnoseLowerCamelCaseViolations( + pat.identifier, allowUnderscores: false, description: identifierDescription(for: node)) } return .skipChildren } @@ -64,26 +65,47 @@ public final class AlwaysUseLowerCamelCase: SyntaxLintRule { // We allow underscores in test names, because there's an existing convention of using // underscores to separate phrases in very detailed test names. let allowUnderscores = testCaseFuncs.contains(node) - diagnoseLowerCamelCaseViolations(node.identifier, allowUnderscores: allowUnderscores) + diagnoseLowerCamelCaseViolations( + node.identifier, allowUnderscores: allowUnderscores, + description: identifierDescription(for: node)) return .skipChildren } public override func visit(_ node: EnumCaseElementSyntax) -> SyntaxVisitorContinueKind { - diagnoseLowerCamelCaseViolations(node.identifier, allowUnderscores: false) + diagnoseLowerCamelCaseViolations( + node.identifier, allowUnderscores: false, description: identifierDescription(for: node)) return .skipChildren } - private func diagnoseLowerCamelCaseViolations(_ identifier: TokenSyntax, allowUnderscores: Bool) { + private func diagnoseLowerCamelCaseViolations( + _ identifier: TokenSyntax, allowUnderscores: Bool, description: String + ) { guard case .identifier(let text) = identifier.tokenKind else { return } if text.isEmpty { return } if (text.dropFirst().contains("_") && !allowUnderscores) || ("A"..."Z").contains(text.first!) { - diagnose(.variableNameMustBeLowerCamelCase(text), on: identifier) { + diagnose(.nameMustBeLowerCamelCase(text, description: description), on: identifier) { $0.highlight(identifier.sourceRange(converter: self.context.sourceLocationConverter)) } } } } +/// Returns a human readable description of the node type that can be used to describe the +/// identifier of the node in diagnostics from this rule. +/// +/// - Parameter node: A node whose identifier may be used in diagnostics. +/// - Returns: A human readable description of the node and its identifier. +fileprivate func identifierDescription(for node: NodeType) -> String { + switch Syntax(node).as(SyntaxEnum.self) { + case .enumCaseElement: return "enum case" + case .functionDecl: return "function" + case .variableDecl(let variableDecl): + return variableDecl.letOrVarKeyword.tokenKind == .varKeyword ? "variable" : "constant" + default: + return "identifier" + } +} + extension ReturnClauseSyntax { /// Whether this return clause specifies an explicit `Void` return type. fileprivate var isVoid: Bool { @@ -98,7 +120,9 @@ extension ReturnClauseSyntax { } extension Diagnostic.Message { - public static func variableNameMustBeLowerCamelCase(_ name: String) -> Diagnostic.Message { - return .init(.warning, "rename variable '\(name)' using lower-camel-case") + public static func nameMustBeLowerCamelCase( + _ name: String, description: String + ) -> Diagnostic.Message { + return .init(.warning, "rename \(description) '\(name)' using lower-camel-case") } } diff --git a/Tests/SwiftFormatRulesTests/AlwaysUseLowerCamelCaseTests.swift b/Tests/SwiftFormatRulesTests/AlwaysUseLowerCamelCaseTests.swift index f288d18d..3e36c332 100644 --- a/Tests/SwiftFormatRulesTests/AlwaysUseLowerCamelCaseTests.swift +++ b/Tests/SwiftFormatRulesTests/AlwaysUseLowerCamelCaseTests.swift @@ -19,16 +19,26 @@ final class AlwaysUseLowerCamelCaseTests: LintOrFormatRuleTestCase { class UnitTests: XCTestCase { func test_HappyPath_Through_GoodCode() {} } + enum FooBarCases { + case UpperCamelCase + case lowerCamelCase + } """ performLint(AlwaysUseLowerCamelCase.self, input: input) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("Test"), line: 1, column: 5) - XCTAssertNotDiagnosed(.variableNameMustBeLowerCamelCase("foo")) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("bad_name"), line: 3, column: 5) - XCTAssertNotDiagnosed(.variableNameMustBeLowerCamelCase("_okayName")) - XCTAssertNotDiagnosed(.variableNameMustBeLowerCamelCase("Foo")) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("FooFunc"), line: 6, column: 8) XCTAssertDiagnosed( - .variableNameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode"), line: 9, column: 8) + .nameMustBeLowerCamelCase("Test", description: "constant"), line: 1, column: 5) + XCTAssertNotDiagnosed(.nameMustBeLowerCamelCase("foo", description: "variable")) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("bad_name", description: "variable"), line: 3, column: 5) + XCTAssertNotDiagnosed(.nameMustBeLowerCamelCase("_okayName", description: "variable")) + XCTAssertNotDiagnosed(.nameMustBeLowerCamelCase("Foo", description: "struct")) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("FooFunc", description: "function"), line: 6, column: 8) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode", description: "function"), + line: 9, column: 8) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("UpperCamelCase", description: "enum case"), line: 12, column: 8) } func testIgnoresUnderscoresInTestNames() { @@ -50,21 +60,30 @@ final class AlwaysUseLowerCamelCaseTests: LintOrFormatRuleTestCase { } """ performLint(AlwaysUseLowerCamelCase.self, input: input) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("Test"), line: 3, column: 5) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("My_Constant_Value"), line: 5, column: 14) - XCTAssertNotDiagnosed(.variableNameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode")) - XCTAssertDiagnosed(.variableNameMustBeLowerCamelCase("FooFunc"), line: 7, column: 16) XCTAssertDiagnosed( - .variableNameMustBeLowerCamelCase("helperFunc_For_HappyPath_Setup"), line: 8, column: 16) + .nameMustBeLowerCamelCase("Test", description: "constant"), line: 3, column: 5) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("My_Constant_Value", description: "constant"), line: 5, column: 14) + XCTAssertNotDiagnosed( + .nameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode", description: "function")) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("FooFunc", description: "function"), line: 7, column: 16) + XCTAssertDiagnosed( + .nameMustBeLowerCamelCase("helperFunc_For_HappyPath_Setup", description: "function"), + line: 8, column: 16) XCTAssertDiagnosed( - .variableNameMustBeLowerCamelCase("testLikeMethod_With_Underscores"), line: 9, column: 16) + .nameMustBeLowerCamelCase("testLikeMethod_With_Underscores", description: "function"), + line: 9, column: 16) XCTAssertDiagnosed( - .variableNameMustBeLowerCamelCase("testLikeMethod_With_Underscores2"), line: 10, column: 16) + .nameMustBeLowerCamelCase("testLikeMethod_With_Underscores2", description: "function"), + line: 10, column: 16) XCTAssertNotDiagnosed( - .variableNameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode_ReturnsVoid")) + .nameMustBeLowerCamelCase( + "test_HappyPath_Through_GoodCode_ReturnsVoid", description: "function")) XCTAssertNotDiagnosed( - .variableNameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode_ReturnsShortVoid")) + .nameMustBeLowerCamelCase( + "test_HappyPath_Through_GoodCode_ReturnsShortVoid", description: "function")) XCTAssertNotDiagnosed( - .variableNameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode_Throws")) + .nameMustBeLowerCamelCase("test_HappyPath_Through_GoodCode_Throws", description: "function")) } }