Skip to content

Commit ab05d64

Browse files
committed
[Clang Importer] Refactor information pertaining to enums out
Introduces new class, EnumInfo, just for the implementation of the ClangImporter to encapsulate various computed information about the enum we're importing. This helps refactor some functionality, aids clarity, and also prevents us from repeating calculations multiple times, as we were doing with classifyEnum's macro-expansion tracking. Provides a base where we can add more heavy lifting in classifyEnum in the future.
1 parent 66cf479 commit ab05d64

File tree

7 files changed

+406
-305
lines changed

7 files changed

+406
-305
lines changed

lib/ClangImporter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_swift_library(swiftClangImporter
1212
ClangDiagnosticConsumer.cpp
1313
ClangImporter.cpp
1414
ImportDecl.cpp
15+
ImportEnumInfo.cpp
1516
ImportMacro.cpp
1617
ImportType.cpp
1718
SwiftLookupTable.cpp

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include <memory>
6161

6262
using namespace swift;
63+
using namespace importer;
6364

6465
// Commonly-used Clang classes.
6566
using clang::CompilerInstance;
@@ -1359,235 +1360,6 @@ ClangImporter::Implementation::exportName(Identifier name) {
13591360
return ident;
13601361
}
13611362

1362-
/// \brief Returns the common prefix of two strings at camel-case word
1363-
/// granularity.
1364-
///
1365-
/// For example, given "NSFooBar" and "NSFooBas", returns "NSFoo"
1366-
/// (not "NSFooBa"). The returned StringRef is a slice of the "a" argument.
1367-
///
1368-
/// If either string has a non-identifier character immediately after the
1369-
/// prefix, \p followedByNonIdentifier will be set to \c true. If both strings
1370-
/// have identifier characters after the prefix, \p followedByNonIdentifier will
1371-
/// be set to \c false. Otherwise, \p followedByNonIdentifier will not be
1372-
/// changed from its initial value.
1373-
///
1374-
/// This is used to derive the common prefix of enum constants so we can elide
1375-
/// it from the Swift interface.
1376-
static StringRef getCommonWordPrefix(StringRef a, StringRef b,
1377-
bool &followedByNonIdentifier) {
1378-
auto aWords = camel_case::getWords(a), bWords = camel_case::getWords(b);
1379-
auto aI = aWords.begin(), aE = aWords.end(),
1380-
bI = bWords.begin(), bE = bWords.end();
1381-
1382-
unsigned prevLength = 0;
1383-
unsigned prefixLength = 0;
1384-
for ( ; aI != aE && bI != bE; ++aI, ++bI) {
1385-
if (*aI != *bI) {
1386-
followedByNonIdentifier = false;
1387-
break;
1388-
}
1389-
1390-
prevLength = prefixLength;
1391-
prefixLength = aI.getPosition() + aI->size();
1392-
}
1393-
1394-
// Avoid creating a prefix where the rest of the string starts with a number.
1395-
if ((aI != aE && !Lexer::isIdentifier(*aI)) ||
1396-
(bI != bE && !Lexer::isIdentifier(*bI))) {
1397-
followedByNonIdentifier = true;
1398-
prefixLength = prevLength;
1399-
}
1400-
1401-
return a.slice(0, prefixLength);
1402-
}
1403-
1404-
/// Returns the common word-prefix of two strings, allowing the second string
1405-
/// to be a common English plural form of the first.
1406-
///
1407-
/// For example, given "NSProperty" and "NSProperties", the full "NSProperty"
1408-
/// is returned. Given "NSMagicArmor" and "NSMagicArmory", only
1409-
/// "NSMagic" is returned.
1410-
///
1411-
/// The "-s", "-es", and "-ies" patterns cover every plural NS_OPTIONS name
1412-
/// in Cocoa and Cocoa Touch.
1413-
///
1414-
/// \see getCommonWordPrefix
1415-
static StringRef getCommonPluralPrefix(StringRef singular, StringRef plural) {
1416-
assert(!plural.empty());
1417-
1418-
if (singular.empty())
1419-
return singular;
1420-
1421-
bool ignored;
1422-
StringRef commonPrefix = getCommonWordPrefix(singular, plural, ignored);
1423-
if (commonPrefix.size() == singular.size() || plural.back() != 's')
1424-
return commonPrefix;
1425-
1426-
StringRef leftover = singular.substr(commonPrefix.size());
1427-
StringRef firstLeftoverWord = camel_case::getFirstWord(leftover);
1428-
StringRef commonPrefixPlusWord =
1429-
singular.substr(0, commonPrefix.size() + firstLeftoverWord.size());
1430-
1431-
// Is the plural string just "[singular]s"?
1432-
plural = plural.drop_back();
1433-
if (plural.endswith(firstLeftoverWord))
1434-
return commonPrefixPlusWord;
1435-
1436-
if (plural.empty() || plural.back() != 'e')
1437-
return commonPrefix;
1438-
1439-
// Is the plural string "[singular]es"?
1440-
plural = plural.drop_back();
1441-
if (plural.endswith(firstLeftoverWord))
1442-
return commonPrefixPlusWord;
1443-
1444-
if (plural.empty() || !(plural.back() == 'i' && singular.back() == 'y'))
1445-
return commonPrefix;
1446-
1447-
// Is the plural string "[prefix]ies" and the singular "[prefix]y"?
1448-
plural = plural.drop_back();
1449-
firstLeftoverWord = firstLeftoverWord.drop_back();
1450-
if (plural.endswith(firstLeftoverWord))
1451-
return commonPrefixPlusWord;
1452-
1453-
return commonPrefix;
1454-
}
1455-
1456-
StringRef ClangImporter::Implementation::getEnumConstantNamePrefix(
1457-
clang::Sema &sema,
1458-
const clang::EnumDecl *decl) {
1459-
switch (classifyEnum(sema.getPreprocessor(), decl)) {
1460-
case EnumKind::Enum:
1461-
case EnumKind::Options:
1462-
// Enums are mapped to Swift enums, Options to Swift option sets, both
1463-
// of which attempt prefix-stripping.
1464-
break;
1465-
1466-
case EnumKind::Constants:
1467-
case EnumKind::Unknown:
1468-
// Nothing to do.
1469-
return StringRef();
1470-
}
1471-
1472-
// If there are no enumers, there is no prefix to compute.
1473-
auto ec = decl->enumerator_begin(), ecEnd = decl->enumerator_end();
1474-
if (ec == ecEnd)
1475-
return StringRef();
1476-
1477-
// Determine whether we can cache the result.
1478-
// FIXME: Pass in a cache?
1479-
bool useCache = &sema == &getClangSema();
1480-
1481-
// If we've already computed the prefix, return it.
1482-
auto known = useCache ? EnumConstantNamePrefixes.find(decl)
1483-
: EnumConstantNamePrefixes.end();
1484-
if (known != EnumConstantNamePrefixes.end())
1485-
return known->second;
1486-
1487-
// Determine whether the given enumerator is non-deprecated and has no
1488-
// specifically-provided name.
1489-
auto isNonDeprecatedWithoutCustomName =
1490-
[](const clang::EnumConstantDecl *elem) -> bool {
1491-
if (elem->hasAttr<clang::SwiftNameAttr>())
1492-
return false;
1493-
1494-
clang::VersionTuple maxVersion{~0U, ~0U, ~0U};
1495-
switch (elem->getAvailability(nullptr, maxVersion)) {
1496-
case clang::AR_Available:
1497-
case clang::AR_NotYetIntroduced:
1498-
for (auto attr : elem->attrs()) {
1499-
if (auto annotate = dyn_cast<clang::AnnotateAttr>(attr)) {
1500-
if (annotate->getAnnotation() == "swift1_unavailable")
1501-
return false;
1502-
}
1503-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(attr)) {
1504-
if (avail->getPlatform()->getName() == "swift")
1505-
return false;
1506-
}
1507-
}
1508-
return true;
1509-
1510-
case clang::AR_Deprecated:
1511-
case clang::AR_Unavailable:
1512-
return false;
1513-
}
1514-
};
1515-
1516-
// Move to the first non-deprecated enumerator, or non-swift_name'd
1517-
// enumerator, if present.
1518-
auto firstNonDeprecated = std::find_if(ec, ecEnd,
1519-
isNonDeprecatedWithoutCustomName);
1520-
bool hasNonDeprecated = (firstNonDeprecated != ecEnd);
1521-
if (hasNonDeprecated) {
1522-
ec = firstNonDeprecated;
1523-
} else {
1524-
// Advance to the first case without a custom name, deprecated or not.
1525-
while (ec != ecEnd && (*ec)->hasAttr<clang::SwiftNameAttr>())
1526-
++ec;
1527-
if (ec == ecEnd) {
1528-
if (useCache)
1529-
EnumConstantNamePrefixes.insert({decl, StringRef()});
1530-
return StringRef();
1531-
}
1532-
}
1533-
1534-
// Compute the common prefix.
1535-
StringRef commonPrefix = (*ec)->getName();
1536-
bool followedByNonIdentifier = false;
1537-
for (++ec; ec != ecEnd; ++ec) {
1538-
// Skip deprecated or swift_name'd enumerators.
1539-
const clang::EnumConstantDecl *elem = *ec;
1540-
if (hasNonDeprecated) {
1541-
if (!isNonDeprecatedWithoutCustomName(elem))
1542-
continue;
1543-
} else {
1544-
if (elem->hasAttr<clang::SwiftNameAttr>())
1545-
continue;
1546-
}
1547-
1548-
commonPrefix = getCommonWordPrefix(commonPrefix, elem->getName(),
1549-
followedByNonIdentifier);
1550-
if (commonPrefix.empty())
1551-
break;
1552-
}
1553-
1554-
if (!commonPrefix.empty()) {
1555-
StringRef checkPrefix = commonPrefix;
1556-
1557-
// Account for the 'kConstant' naming convention on enumerators.
1558-
if (checkPrefix[0] == 'k') {
1559-
bool canDropK;
1560-
if (checkPrefix.size() >= 2)
1561-
canDropK = clang::isUppercase(checkPrefix[1]);
1562-
else
1563-
canDropK = !followedByNonIdentifier;
1564-
1565-
if (canDropK)
1566-
checkPrefix = checkPrefix.drop_front();
1567-
}
1568-
1569-
// Don't use importFullName() here, we want to ignore the swift_name
1570-
// and swift_private attributes.
1571-
StringRef enumNameStr = decl->getName();
1572-
StringRef commonWithEnum = getCommonPluralPrefix(checkPrefix,
1573-
enumNameStr);
1574-
size_t delta = commonPrefix.size() - checkPrefix.size();
1575-
1576-
// Account for the 'EnumName_Constant' convention on enumerators.
1577-
if (commonWithEnum.size() < checkPrefix.size() &&
1578-
checkPrefix[commonWithEnum.size()] == '_' &&
1579-
!followedByNonIdentifier) {
1580-
delta += 1;
1581-
}
1582-
1583-
commonPrefix = commonPrefix.slice(0, commonWithEnum.size() + delta);
1584-
}
1585-
1586-
if (useCache)
1587-
EnumConstantNamePrefixes.insert({decl, commonPrefix});
1588-
return commonPrefix;
1589-
}
1590-
15911363
/// Determine whether the given Clang selector matches the given
15921364
/// selector pieces.
15931365
static bool isNonNullarySelector(clang::Selector selector,
@@ -2038,7 +1810,7 @@ auto ClangImporter::Implementation::importFullName(
20381810
// scope, depending how their enclosing enumeration is imported.
20391811
if (isa<clang::EnumConstantDecl>(D)) {
20401812
auto enumDecl = cast<clang::EnumDecl>(dc);
2041-
switch (classifyEnum(clangSema.getPreprocessor(), enumDecl)) {
1813+
switch (getEnumKind(enumDecl, &clangSema.getPreprocessor())) {
20421814
case EnumKind::Enum:
20431815
case EnumKind::Options:
20441816
// Enums are mapped to Swift enums, Options to Swift option sets.
@@ -2289,7 +2061,8 @@ auto ClangImporter::Implementation::importFullName(
22892061
// Enumeration constants may have common prefixes stripped.
22902062
if (isa<clang::EnumConstantDecl>(D)) {
22912063
auto enumDecl = cast<clang::EnumDecl>(D->getDeclContext());
2292-
StringRef removePrefix = getEnumConstantNamePrefix(clangSema, enumDecl);
2064+
StringRef removePrefix =
2065+
getEnumConstantNamePrefix(enumDecl, &clangSema.getPreprocessor());
22932066
if (baseName.startswith(removePrefix))
22942067
baseName = baseName.substr(removePrefix.size());
22952068
}
@@ -2382,15 +2155,15 @@ auto ClangImporter::Implementation::importFullName(
23822155

23832156
// Local function to determine whether the given declaration is subject to
23842157
// a swift_private attribute.
2385-
auto hasSwiftPrivate = [&clangSema](const clang::NamedDecl *D) {
2158+
auto hasSwiftPrivate = [&clangSema, this](const clang::NamedDecl *D) {
23862159
if (D->hasAttr<clang::SwiftPrivateAttr>())
23872160
return true;
23882161

23892162
// Enum constants that are not imported as members should be considered
23902163
// private if the parent enum is marked private.
23912164
if (auto *ECD = dyn_cast<clang::EnumConstantDecl>(D)) {
23922165
auto *ED = cast<clang::EnumDecl>(ECD->getDeclContext());
2393-
switch (classifyEnum(clangSema.getPreprocessor(), ED)) {
2166+
switch (getEnumKind(ED, &clangSema.getPreprocessor())) {
23942167
case EnumKind::Constants:
23952168
case EnumKind::Unknown:
23962169
if (ED->hasAttr<clang::SwiftPrivateAttr>())

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ STATISTIC(NumFactoryMethodsAsInitializers,
5353
"# of factory methods mapped to initializers");
5454

5555
using namespace swift;
56+
using namespace importer;
5657

5758
namespace swift {
5859
namespace inferred_attributes {
@@ -1166,8 +1167,6 @@ static void inferProtocolMemberAvailability(ClangImporter::Implementation &impl,
11661167
}
11671168

11681169
namespace {
1169-
typedef ClangImporter::Implementation::EnumKind EnumKind;
1170-
11711170
/// \brief Convert Clang declarations into the corresponding Swift
11721171
/// declarations.
11731172
class SwiftDeclConverter
@@ -1871,7 +1870,7 @@ namespace {
18711870

18721871
// Create the enum declaration and record it.
18731872
NominalTypeDecl *result;
1874-
auto enumKind = Impl.classifyEnum(Impl.getClangPreprocessor(), decl);
1873+
auto enumKind = Impl.getEnumKind(decl);
18751874
switch (enumKind) {
18761875
case EnumKind::Constants: {
18771876
// There is no declaration. Rather, the type is mapped to the
@@ -2312,7 +2311,7 @@ namespace {
23122311
if (name.empty())
23132312
return nullptr;
23142313

2315-
switch (Impl.classifyEnum(Impl.getClangPreprocessor(), clangEnum)) {
2314+
switch (Impl.getEnumKind(clangEnum)) {
23162315
case EnumKind::Constants: {
23172316
// The enumeration was simply mapped to an integral type. Create a
23182317
// constant with that integral type.
@@ -4925,37 +4924,6 @@ namespace {
49254924
};
49264925
}
49274926

4928-
/// \brief Classify the given Clang enumeration to describe how to import it.
4929-
EnumKind ClangImporter::Implementation::
4930-
classifyEnum(clang::Preprocessor &pp, const clang::EnumDecl *decl) {
4931-
// Anonymous enumerations simply get mapped to constants of the
4932-
// underlying type of the enum, because there is no way to conjure up a
4933-
// name for the Swift type.
4934-
if (!decl->hasNameForLinkage())
4935-
return EnumKind::Constants;
4936-
4937-
// Was the enum declared using *_ENUM or *_OPTIONS?
4938-
// FIXME: Use Clang attributes instead of grovelling the macro expansion loc.
4939-
auto loc = decl->getLocStart();
4940-
if (loc.isMacroID()) {
4941-
StringRef MacroName = pp.getImmediateMacroName(loc);
4942-
if (MacroName == "CF_ENUM" || MacroName == "__CF_NAMED_ENUM" ||
4943-
MacroName == "OBJC_ENUM" ||
4944-
MacroName == "SWIFT_ENUM" || MacroName == "SWIFT_ENUM_NAMED")
4945-
return EnumKind::Enum;
4946-
if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS"
4947-
|| MacroName == "SWIFT_OPTIONS")
4948-
return EnumKind::Options;
4949-
}
4950-
4951-
// Hardcode a particular annoying case in the OS X headers.
4952-
if (decl->getName() == "DYLD_BOOL")
4953-
return EnumKind::Enum;
4954-
4955-
// Fall back to the 'Unknown' path.
4956-
return EnumKind::Unknown;
4957-
}
4958-
49594927
Decl *ClangImporter::Implementation::importDeclCached(
49604928
const clang::NamedDecl *ClangDecl) {
49614929
auto Known = ImportedDecls.find(ClangDecl->getCanonicalDecl());

0 commit comments

Comments
 (0)