Sources/SwiftCodeSanKit/Core/DeclVisitor.swift (127 lines of code) (raw):

// // Copyright (c) 2018. Uber Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation import SwiftSyntax /** Visit decls in source code being parsed */ final class DeclVisitor: SyntaxVisitor { var declMap = DeclMap() let path: String let module: String let topDeclsOnly: Bool let whitelistPath: Bool let whitelist: Whitelist? var importedModules = [String]() init(_ path: String, module: String?, topDeclsOnly: Bool, whitelistPath: Bool, whitelist: Whitelist?) { self.whitelist = whitelist self.whitelistPath = whitelistPath self.path = path self.module = module ?? "" self.topDeclsOnly = topDeclsOnly } override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members) return .skipChildren } override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { if node.attributesDescription.contains(String.propertyWrapper) { return .skipChildren } updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members) return .visitChildren } override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { if node.attributesDescription.contains(String.propertyWrapper) { return .skipChildren } updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members) return .skipChildren } override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members) return .skipChildren } override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members) return .skipChildren } override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind { importedModules.append(node.path.description.trimmed) return .visitChildren } override func visit(_ node: CodeBlockItemSyntax) -> SyntaxVisitorContinueKind { if let item = node.item.as(FunctionDeclSyntax.self) { updateDecl(item, description: item.description, members: nil) return .skipChildren } else if let _ = node.item.as(OperatorDeclSyntax.self) { return .skipChildren } else if let item = node.item.as(VariableDeclSyntax.self) { updateDecl(item, description: item.description, members: nil) return .skipChildren } else if let item = node.item.as(TypealiasDeclSyntax.self) { updateDecl(item, description: item.description, members: nil) return .skipChildren } return .visitChildren } private func memberDecls(_ decl: DeclSyntax, encloser: String, encloserDeclType: DeclType, encloserWhitelisted: Bool) -> [DeclMetadata] { let mdecls = decl.declMetadatas(path: path, module: module, encloser: encloser, description: decl.description, imports: importedModules) for mdecl in mdecls { if encloserDeclType == .extensionType { mdecl.isExtensionMember = true } let memberWhitelisted = whitelist?.declWhitelisted(name: mdecl.name, isMember: true, module: nil, parents: nil, path: mdecl.path) ?? false if encloserWhitelisted || memberWhitelisted || mdecl.declType == .initType || mdecl.declType == .subscriptType || mdecl.declType == .operatorType { if mdecl.isPublicOrOpen { mdecl.shouldExpose = true } mdecl.used = true } } return mdecls } private func updateDecl(_ item: DeclProtocol, description: String, members: MemberDeclListSyntax?) { let decls = item.declMetadatas(path: path, module: module, encloser: "", description: description, imports: importedModules) for decl in decls { var shouldWhitelist = (decl.declType == .operatorType) if !shouldWhitelist, !decl.name.isEmpty { if let whitelist = whitelist, whitelist.declWhitelisted(name: decl.name, isMember: false, module: module, parents: decl.inheritedTypes, path: decl.path) { // whitelisted so don't add to declMap shouldWhitelist = true } } if shouldWhitelist { if decl.isPublicOrOpen { decl.shouldExpose = true } decl.used = true } if let members = members { var list = [DeclMetadata]() for m in members { if let ifconfig = m.decl.as(IfConfigDeclSyntax.self) { for clause in ifconfig.clauses { if let clauseMembers = clause.elements.as(MemberDeclListSyntax.self) { for el in clauseMembers { let mdecls = memberDecls(el.decl, encloser: decl.name, encloserDeclType: decl.declType, encloserWhitelisted: shouldWhitelist) list.append(contentsOf: mdecls) } } } } else { let mdecls = memberDecls(m.decl, encloser: decl.name, encloserDeclType: decl.declType, encloserWhitelisted: shouldWhitelist) list.append(contentsOf: mdecls) } } decl.members = list } if !decl.name.isEmpty, declMap[decl.name] == nil { declMap[decl.name] = [] } declMap[decl.name]?.append(decl) } } }