Skip to content

Commit

Permalink
Merge pull request #222 from dylansturg/accurate_decltypes
Browse files Browse the repository at this point in the history
Specify the type of decl to rename in AlwaysUseLowerCamelCase.
  • Loading branch information
allevato committed Sep 17, 2020
1 parent 9080762 commit 97628a8
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 24 deletions.
38 changes: 31 additions & 7 deletions Sources/SwiftFormatRules/AlwaysUseLowerCamelCase.swift
Expand Up @@ -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
}
Expand All @@ -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<NodeType: SyntaxProtocol>(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 {
Expand All @@ -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")
}
}
53 changes: 36 additions & 17 deletions Tests/SwiftFormatRulesTests/AlwaysUseLowerCamelCaseTests.swift
Expand Up @@ -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() {
Expand All @@ -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"))
}
}

0 comments on commit 97628a8

Please sign in to comment.