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

Avoid to generate duplicated variable name #231

Merged
merged 15 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
33 changes: 29 additions & 4 deletions Sources/MockoloFramework/Models/ClassModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class ClassModel: Model {
let identifier: String
let declType: DeclType
let entities: [(String, Model)]
let initParamCandidates: [Model]
let initParamCandidates: [VariableModel]
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
let declaredInits: [MethodModel]
let metadata: AnnotationMetadata?

Expand All @@ -39,7 +39,7 @@ final class ClassModel: Model {
attributes: [String],
offset: Int64,
metadata: AnnotationMetadata?,
initParamCandidates: [Model],
initParamCandidates: [VariableModel],
declaredInits: [MethodModel],
entities: [(String, Model)]) {
self.identifier = identifier
Expand All @@ -55,7 +55,32 @@ final class ClassModel: Model {
self.accessLevel = acl
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool, useMockObservable: Bool, allowSetCallCount: Bool = false, mockFinal: Bool = false, enableFuncArgsHistory: Bool = false, disableCombineDefaultValues: Bool = false) -> String? {
return applyClassTemplate(name: name, identifier: self.identifier, accessLevel: accessLevel, attribute: attribute, declType: declType, metadata: metadata, useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable, allowSetCallCount: allowSetCallCount, mockFinal: mockFinal, enableFuncArgsHistory: enableFuncArgsHistory, disableCombineDefaultValues: disableCombineDefaultValues, initParamCandidates: initParamCandidates, declaredInits: declaredInits, entities: entities)
func render(
with identifier: String,
encloser: String,
useTemplateFunc: Bool,
useMockObservable: Bool,
allowSetCallCount: Bool = false,
mockFinal: Bool = false,
enableFuncArgsHistory: Bool = false,
disableCombineDefaultValues: Bool = false
) -> String? {
return applyClassTemplate(
name: name,
identifier: self.identifier,
accessLevel: accessLevel,
attribute: attribute,
declType: declType,
metadata: metadata,
useTemplateFunc: useTemplateFunc,
useMockObservable: useMockObservable,
allowSetCallCount: allowSetCallCount,
mockFinal: mockFinal,
enableFuncArgsHistory: enableFuncArgsHistory,
disableCombineDefaultValues: disableCombineDefaultValues,
initParamCandidates: initParamCandidates,
declaredInits: declaredInits,
entities: entities
)
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
}
}
33 changes: 30 additions & 3 deletions Sources/MockoloFramework/Models/ParamModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,37 @@ final class ParamModel: Model {
var underlyingName: String {
return "_\(name)"
}

var asVarDecl: String? {

/// - Parameters:
/// - eraseType:
/// If other initializers in decl has same name as this param and type is different from each other,
/// please pass `True` to this parameter. Default value is `false`.
///
/// ```
/// protocol A {
/// init(param: String)
/// init(param: any Sequence<Character>)
/// }
/// class B: A {
/// var param: Any! // NOTE: type erasing
/// init () {}
/// required init(param: String) {
/// self.param = param
/// }
/// required init(param: any Sequence<Character>) {
/// self.param = param
/// }
/// }
/// ```
func asInitVarDecl(eraseType: Bool) -> String? {
if self.inInit, self.needVarDecl {
return applyVarTemplate(name: name, type: type)
let type: `Type`
if eraseType {
type = Type(.any)
} else {
type = self.type
}
return applyVarTemplate(type: type)
}
return nil
}
Expand Down
8 changes: 5 additions & 3 deletions Sources/MockoloFramework/Models/ParsedEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ struct ResolvedEntity {
return declaredInits.map { $0.params }.flatMap{$0}
}

var initParamCandidates: [Model] {
return sortedInitVars(in: uniqueModels.map{$0.1})
var initParamCandidates: [VariableModel] {
return sortedInitVars(
in: uniqueModels.compactMap{ $0.1 as? VariableModel }
)
}

/// Returns models that can be used as parameters to an initializer
/// @param models The models of the current entity including unprocessed (ones to generate) and
/// processed (already mocked by a previous run if any) models.
/// @returns A list of init parameter models
private func sortedInitVars(`in` models: [Model]) -> [Model] {
private func sortedInitVars(`in` models: [VariableModel]) -> [VariableModel] {
let processed = models.filter {$0.processed && $0.canBeInitParam}
let unprocessed = models.filter {!$0.processed && $0.canBeInitParam}

Expand Down
54 changes: 41 additions & 13 deletions Sources/MockoloFramework/Templates/ClassTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension ClassModel {
mockFinal: Bool,
enableFuncArgsHistory: Bool,
disableCombineDefaultValues: Bool,
initParamCandidates: [Model],
initParamCandidates: [VariableModel],
declaredInits: [MethodModel],
entities: [(String, Model)]) -> String {

Expand Down Expand Up @@ -101,14 +101,16 @@ extension ClassModel {
return template
}

private func extraInitsIfNeeded(initParamCandidates: [Model],
declaredInits: [MethodModel],
acl: String,
declType: DeclType,
overrides: [String: String]?) -> String {
private func extraInitsIfNeeded(
initParamCandidates: [VariableModel],
declaredInits: [MethodModel],
acl: String,
declType: DeclType,
overrides: [String: String]?
) -> String {

let declaredInitParamsPerInit = declaredInits.map { $0.params }

var needParamedInit = false
var needBlankInit = false

Expand Down Expand Up @@ -169,13 +171,23 @@ extension ClassModel {
}

let extraInitParamNames = initParamCandidates.map{$0.name}
let extraVarsToDecl = declaredInitParamsPerInit.flatMap{$0}.compactMap { (p: ParamModel) -> String? in
if !extraInitParamNames.contains(p.name) {
return p.asVarDecl
var processed = Set<String>()
let extraVarsToDecl = declaredInitParamsPerInit
.flatMap { $0 }
.compactMap { (p: ParamModel) -> String? in
if !extraInitParamNames.contains(p.name), !processed.contains(p.name) {
processed.insert(p.name)
let shouldEraseType = declaredInitParamsPerInit
.flatMap { $0 }
.checkHasConflictedParam(
name: p.name,
typeName: p.type.typeName
)
return p.asInitVarDecl(eraseType: shouldEraseType)
}
return nil
}
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
.joined(separator: "\n")
.joined(separator: "\n")

let declaredInitStr = declaredInits.compactMap { (m: MethodModel) -> String? in
if case let .initKind(required, override) = m.kind, !m.processed {
Expand Down Expand Up @@ -293,3 +305,19 @@ extension ClassModel {
}
}
}

fileprivate extension Array where Element == ParamModel {
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
func findParams(name: String) -> [ParamModel] {
filter {
$0.name == name
}
}

/// Check if there is same parameter name with different typeName.
func checkHasConflictedParam(name: String, typeName: String) -> Bool {
let sameParams = findParams(
name: name
)
return !sameParams.allSatisfy { $0.type.typeName == typeName }
}
}
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 2 additions & 3 deletions Sources/MockoloFramework/Templates/ParamTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ extension ParamModel {
}
return result
}

func applyVarTemplate(name: String,
type: Type) -> String {

func applyVarTemplate(type: `Type`) -> String {
assert(!type.isUnknown)
let vardecl = "\(1.tab)private var \(underlyingName): \(type.underlyingType)"
return vardecl
Expand Down
2 changes: 1 addition & 1 deletion Sources/MockoloFramework/Utils/TypeParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fileprivate var validIdentifierChars: CharacterSet = {
return valid
}()

public final class Type {
public final class `Type` {
let typeName: String
let cast: String?
var cachedDefaultVal: String?
Expand Down
58 changes: 58 additions & 0 deletions Tests/TestInit/FixtureInit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,61 @@ public class ForcastUpdatingMock: ForcastUpdating {
}
"""

let initWithSameParamNameButDifferentType = """
/// \(String.mockAnnotation)
protocol MyProtocol {
init(param: Any)
init(param: String)
init(param: [Character])
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
}
"""

let initWithSameParamNameButDifferentTypeMock = """



class MyProtocolMock: MyProtocol {
private var _param: Any!
init() { }
required init(param: Any) {
self._param = param
}
required init(param: String = "") {
self._param = param
}
required init(param: [Character] = [Character]()) {
fummicc1 marked this conversation as resolved.
Show resolved Hide resolved
self._param = param
}


}
"""

let multipleInitsWithSameParamName = """
/// \(String.mockAnnotation)
protocol MyProtocol {
init(param: String, anotherParam: Int)
init(param: String, anotherParam: String)
}
"""

let multipleInitsWithSameParamNameMock = """



class MyProtocolMock: MyProtocol {
private var _param: String!
private var _anotherParam: Any!
init() { }
required init(param: String = "", anotherParam: Int = 0) {
self._param = param
self._anotherParam = anotherParam
}
required init(param: String = "", anotherParam: String = "") {
self._param = param
self._anotherParam = anotherParam
}


}
"""
14 changes: 14 additions & 0 deletions Tests/TestInit/InitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,18 @@ class InitTests: MockoloTestCase {
verify(srcContent: keywordParams,
dstContent: keywordParamsMock)
}

func testInitiWithSameParamName() {
verify(
srcContent: multipleInitsWithSameParamName,
dstContent: multipleInitsWithSameParamNameMock
)
}

func testInitiWithSameParamNameButDifferentType() {
verify(
srcContent: initWithSameParamNameButDifferentType,
dstContent: initWithSameParamNameButDifferentTypeMock
)
}
}