Skip to content

Commit

Permalink
Clang importer: switch method name import to importFullName().
Browse files Browse the repository at this point in the history
This lets us kill the redundant Objective-C selector to DeclName
mapping used only for methods.
  • Loading branch information
DougGregor committed Dec 3, 2015
1 parent 3102e23 commit 4c50598
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 214 deletions.
215 changes: 33 additions & 182 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
#include "clang/Rewrite/Frontend/Rewriters.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Path.h"
Expand All @@ -60,27 +59,6 @@

using namespace swift;

//===--------------------------------------------------------------------===//
// Importer statistics
//===--------------------------------------------------------------------===//
#define DEBUG_TYPE "Clang module importer"
STATISTIC(NumNullaryMethodNames,
"nullary selectors imported");
STATISTIC(NumUnaryMethodNames,
"unary selectors imported");
STATISTIC(NumNullaryInitMethodsMadeUnary,
"nullary Objective-C init methods turned into unary initializers");
STATISTIC(NumMultiMethodNames,
"multi-part selector method names imported");
STATISTIC(NumMethodsMissingFirstArgName,
"selectors where the first argument name is missing");
STATISTIC(NumInitsDroppedWith,
"# of initializer selectors from which \"with\" was dropped");
STATISTIC(NumInitsPrepositionSplit,
"# of initializer selectors where the split was on a preposition");
STATISTIC(NumInitsNonPrepositionSplit,
"# of initializer selectors where the split wasn't on a preposition");

