mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Start importing factory methods as initializers.
When an Objective-C class method follows the naming convention of a factory method, i.e., its starting words match the ending words of the class name, import it as a convenience initializer when it also: - Returns instancetype (i.e., dynamic Self in Swift parlance) - Has no NSError** parameters, which indicate the potential for failures This is under a new flag (-enable-objc-factory-method-constructors) because it is not generally functional. However, this is a step toward <rdar://problem/16509024>. Swift SVN r16479
This commit is contained in:
@@ -48,8 +48,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Implementation &Impl;
|
Implementation &Impl;
|
||||||
|
|
||||||
ClangImporter(ASTContext &ctx, bool splitPrepositions,
|
ClangImporter(ASTContext &ctx, const ClangImporterOptions &clangImporterOpts);
|
||||||
bool implicitProperties);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// \brief Create a new Clang importer that can import a suitable Clang
|
/// \brief Create a new Clang importer that can import a suitable Clang
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ public:
|
|||||||
/// If true, matched getter-like and setter-like methods will be imported as
|
/// If true, matched getter-like and setter-like methods will be imported as
|
||||||
/// properties.
|
/// properties.
|
||||||
bool InferImplicitProperties = false;
|
bool InferImplicitProperties = false;
|
||||||
|
|
||||||
|
/// If true, import factory methods as constructors.
|
||||||
|
bool ImportFactoryMethodsAsConstructors = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace swift
|
} // end namespace swift
|
||||||
|
|||||||
@@ -122,7 +122,11 @@ def enable_experimental_patterns : Flag<["-"], "enable-experimental-patterns">,
|
|||||||
def enable_objc_implicit_properties :
|
def enable_objc_implicit_properties :
|
||||||
Flag<["-"], "enable-objc-implicit-properties">,
|
Flag<["-"], "enable-objc-implicit-properties">,
|
||||||
HelpText<"Import Objective-C \"implicit properties\" as properties">;
|
HelpText<"Import Objective-C \"implicit properties\" as properties">;
|
||||||
|
|
||||||
|
def enable_objc_factory_method_constructors :
|
||||||
|
Flag<["-"], "enable-objc-factory-method-constructors">,
|
||||||
|
HelpText<"Import Objective-C factory methods as constructors">;
|
||||||
|
|
||||||
def enable_source_import : Flag<["-"], "enable-source-import">,
|
def enable_source_import : Flag<["-"], "enable-source-import">,
|
||||||
HelpText<"Enable importing of Swift source files">;
|
HelpText<"Enable importing of Swift source files">;
|
||||||
|
|
||||||
|
|||||||
@@ -100,9 +100,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ClangImporter::ClangImporter(ASTContext &ctx, bool splitPrepositions,
|
ClangImporter::ClangImporter(ASTContext &ctx,
|
||||||
bool implicitProperties)
|
const ClangImporterOptions &clangImporterOpts)
|
||||||
: Impl(*new Implementation(ctx, splitPrepositions, implicitProperties))
|
: Impl(*new Implementation(ctx, clangImporterOpts))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +123,7 @@ void ClangImporter::clearTypeResolver() {
|
|||||||
ClangImporter *ClangImporter::create(ASTContext &ctx, StringRef targetTriple,
|
ClangImporter *ClangImporter::create(ASTContext &ctx, StringRef targetTriple,
|
||||||
const ClangImporterOptions &clangImporterOpts) {
|
const ClangImporterOptions &clangImporterOpts) {
|
||||||
std::unique_ptr<ClangImporter> importer{
|
std::unique_ptr<ClangImporter> importer{
|
||||||
new ClangImporter(ctx, ctx.LangOpts.SplitPrepositions,
|
new ClangImporter(ctx, clangImporterOpts)
|
||||||
clangImporterOpts.InferImplicitProperties)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the SearchPathOptions to use when creating the Clang importer.
|
// Get the SearchPathOptions to use when creating the Clang importer.
|
||||||
@@ -373,6 +372,15 @@ Module *ClangImporter::loadModule(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClangImporter::Implementation::Implementation(ASTContext &ctx,
|
||||||
|
const ClangImporterOptions &opts)
|
||||||
|
: SwiftContext(ctx),
|
||||||
|
SplitPrepositions(ctx.LangOpts.SplitPrepositions),
|
||||||
|
InferImplicitProperties(opts.InferImplicitProperties),
|
||||||
|
ImportFactoryMethodsAsConstructors(opts.ImportFactoryMethodsAsConstructors)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ClangModuleUnit *
|
ClangModuleUnit *
|
||||||
ClangImporter::Implementation::getWrapperForModule(ClangImporter &importer,
|
ClangImporter::Implementation::getWrapperForModule(ClangImporter &importer,
|
||||||
clang::Module *underlying) {
|
clang::Module *underlying) {
|
||||||
@@ -796,6 +804,81 @@ DeclName ClangImporter::Implementation::mapSelectorToDeclName(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeclName ClangImporter::Implementation::mapFactorySelectorToInitializerName(
|
||||||
|
ObjCSelector selector,
|
||||||
|
StringRef className) {
|
||||||
|
auto firstPiece = selector.getSelectorPieces()[0];
|
||||||
|
if (firstPiece.empty())
|
||||||
|
return DeclName();
|
||||||
|
|
||||||
|
// Match the camelCase beginning of the first selector piece to the
|
||||||
|
// ending of the class name.
|
||||||
|
auto firstPieceStr = firstPiece.str();
|
||||||
|
auto methodWords = camel_case::getWords(firstPieceStr);
|
||||||
|
auto classWords = camel_case::getWords(className);
|
||||||
|
auto methodWordIter = methodWords.begin(),
|
||||||
|
methodWordIterEnd = methodWords.end();
|
||||||
|
auto classWordRevIter = classWords.rbegin(),
|
||||||
|
classWordRevIterEnd = classWords.rend();
|
||||||
|
|
||||||
|
// Find the last instance of the first word in the method's name within
|
||||||
|
// the words in the class name.
|
||||||
|
while (classWordRevIter != classWordRevIterEnd &&
|
||||||
|
!camel_case::sameWordIgnoreFirstCase(*methodWordIter,
|
||||||
|
*classWordRevIter)) {
|
||||||
|
++classWordRevIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find the first word in the method's name at all, we're
|
||||||
|
// done.
|
||||||
|
if (classWordRevIter == classWordRevIterEnd)
|
||||||
|
return DeclName();
|
||||||
|
|
||||||
|
// Now, match from the first word up until the end of the class.
|
||||||
|
auto classWordIter = classWordRevIter.base(),
|
||||||
|
classWordIterEnd = classWords.end();
|
||||||
|
++methodWordIter;
|
||||||
|
while (classWordIter != classWordIterEnd &&
|
||||||
|
methodWordIter != methodWordIterEnd &&
|
||||||
|
camel_case::sameWordIgnoreFirstCase(*classWordIter,
|
||||||
|
*methodWordIter)) {
|
||||||
|
++classWordIter;
|
||||||
|
++methodWordIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't reach the end of the class name, don't match.
|
||||||
|
if (classWordIter != classWordIterEnd)
|
||||||
|
return DeclName();
|
||||||
|
|
||||||
|
// We found the chopping point. Form the first argument name.
|
||||||
|
llvm::SmallString<32> scratch;
|
||||||
|
SmallVector<Identifier, 4> argumentNames;
|
||||||
|
argumentNames.push_back(
|
||||||
|
importArgName(SwiftContext,
|
||||||
|
splitSelectorPieceAt(firstPieceStr,
|
||||||
|
methodWordIter.getPosition(),
|
||||||
|
scratch).second));
|
||||||
|
|
||||||
|
// Handle nullary factory methods.
|
||||||
|
if (selector.getNumArgs() == 0) {
|
||||||
|
if (argumentNames[0].empty())
|
||||||
|
return DeclName(SwiftContext, SwiftContext.Id_init, { });
|
||||||
|
|
||||||
|
// FIXME: Fake up an empty tuple here?
|
||||||
|
return DeclName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the remaining selector pieces.
|
||||||
|
for (auto piece : selector.getSelectorPieces().slice(1)) {
|
||||||
|
if (piece.empty())
|
||||||
|
argumentNames.push_back(piece);
|
||||||
|
else
|
||||||
|
argumentNames.push_back(importArgName(SwiftContext, piece.str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclName(SwiftContext, SwiftContext.Id_init, argumentNames);
|
||||||
|
}
|
||||||
|
|
||||||
void ClangImporter::Implementation::populateKnownDesignatedInits() {
|
void ClangImporter::Implementation::populateKnownDesignatedInits() {
|
||||||
if (!KnownDesignatedInits.empty())
|
if (!KnownDesignatedInits.empty())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -40,6 +40,12 @@
|
|||||||
#define DEBUG_TYPE "Clang module importer"
|
#define DEBUG_TYPE "Clang module importer"
|
||||||
|
|
||||||
STATISTIC(NumTotalImportedEntities, "# of imported clang entities");
|
STATISTIC(NumTotalImportedEntities, "# of imported clang entities");
|
||||||
|
STATISTIC(NumFactoryMethodsNSError,
|
||||||
|
"# of factory methods not mapped due to NSError");
|
||||||
|
STATISTIC(NumFactoryMethodsWrongResult,
|
||||||
|
"# of factory methods not mapped due to an incorrect result type");
|
||||||
|
STATISTIC(NumFactoryMethodsAsInitializers,
|
||||||
|
"# of factory methods mapped to initializers");
|
||||||
|
|
||||||
using namespace swift;
|
using namespace swift;
|
||||||
|
|
||||||
@@ -1735,6 +1741,63 @@ namespace {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the given method is a factory method, import it as a constructor
|
||||||
|
Optional<ConstructorDecl *>
|
||||||
|
importFactoryMethodAsConstructor(const clang::ObjCMethodDecl *decl,
|
||||||
|
ObjCSelector selector,
|
||||||
|
DeclContext *dc) {
|
||||||
|
if (!Impl.ImportFactoryMethodsAsConstructors)
|
||||||
|
return Nothing;
|
||||||
|
|
||||||
|
// Only class methods can be mapped to constructors.
|
||||||
|
if (!decl->isClassMethod())
|
||||||
|
return Nothing;
|
||||||
|
|
||||||
|
// Said class methods must be in an actual class.
|
||||||
|
auto objcClass = decl->getClassInterface();
|
||||||
|
if (!objcClass)
|
||||||
|
return Nothing;
|
||||||
|
|
||||||
|
// Check whether the name fits the pattern.
|
||||||
|
DeclName initName
|
||||||
|
= Impl.mapFactorySelectorToInitializerName(selector,
|
||||||
|
objcClass->getName());
|
||||||
|
if (!initName)
|
||||||
|
return Nothing;
|
||||||
|
|
||||||
|
// Check whether the result is instancetype.
|
||||||
|
if (!decl->hasRelatedResultType()) {
|
||||||
|
++NumFactoryMethodsWrongResult;
|
||||||
|
return Nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether there is an NSError parameter, which implies failability.
|
||||||
|
// FIXME: Revisit once we have failing initializers.
|
||||||
|
for (auto param : decl->parameters()) {
|
||||||
|
if (auto ptr = param->getType()->getAs<clang::PointerType>()) {
|
||||||
|
if (auto classPtr = ptr->getPointeeType()
|
||||||
|
->getAs<clang::ObjCObjectPointerType>()) {
|
||||||
|
if (auto classDecl = classPtr->getInterfaceDecl()) {
|
||||||
|
if (classDecl->getName() == "NSError") {
|
||||||
|
++NumFactoryMethodsNSError;
|
||||||
|
return Nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Check for redundant initializers. It's very, very hard to
|
||||||
|
// avoid order dependencies here. Perhaps we should just deal with the
|
||||||
|
// ambiguity later?
|
||||||
|
auto result = importConstructor(decl, dc, false,
|
||||||
|
InitializerKind::Convenience,
|
||||||
|
/*required=*/false, selector, initName);
|
||||||
|
if (result)
|
||||||
|
++NumFactoryMethodsAsInitializers;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Decl *VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl,
|
Decl *VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl,
|
||||||
DeclContext *dc,
|
DeclContext *dc,
|
||||||
bool forceClassMethod = false) {
|
bool forceClassMethod = false) {
|
||||||
@@ -1765,6 +1828,10 @@ namespace {
|
|||||||
if (methodAlreadyImported(selector, isInstance, dc))
|
if (methodAlreadyImported(selector, isInstance, dc))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
// If this is a factory method, try to import it as a constructor.
|
||||||
|
if (auto factory = importFactoryMethodAsConstructor(decl, selector, dc))
|
||||||
|
return *factory;
|
||||||
|
|
||||||
DeclName name = Impl.mapSelectorToDeclName(selector,
|
DeclName name = Impl.mapSelectorToDeclName(selector,
|
||||||
/*isInitializer=*/false);
|
/*isInitializer=*/false);
|
||||||
if (!name)
|
if (!name)
|
||||||
@@ -2009,10 +2076,6 @@ namespace {
|
|||||||
bool implicit,
|
bool implicit,
|
||||||
Optional<InitializerKind> kind,
|
Optional<InitializerKind> kind,
|
||||||
bool required) {
|
bool required) {
|
||||||
// Figure out the type of the container.
|
|
||||||
auto containerTy = dc->getDeclaredTypeOfContext();
|
|
||||||
assert(containerTy && "Method in non-type context?");
|
|
||||||
|
|
||||||
// Only methods in the 'init' family can become constructors.
|
// Only methods in the 'init' family can become constructors.
|
||||||
assert(objcMethod->getMethodFamily() == clang::OMF_init &&
|
assert(objcMethod->getMethodFamily() == clang::OMF_init &&
|
||||||
"Not an init method");
|
"Not an init method");
|
||||||
@@ -2028,21 +2091,31 @@ namespace {
|
|||||||
if (methodAlreadyImported(selector, /*isInstance=*/false, dc))
|
if (methodAlreadyImported(selector, /*isInstance=*/false, dc))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Find the interface, if we can.
|
// Map the name and complete the import.
|
||||||
const clang::ObjCInterfaceDecl *interface = nullptr;
|
|
||||||
if (isa<clang::ObjCProtocolDecl>(objcMethod->getDeclContext())) {
|
|
||||||
// FIXME: Part of the mirroring hack.
|
|
||||||
if (auto classDecl = containerTy->getClassOrBoundGenericClass())
|
|
||||||
interface = dyn_cast_or_null<clang::ObjCInterfaceDecl>(
|
|
||||||
classDecl->getClangDecl());
|
|
||||||
} else {
|
|
||||||
// For non-protocol methods, just look for the interface.
|
|
||||||
interface = objcMethod->getClassInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLoc loc;
|
|
||||||
auto name = Impl.mapSelectorToDeclName(selector, /*isInitializer=*/true);
|
auto name = Impl.mapSelectorToDeclName(selector, /*isInitializer=*/true);
|
||||||
|
return importConstructor(objcMethod, dc, implicit, kind, required,
|
||||||
|
selector, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Given an imported method, try to import it as a constructor.
|
||||||
|
///
|
||||||
|
/// Objective-C methods in the 'init' family are imported as
|
||||||
|
/// constructors in Swift, enabling object construction syntax, e.g.,
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// // in objc: [[NSArray alloc] initWithCapacity:1024]
|
||||||
|
/// NSArray(withCapacity: 1024)
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// This variant of the function is responsible for actually binding the
|
||||||
|
/// constructor declaration appropriately.
|
||||||
|
ConstructorDecl *importConstructor(const clang::ObjCMethodDecl *objcMethod,
|
||||||
|
DeclContext *dc,
|
||||||
|
bool implicit,
|
||||||
|
Optional<InitializerKind> kind,
|
||||||
|
bool required,
|
||||||
|
ObjCSelector selector,
|
||||||
|
DeclName name) {
|
||||||
// Add the implicit 'self' parameter patterns.
|
// Add the implicit 'self' parameter patterns.
|
||||||
SmallVector<Pattern *, 4> bodyPatterns;
|
SmallVector<Pattern *, 4> bodyPatterns;
|
||||||
auto selfTy = getSelfTypeForContext(dc);
|
auto selfTy = getSelfTypeForContext(dc);
|
||||||
@@ -2063,10 +2136,26 @@ namespace {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Check whether we've already created the constructor.
|
// Check whether we've already created the constructor.
|
||||||
known = Impl.Constructors.find({objcMethod, dc});
|
auto known = Impl.Constructors.find({objcMethod, dc});
|
||||||
if (known != Impl.Constructors.end())
|
if (known != Impl.Constructors.end())
|
||||||
return known->second;
|
return known->second;
|
||||||
|
|
||||||
|
// Figure out the type of the container.
|
||||||
|
auto containerTy = dc->getDeclaredTypeOfContext();
|
||||||
|
assert(containerTy && "Method in non-type context?");
|
||||||
|
|
||||||
|
// Find the interface, if we can.
|
||||||
|
const clang::ObjCInterfaceDecl *interface = nullptr;
|
||||||
|
if (isa<clang::ObjCProtocolDecl>(objcMethod->getDeclContext())) {
|
||||||
|
// FIXME: Part of the mirroring hack.
|
||||||
|
if (auto classDecl = containerTy->getClassOrBoundGenericClass())
|
||||||
|
interface = dyn_cast_or_null<clang::ObjCInterfaceDecl>(
|
||||||
|
classDecl->getClangDecl());
|
||||||
|
} else {
|
||||||
|
// For non-protocol methods, just look for the interface.
|
||||||
|
interface = objcMethod->getClassInterface();
|
||||||
|
}
|
||||||
|
|
||||||
// A constructor returns an object of the type, not 'id'.
|
// A constructor returns an object of the type, not 'id'.
|
||||||
// This is effectively implementing related-result-type semantics.
|
// This is effectively implementing related-result-type semantics.
|
||||||
// FIXME: Perhaps actually check whether the routine has a related result
|
// FIXME: Perhaps actually check whether the routine has a related result
|
||||||
@@ -2083,7 +2172,7 @@ namespace {
|
|||||||
|
|
||||||
// Create the actual constructor.
|
// Create the actual constructor.
|
||||||
auto result = new (Impl.SwiftContext)
|
auto result = new (Impl.SwiftContext)
|
||||||
ConstructorDecl(name, loc, selfPat, bodyPatterns.back(),
|
ConstructorDecl(name, SourceLoc(), selfPat, bodyPatterns.back(),
|
||||||
/*GenericParams=*/0, dc);
|
/*GenericParams=*/0, dc);
|
||||||
result->setClangNode(objcMethod);
|
result->setClangNode(objcMethod);
|
||||||
addObjCAttribute(result, selector);
|
addObjCAttribute(result, selector);
|
||||||
|
|||||||
@@ -177,10 +177,7 @@ public:
|
|||||||
Constants
|
Constants
|
||||||
};
|
};
|
||||||
|
|
||||||
Implementation(ASTContext &ctx, bool splitPrepositions,
|
Implementation(ASTContext &ctx, const ClangImporterOptions &opts);
|
||||||
bool implicitProperties)
|
|
||||||
: SwiftContext(ctx), SplitPrepositions(splitPrepositions),
|
|
||||||
InferImplicitProperties(implicitProperties) { }
|
|
||||||
|
|
||||||
~Implementation() {
|
~Implementation() {
|
||||||
assert(NumCurrentImportingEntities == 0);
|
assert(NumCurrentImportingEntities == 0);
|
||||||
@@ -191,6 +188,7 @@ public:
|
|||||||
|
|
||||||
const bool SplitPrepositions;
|
const bool SplitPrepositions;
|
||||||
const bool InferImplicitProperties;
|
const bool InferImplicitProperties;
|
||||||
|
const bool ImportFactoryMethodsAsConstructors;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief A count of the number of load module operations.
|
/// \brief A count of the number of load module operations.
|
||||||
@@ -453,6 +451,18 @@ public:
|
|||||||
/// initializer.
|
/// initializer.
|
||||||
DeclName mapSelectorToDeclName(ObjCSelector selector, bool isInitializer);
|
DeclName mapSelectorToDeclName(ObjCSelector selector, bool isInitializer);
|
||||||
|
|
||||||
|
/// Try to map the given selector, which may be the name of a factory method,
|
||||||
|
/// to the name of an initializer.
|
||||||
|
///
|
||||||
|
/// \param selector The selector to map.
|
||||||
|
///
|
||||||
|
/// \param className The name of the class in which the method occurs.
|
||||||
|
///
|
||||||
|
/// \returns the initializer name for this factory method, or an empty
|
||||||
|
/// name if this selector does not fit the pattern.
|
||||||
|
DeclName mapFactorySelectorToInitializerName(ObjCSelector selector,
|
||||||
|
StringRef className);
|
||||||
|
|
||||||
/// \brief Import the given Swift source location into Clang.
|
/// \brief Import the given Swift source location into Clang.
|
||||||
clang::SourceLocation importSourceLoc(SourceLoc loc);
|
clang::SourceLocation importSourceLoc(SourceLoc loc);
|
||||||
|
|
||||||
|
|||||||
@@ -566,6 +566,8 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args,
|
|||||||
|
|
||||||
Opts.InferImplicitProperties =
|
Opts.InferImplicitProperties =
|
||||||
Args.hasArg(OPT_enable_objc_implicit_properties);
|
Args.hasArg(OPT_enable_objc_implicit_properties);
|
||||||
|
Opts.ImportFactoryMethodsAsConstructors =
|
||||||
|
Args.hasArg(OPT_enable_objc_factory_method_constructors);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
23
test/ClangModules/objc_factory_method.swift
Normal file
23
test/ClangModules/objc_factory_method.swift
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// RUN: rm -rf %t/clang-module-cache
|
||||||
|
// RUN: %swift %clang-importer-sdk -parse -module-cache-path %t/clang-module-cache -enable-objc-factory-method-constructors %s -verify
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
func testInstanceTypeFactoryMethod(queen: B) {
|
||||||
|
var hive1 = Hive(withQueen: queen)
|
||||||
|
|
||||||
|
// FIXME: Need to suppress the class method when there is a corresponding
|
||||||
|
// initializer with the same name.
|
||||||
|
var of1 = NSObjectFactory() // FIXME: expected-error{{does not type-check}}
|
||||||
|
var of2 = NSObjectFactory(withInteger: 1)
|
||||||
|
var of3 = NSObjectFactory(withDouble: 314159)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNSErrorFactoryMethod(path: String) {
|
||||||
|
var error: NSError?
|
||||||
|
var s1 = NSString(withContentsOfFile: path, error: &error) // expected-error{{does not type-check}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNonInstanceTypeFactoryMethod(s: String) {
|
||||||
|
var of1 = NSObjectFactory(withString: s) // expected-error{{does not type-check}}
|
||||||
|
}
|
||||||
@@ -57,3 +57,11 @@
|
|||||||
@interface NSTableViewController : NSViewController
|
@interface NSTableViewController : NSViewController
|
||||||
-(instancetype)initWithInt:(NSInteger)value NS_DESIGNATED_INITIALIZER;
|
-(instancetype)initWithInt:(NSInteger)value NS_DESIGNATED_INITIALIZER;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface NSObjectFactory : NSObject
|
||||||
|
+(instancetype)objectFactory;
|
||||||
|
+(instancetype)objectFactoryWithInteger:(NSInteger)i;
|
||||||
|
+(instancetype)factoryWithDouble:(double)i;
|
||||||
|
+(id)factoryWithString:(NSString *)s;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user