Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for pretty print categories in rule registry generation #965

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/RuleDocumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Use the rules below in the `rules` block of your `.swift-format`
configuration file, as described in
[Configuration](Configuration.md). All of these rules can be
[Configuration](Documentation/Configuration.md). All of these rules can be
applied in the linter, but only some of them can format your source code
automatically.

Expand Down
9 changes: 9 additions & 0 deletions Sources/SwiftFormat/Core/RuleRegistry+Generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,14 @@
"UseTripleSlashForDocumentationComments": true,
"UseWhereClausesInForLoops": false,
"ValidateDocumentationComments": false,
"AddLines": true,
"EndOfLineComment": true,
"Indentation": true,
"LineLength": true,
"RemoveLine": true,
"Spacing": true,
"SpacingCharacter": true,
"TrailingComma": true,
"TrailingWhitespace": true,
]
}
98 changes: 98 additions & 0 deletions Sources/generate-swift-format/PrettyPrintCollector.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation
@_spi(Rules) import SwiftFormat
import SwiftParser
import SwiftSyntax

/// Collects information about rules in the formatter code base.
final class PrettyPrintCollector {

/// A list of all the format-only pretty-print categories found in the code base.
var allPrettyPrinterCategories = Set<String>()

/// Populates the internal collections with rules in the given directory.
///
/// - Parameter url: The file system URL that should be scanned for rules.
func collect(from url: URL) throws {
// For each file in the Rules directory, find types that either conform to SyntaxLintRule or
// inherit from SyntaxFormatRule.
let fm = FileManager.default
guard let rulesEnumerator = fm.enumerator(atPath: url.path) else {
fatalError("Could not list the directory \(url.path)")
}

for baseName in rulesEnumerator {
// Ignore files that aren't Swift source files.
guard let baseName = baseName as? String, baseName.hasSuffix(".swift") else { continue }

let fileURL = url.appendingPathComponent(baseName)
let fileInput = try String(contentsOf: fileURL)
let sourceFile = Parser.parse(source: fileInput)

for statement in sourceFile.statements {
let pp = self.detectPrettyPrintCategories(at: statement)
allPrettyPrinterCategories.formUnion(pp)
}
}
}

private func detectPrettyPrintCategories(at statement: CodeBlockItemSyntax) -> [String] {
guard let enumDecl = statement.item.as(EnumDeclSyntax.self) else {
return []
}

// Make sure it has an inheritance clause.
guard let inheritanceClause = enumDecl.inheritanceClause else {
return []
}

// Scan through the inheritance clause to find one of the protocols/types we're interested in.
for inheritance in inheritanceClause.inheritedTypes {
guard let identifier = inheritance.type.as(IdentifierTypeSyntax.self) else {
continue
}

if identifier.name.text != "FindingCategorizing" {
// Keep looking at the other inheritances.
continue
}

// Now that we know it's a pretty printing category, collect the `description` method and extract the name.
for member in enumDecl.memberBlock.members {
guard let varDecl = member.decl.as(VariableDeclSyntax.self) else { continue }
guard
let descriptionDecl = varDecl.bindings
.first(where: {
$0.pattern.as(IdentifierPatternSyntax.self)?.identifier.text == "description"
})
else { continue }
let pp = PrettyPrintCategoryVisitor(viewMode: .sourceAccurate)
_ = pp.walk(descriptionDecl)
return pp.prettyPrintCategories
}
}

return []
}
}

final class PrettyPrintCategoryVisitor: SyntaxVisitor {

var prettyPrintCategories: [String] = []

override func visit(_ node: StringSegmentSyntax) -> SyntaxVisitorContinueKind {
prettyPrintCategories.append(node.content.text)
return .skipChildren
}
}
10 changes: 9 additions & 1 deletion Sources/generate-swift-format/RuleRegistryGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ final class RuleRegistryGenerator: FileGenerator {
/// The rules collected by scanning the formatter source code.
let ruleCollector: RuleCollector

/// The pretty-printing categories collected by scanning the formatter source code.
let prettyPrintCollector: PrettyPrintCollector

/// Creates a new rule registry generator.
init(ruleCollector: RuleCollector) {
init(ruleCollector: RuleCollector, prettyPrintCollector: PrettyPrintCollector) {
self.ruleCollector = ruleCollector
self.prettyPrintCollector = prettyPrintCollector
}

func write(into handle: FileHandle) throws {
Expand Down Expand Up @@ -49,6 +53,10 @@ final class RuleRegistryGenerator: FileGenerator {
for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) {
handle.write(" \"\(detectedRule.typeName)\": \(!detectedRule.isOptIn),\n")
}

for ppCategory in prettyPrintCollector.allPrettyPrinterCategories.sorted(by: { $0 < $1 }) {
handle.write(" \"\(ppCategory)\": true,\n")
}
handle.write(" ]\n}\n")
}
}
9 changes: 8 additions & 1 deletion Sources/generate-swift-format/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ let rulesDirectory =
sourcesDirectory
.appendingPathComponent("SwiftFormat")
.appendingPathComponent("Rules")
let prettyPrintDirectory =
sourcesDirectory
.appendingPathComponent("SwiftFormat")
.appendingPathComponent("PrettyPrint")
let pipelineFile =
sourcesDirectory
.appendingPathComponent("SwiftFormat")
Expand All @@ -46,12 +50,15 @@ let ruleDocumentationFile =
var ruleCollector = RuleCollector()
try ruleCollector.collect(from: rulesDirectory)

var prettyPrintCollector = PrettyPrintCollector()
try prettyPrintCollector.collect(from: prettyPrintDirectory)

// Generate a file with extensions for the lint and format pipelines.
let pipelineGenerator = PipelineGenerator(ruleCollector: ruleCollector)
try pipelineGenerator.generateFile(at: pipelineFile)

// Generate the rule registry dictionary for configuration.
let registryGenerator = RuleRegistryGenerator(ruleCollector: ruleCollector)
let registryGenerator = RuleRegistryGenerator(ruleCollector: ruleCollector, prettyPrintCollector: prettyPrintCollector)
try registryGenerator.generateFile(at: ruleRegistryFile)

// Generate the rule name cache.
Expand Down