// Commonly-used Clang classes.
using clang::CompilerInstance;
using clang::CompilerInvocation;
Expand Down Expand Up @@ -725,7 +703,7 @@ void ClangImporter::Implementation::addEntryToLookupTable(
if (!suppressDecl) {
// If we have a name to import as, add this entry to the table.
clang::DeclContext *effectiveContext;
if (DeclName name = importFullName(named, &effectiveContext)) {
if (DeclName name = importFullName(named, None, &effectiveContext)) {
table.addEntry(name, named, effectiveContext);
}
}
Expand Down Expand Up @@ -1636,6 +1614,7 @@ static bool shouldMakeSelectorNonVariadic(clang::Selector selector) {

auto ClangImporter::Implementation::importFullName(
const clang::NamedDecl *D,
ImportNameOptions options,
clang::DeclContext **effectiveContext) -> ImportedName {
ImportedName result;

Expand Down Expand Up @@ -1680,20 +1659,36 @@ auto ClangImporter::Implementation::importFullName(

// If we have a swift_name attribute, use that.
if (auto *nameAttr = D->getAttr<clang::SwiftNameAttr>()) {
bool skipCustomName = false;

// If we have an Objective-C method that is being mapped to an
// initializer (e.g., a factory method whose name doesn't fit the
// convention for factory methods), make sure that it can be
// imported as an initializer.
if (auto method = dyn_cast<clang::ObjCMethodDecl>(D)) {
unsigned initPrefixLength;
if (nameAttr->getName().startswith("init(") &&
!shouldImportAsInitializer(method, initPrefixLength, result.InitKind))
return { };
if (nameAttr->getName().startswith("init(")) {
if (!shouldImportAsInitializer(method, initPrefixLength,
result.InitKind)) {
// We cannot import this as an initializer anyway.
return { };
}

// If this swift_name attribute maps a factory method to an
// initializer and we were asked not to do so, ignore the
// custom name.
if (options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) &&
(result.InitKind == CtorInitializerKind::Factory ||
result.InitKind == CtorInitializerKind::ConvenienceFactory))
skipCustomName = true;
}
}

result.HasCustomName = true;
result.Imported = parseDeclName(SwiftContext, nameAttr->getName());
return result;
if (!skipCustomName) {
result.HasCustomName = true;
result.Imported = parseDeclName(SwiftContext, nameAttr->getName());
return result;
}
}

// For empty names, there is nothing to do.
Expand Down Expand Up @@ -1728,6 +1723,15 @@ auto ClangImporter::Implementation::importFullName(
isInitializer = shouldImportAsInitializer(objcMethod, initializerPrefixLen,
result.InitKind);

// If we would import a factory method as an initializer but were
// asked not to, don't consider this as an initializer.
if (isInitializer &&
options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) &&
(result.InitKind == CtorInitializerKind::Factory ||
result.InitKind == CtorInitializerKind::ConvenienceFactory)) {
isInitializer = false;
}

// Map the Objective-C selector directly.
auto selector = D->getDeclName().getObjCSelector();
if (isInitializer)
Expand Down Expand Up @@ -1997,139 +2001,6 @@ ClangImporter::Implementation::importIdentifier(
return SwiftContext.getIdentifier(name);
}

/// Import an argument name.
static Identifier importArgName(ASTContext &ctx, StringRef name,
bool dropWith, bool isSwiftPrivate) {
// Simple case: empty name.
if (name.empty()) {
if (isSwiftPrivate)
return ctx.getIdentifier("__");
return Identifier();
}

SmallString<32> scratch;
auto words = camel_case::getWords(name);
auto firstWord = *words.begin();
StringRef argName = name;

// If we're dropping "with", handle that now.
if (dropWith) {
// If the first word is "with"...
if (name.size() > 4 &&
camel_case::sameWordIgnoreFirstCase(firstWord, "with")) {
// Drop it.
++NumInitsDroppedWith;

auto iter = words.begin();
++iter;

argName = name.substr(iter.getPosition());
// Don't drop "with" if the resulting arg is a reserved name.
if (ClangImporter::Implementation::isSwiftReservedName(
camel_case::toLowercaseWord(argName, scratch))) {
argName = name;
}
} else {
// If we're tracking statistics, check whether the name starts with
// a preposition.
if (llvm::AreStatisticsEnabled()) {
if (getPrepositionKind(firstWord))
++NumInitsPrepositionSplit;
else
++NumInitsNonPrepositionSplit;
}

argName = name;
}
}

/// Lowercase the first word to form the argument name.
argName = camel_case::toLowercaseWord(argName, scratch);
if (!isSwiftPrivate)
return ctx.getIdentifier(argName);

SmallString<32> prefixed{"__"};
prefixed.append(argName);
return ctx.getIdentifier(prefixed.str());
}

/// Map an Objective-C selector name to a Swift method name.
static DeclName mapSelectorName(ASTContext &ctx,
ObjCSelector selector,
bool isInitializer,
bool isSwiftPrivate) {
// Zero-argument selectors.
if (selector.getNumArgs() == 0) {
++NumNullaryMethodNames;

auto name = selector.getSelectorPieces()[0];
StringRef nameText = name.empty()? "" : name.str();

if (!isInitializer) {
if (!isSwiftPrivate)
return DeclName(ctx, name, {});

SmallString<32> newName{"__"};
newName.append(nameText);
return DeclName(ctx, ctx.getIdentifier(newName.str()), {});
}

// Simple case for initializers.
if (nameText == "init" && !isSwiftPrivate)
return DeclName(ctx, name, { });

// This is an initializer with no parameters but a name that
// contains more than 'init', so synthesize an argument to capture
// what follows 'init'.
++NumNullaryInitMethodsMadeUnary;
assert(camel_case::getFirstWord(nameText).equals("init"));
auto baseName = ctx.Id_init;
auto argName = importArgName(ctx, nameText.substr(4), /*dropWith=*/true,
isSwiftPrivate);
return DeclName(ctx, baseName, argName);
}

// Determine the base name and first argument name.
Identifier baseName;
SmallVector<Identifier, 2> argumentNames;
Identifier firstPiece = selector.getSelectorPieces()[0];
StringRef firstPieceText = firstPiece.empty()? "" : firstPiece.str();
if (isInitializer) {
assert(camel_case::getFirstWord(firstPieceText).equals("init"));
baseName = ctx.Id_init;
argumentNames.push_back(importArgName(ctx, firstPieceText.substr(4),
/*dropWith=*/true, isSwiftPrivate));
} else {
baseName = firstPiece;
if (isSwiftPrivate) {
SmallString<32> newName{"__"};
newName.append(firstPieceText);
baseName = ctx.getIdentifier(newName);
}
argumentNames.push_back(Identifier());
}

if (argumentNames[0].empty())
++NumMethodsMissingFirstArgName;

// Determine the remaining argument names.
unsigned n = selector.getNumArgs();
if (n == 1)
++NumUnaryMethodNames;
else
++NumMultiMethodNames;

for (auto piece : selector.getSelectorPieces().slice(1)) {
if (piece.empty())
argumentNames.push_back(piece);
else
argumentNames.push_back(importArgName(ctx, piece.str(),
/*dropWith=*/false,
/*isSwiftPrivate=*/false));
}
return DeclName(ctx, baseName, argumentNames);
}

namespace {
/// Function object used to create Clang selectors from strings.
class CreateSelector {
Expand Down Expand Up @@ -2230,26 +2101,6 @@ ClangImporter::Implementation::exportSelector(ObjCSelector selector) {
pieces.data());
}


DeclName
ClangImporter::Implementation::mapSelectorToDeclName(ObjCSelector selector,
bool isInitializer,
bool isSwiftPrivate)
{
// Check whether we've already mapped this selector.
auto known = SelectorMappings.find({selector, isInitializer});
if (known != SelectorMappings.end())
return known->second;

// Map the selector.
auto result = mapSelectorName(SwiftContext, selector, isInitializer,
isSwiftPrivate);

// Cache the result and return.
SelectorMappings[{selector, isInitializer}] = result;
return result;
}

/// Translate the "nullability" notion from API notes into an optional type
/// kind.
OptionalTypeKind ClangImporter::Implementation::translateNullability(
Expand Down
24 changes: 7 additions & 17 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2854,22 +2854,11 @@ namespace {
if (methodAlreadyImported(selector, isInstance, dc))
return nullptr;

DeclName name;
bool hasCustomName;
if (auto *customNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
if (!customNameAttr->getName().startswith("init(")) {
auto importedName = Impl.importFullName(decl);
name = importedName.Imported;
hasCustomName = importedName.HasCustomName;
}
}
if (!name) {
hasCustomName = false;
bool isSwiftPrivate = decl->hasAttr<clang::SwiftPrivateAttr>();
name = Impl.mapSelectorToDeclName(selector, /*isInitializer=*/false,
isSwiftPrivate);
}
if (!name)
auto importedName
= Impl.importFullName(decl,
ClangImporter::Implementation::ImportNameFlags
::SuppressFactoryMethodAsInit);
if (!importedName)
return nullptr;

assert(dc->getDeclaredTypeOfContext() && "Method in non-type context?");
Expand Down Expand Up @@ -2897,6 +2886,7 @@ namespace {
kind = SpecialMethodKind::NSDictionarySubscriptGetter;

// Import the type that this method will have.
DeclName name = importedName.Imported;
Optional<ForeignErrorConvention> errorConvention;
auto type = Impl.importMethodType(decl,
decl->getReturnType(),
Expand All @@ -2905,7 +2895,7 @@ namespace {
decl->isVariadic(),
decl->hasAttr<clang::NoReturnAttr>(),
isInSystemModule(dc),
hasCustomName,
importedName.HasCustomName,
bodyPatterns,
name,
errorConvention,
Expand Down
25 changes: 10 additions & 15 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
llvm::SmallDenseMap<const clang::TypedefNameDecl *, MappedTypeNameKind, 16>
SpecialTypedefNames;

/// Mapping from Objective-C selectors to method names.
llvm::DenseMap<std::pair<ObjCSelector, char>, DeclName> SelectorMappings;

/// Is the given identifier a reserved name in Swift?
static bool isSwiftReservedName(StringRef name);

Expand Down Expand Up @@ -769,6 +766,15 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
explicit operator bool() const { return static_cast<bool>(Imported); }
};

/// Flags that control the import of names in importFullName.
enum class ImportNameFlags {
/// Suppress the factory-method-as-initializer transformation.
SuppressFactoryMethodAsInit = 0x01,
};

/// Options that control the import of names in importFullName.
typedef OptionSet<ImportNameFlags> ImportNameOptions;

/// Imports the full name of the given Clang declaration into Swift.
///
/// Note that this may result in a name very different from the Clang name,
Expand All @@ -781,6 +787,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// This can differ from D's redeclaration context when the Clang importer
/// introduces nesting, e.g., for enumerators within an NS_ENUM.
ImportedName importFullName(const clang::NamedDecl *D,
ImportNameOptions options = None,
clang::DeclContext **effectiveContext = nullptr);

/// \brief Import the given Clang identifier into Swift.
Expand All @@ -802,18 +809,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// Export a Swift Objective-C selector as a Clang Objective-C selector.
clang::Selector exportSelector(ObjCSelector selector);

/// Map the given selector to a declaration name.
///
/// \param selector The selector to map.
///
/// \param isInitializer Whether this name should be mapped as an
/// initializer.
///
/// \param isSwiftPrivate Whether this name is for a declaration marked with
/// the 'swift_private' attribute.
DeclName mapSelectorToDeclName(ObjCSelector selector, bool isInitializer,
bool isSwiftPrivate);

/// \brief Import the given Swift source location into Clang.
clang::SourceLocation exportSourceLoc(SourceLoc loc);

Expand Down

0 comments on commit 4c50598

Please sign in to comment.