@@ -1349,6 +1349,232 @@ static DeclName parseDeclName(ASTContext &ctx, StringRef Name) {
1349
1349
return DeclName (ctx, BaseID, ParamIDs);
1350
1350
}
1351
1351
1352
+ // / \brief Returns the common prefix of two strings at camel-case word
1353
+ // / granularity.
1354
+ // /
1355
+ // / For example, given "NSFooBar" and "NSFooBas", returns "NSFoo"
1356
+ // / (not "NSFooBa"). The returned StringRef is a slice of the "a" argument.
1357
+ // /
1358
+ // / If either string has a non-identifier character immediately after the
1359
+ // / prefix, \p followedByNonIdentifier will be set to \c true. If both strings
1360
+ // / have identifier characters after the prefix, \p followedByNonIdentifier will
1361
+ // / be set to \c false. Otherwise, \p followedByNonIdentifier will not be
1362
+ // / changed from its initial value.
1363
+ // /
1364
+ // / This is used to derive the common prefix of enum constants so we can elide
1365
+ // / it from the Swift interface.
1366
+ static StringRef getCommonWordPrefix (StringRef a, StringRef b,
1367
+ bool &followedByNonIdentifier) {
1368
+ auto aWords = camel_case::getWords (a), bWords = camel_case::getWords (b);
1369
+ auto aI = aWords.begin (), aE = aWords.end (),
1370
+ bI = bWords.begin (), bE = bWords.end ();
1371
+
1372
+ unsigned prevLength = 0 ;
1373
+ unsigned prefixLength = 0 ;
1374
+ for ( ; aI != aE && bI != bE; ++aI, ++bI) {
1375
+ if (*aI != *bI) {
1376
+ followedByNonIdentifier = false ;
1377
+ break ;
1378
+ }
1379
+
1380
+ prevLength = prefixLength;
1381
+ prefixLength = aI.getPosition () + aI->size ();
1382
+ }
1383
+
1384
+ // Avoid creating a prefix where the rest of the string starts with a number.
1385
+ if ((aI != aE && !Lexer::isIdentifier (*aI)) ||
1386
+ (bI != bE && !Lexer::isIdentifier (*bI))) {
1387
+ followedByNonIdentifier = true ;
1388
+ prefixLength = prevLength;
1389
+ }
1390
+
1391
+ return a.slice (0 , prefixLength);
1392
+ }
1393
+
1394
+ // / Returns the common word-prefix of two strings, allowing the second string
1395
+ // / to be a common English plural form of the first.
1396
+ // /
1397
+ // / For example, given "NSProperty" and "NSProperties", the full "NSProperty"
1398
+ // / is returned. Given "NSMagicArmor" and "NSMagicArmory", only
1399
+ // / "NSMagic" is returned.
1400
+ // /
1401
+ // / The "-s", "-es", and "-ies" patterns cover every plural NS_OPTIONS name
1402
+ // / in Cocoa and Cocoa Touch.
1403
+ // /
1404
+ // / \see getCommonWordPrefix
1405
+ static StringRef getCommonPluralPrefix (StringRef singular, StringRef plural) {
1406
+ assert (!plural.empty ());
1407
+
1408
+ if (singular.empty ())
1409
+ return singular;
1410
+
1411
+ bool ignored;
1412
+ StringRef commonPrefix = getCommonWordPrefix (singular, plural, ignored);
1413
+ if (commonPrefix.size () == singular.size () || plural.back () != ' s' )
1414
+ return commonPrefix;
1415
+
1416
+ StringRef leftover = singular.substr (commonPrefix.size ());
1417
+ StringRef firstLeftoverWord = camel_case::getFirstWord (leftover);
1418
+ StringRef commonPrefixPlusWord =
1419
+ singular.substr (0 , commonPrefix.size () + firstLeftoverWord.size ());
1420
+
1421
+ // Is the plural string just "[singular]s"?
1422
+ plural = plural.drop_back ();
1423
+ if (plural.endswith (firstLeftoverWord))
1424
+ return commonPrefixPlusWord;
1425
+
1426
+ if (plural.empty () || plural.back () != ' e' )
1427
+ return commonPrefix;
1428
+
1429
+ // Is the plural string "[singular]es"?
1430
+ plural = plural.drop_back ();
1431
+ if (plural.endswith (firstLeftoverWord))
1432
+ return commonPrefixPlusWord;
1433
+
1434
+ if (plural.empty () || !(plural.back () == ' i' && singular.back () == ' y' ))
1435
+ return commonPrefix;
1436
+
1437
+ // Is the plural string "[prefix]ies" and the singular "[prefix]y"?
1438
+ plural = plural.drop_back ();
1439
+ firstLeftoverWord = firstLeftoverWord.drop_back ();
1440
+ if (plural.endswith (firstLeftoverWord))
1441
+ return commonPrefixPlusWord;
1442
+
1443
+ return commonPrefix;
1444
+ }
1445
+
1446
+ StringRef ClangImporter::Implementation::getEnumConstantNamePrefix (
1447
+ const clang::EnumDecl *decl) {
1448
+ switch (classifyEnum (decl)) {
1449
+ case EnumKind::Enum:
1450
+ case EnumKind::Options:
1451
+ // Enums are mapped to Swift enums, Options to Swift option sets, both
1452
+ // of which attempt prefix-stripping.
1453
+ break ;
1454
+
1455
+ case EnumKind::Constants:
1456
+ case EnumKind::Unknown:
1457
+ // Nothing to do.
1458
+ return StringRef ();
1459
+ }
1460
+
1461
+ // If there are no enumers, there is no prefix to compute.
1462
+ auto ec = decl->enumerator_begin (), ecEnd = decl->enumerator_end ();
1463
+ if (ec == ecEnd)
1464
+ return StringRef ();
1465
+
1466
+ // If we've already computed the prefix, return it.
1467
+ auto known = EnumConstantNamePrefixes.find (decl);
1468
+ if (known != EnumConstantNamePrefixes.end ())
1469
+ return known->second ;
1470
+
1471
+ // Determine whether the given enumerator is non-deprecated and has no
1472
+ // specifically-provided name.
1473
+ auto isNonDeprecatedWithoutCustomName =
1474
+ [](const clang::EnumConstantDecl *elem) -> bool {
1475
+ if (elem->hasAttr <clang::SwiftNameAttr>())
1476
+ return false ;
1477
+
1478
+ clang::VersionTuple maxVersion{~0U , ~0U , ~0U };
1479
+ switch (elem->getAvailability (nullptr , maxVersion)) {
1480
+ case clang::AR_Available:
1481
+ case clang::AR_NotYetIntroduced:
1482
+ for (auto attr : elem->attrs ()) {
1483
+ if (auto annotate = dyn_cast<clang::AnnotateAttr>(attr)) {
1484
+ if (annotate->getAnnotation () == " swift1_unavailable" )
1485
+ return false ;
1486
+ }
1487
+ if (auto avail = dyn_cast<clang::AvailabilityAttr>(attr)) {
1488
+ if (avail->getPlatform ()->getName () == " swift" )
1489
+ return false ;
1490
+ }
1491
+ }
1492
+ return true ;
1493
+
1494
+ case clang::AR_Deprecated:
1495
+ case clang::AR_Unavailable:
1496
+ return false ;
1497
+ }
1498
+ };
1499
+
1500
+ // Move to the first non-deprecated enumerator, or non-swift_name'd
1501
+ // enumerator, if present.
1502
+ auto firstNonDeprecated = std::find_if (ec, ecEnd,
1503
+ isNonDeprecatedWithoutCustomName);
1504
+ bool hasNonDeprecated = (firstNonDeprecated != ecEnd);
1505
+ if (hasNonDeprecated) {
1506
+ ec = firstNonDeprecated;
1507
+ } else {
1508
+ // Advance to the first case without a custom name, deprecated or not.
1509
+ while (ec != ecEnd && (*ec)->hasAttr <clang::SwiftNameAttr>())
1510
+ ++ec;
1511
+ if (ec == ecEnd) {
1512
+ EnumConstantNamePrefixes.insert ({decl, StringRef ()});
1513
+ return StringRef ();
1514
+ }
1515
+ }
1516
+
1517
+ // Compute th e common prefix.
1518
+ StringRef commonPrefix = (*ec)->getName ();
1519
+ bool followedByNonIdentifier = false ;
1520
+ for (++ec; ec != ecEnd; ++ec) {
1521
+ // Skip deprecated or swift_name'd enumerators.
1522
+ const clang::EnumConstantDecl *elem = *ec;
1523
+ if (hasNonDeprecated) {
1524
+ if (!isNonDeprecatedWithoutCustomName (elem))
1525
+ continue ;
1526
+ } else {
1527
+ if (elem->hasAttr <clang::SwiftNameAttr>())
1528
+ continue ;
1529
+ }
1530
+
1531
+ commonPrefix = getCommonWordPrefix (commonPrefix, elem->getName (),
1532
+ followedByNonIdentifier);
1533
+ if (commonPrefix.empty ())
1534
+ break ;
1535
+ }
1536
+
1537
+ if (!commonPrefix.empty ()) {
1538
+ StringRef checkPrefix = commonPrefix;
1539
+
1540
+ // Account for the 'kConstant' naming convention on enumerators.
1541
+ if (checkPrefix[0 ] == ' k' ) {
1542
+ bool canDropK;
1543
+ if (checkPrefix.size () >= 2 )
1544
+ canDropK = clang::isUppercase (checkPrefix[1 ]);
1545
+ else
1546
+ canDropK = !followedByNonIdentifier;
1547
+
1548
+ if (canDropK)
1549
+ checkPrefix = checkPrefix.drop_front ();
1550
+ }
1551
+
1552
+ // Account for the enum being imported using
1553
+ // __attribute__((swift_private)). This is a little ad hoc, but it's a
1554
+ // rare case anyway.
1555
+ Identifier enumName = importFullName (decl).getBaseName ();
1556
+ StringRef enumNameStr = enumName.str ();
1557
+ if (enumNameStr.startswith (" __" ) && !checkPrefix.startswith (" __" ))
1558
+ enumNameStr = enumNameStr.drop_front (2 );
1559
+
1560
+ StringRef commonWithEnum = getCommonPluralPrefix (checkPrefix,
1561
+ enumNameStr);
1562
+ size_t delta = commonPrefix.size () - checkPrefix.size ();
1563
+
1564
+ // Account for the 'EnumName_Constant' convention on enumerators.
1565
+ if (commonWithEnum.size () < checkPrefix.size () &&
1566
+ checkPrefix[commonWithEnum.size ()] == ' _' &&
1567
+ !followedByNonIdentifier) {
1568
+ delta += 1 ;
1569
+ }
1570
+
1571
+ commonPrefix = commonPrefix.slice (0 , commonWithEnum.size () + delta);
1572
+ }
1573
+
1574
+ EnumConstantNamePrefixes.insert ({decl, commonPrefix});
1575
+ return commonPrefix;
1576
+ }
1577
+
1352
1578
DeclName ClangImporter::Implementation::importFullName (
1353
1579
const clang::NamedDecl *D,
1354
1580
bool *hasCustomName,
@@ -1433,6 +1659,16 @@ DeclName ClangImporter::Implementation::importFullName(
1433
1659
}
1434
1660
}
1435
1661
1662
+ // Perform automatic name transformations.
1663
+
1664
+ // Enumeration constants may have common prefixes stripped.
1665
+ if (isa<clang::EnumConstantDecl>(D)) {
1666
+ auto enumDecl = cast<clang::EnumDecl>(D->getDeclContext ());
1667
+ StringRef removePrefix = getEnumConstantNamePrefix (enumDecl);
1668
+ if (baseName.startswith (removePrefix))
1669
+ baseName = baseName.substr (removePrefix.size ());
1670
+ }
1671
+
1436
1672
// Local function to determine whether the given declaration is subject to
1437
1673
// a swift_private attribute.
1438
1674
auto hasSwiftPrivate = [this ](const clang::NamedDecl *D) {
@@ -1558,87 +1794,6 @@ ClangImporter::Implementation::importIdentifier(
1558
1794
return SwiftContext.getIdentifier (name);
1559
1795
}
1560
1796
1561
- Identifier
1562
- ClangImporter::Implementation::importName (const clang::NamedDecl *D,
1563
- StringRef removePrefix) {
1564
- if (auto *nameAttr = D->getAttr <clang::SwiftNameAttr>()) {
1565
- StringRef customName = nameAttr->getName ();
1566
- if (Lexer::isIdentifier (customName))
1567
- return SwiftContext.getIdentifier (customName);
1568
-
1569
- return Identifier ();
1570
- }
1571
-
1572
- Identifier result = importIdentifier (D->getIdentifier (), removePrefix);
1573
- if (result.empty ())
1574
- return result;
1575
-
1576
- auto hasSwiftPrivate = [this ](const clang::NamedDecl *D) {
1577
- if (D->hasAttr <clang::SwiftPrivateAttr>())
1578
- return true ;
1579
-
1580
- // Enum constants that are not imported as members should be considered
1581
- // private if the parent enum is marked private.
1582
- if (auto *ECD = dyn_cast<clang::EnumConstantDecl>(D)) {
1583
- auto *ED = cast<clang::EnumDecl>(ECD->getDeclContext ());
1584
- switch (classifyEnum (ED)) {
1585
- case EnumKind::Constants:
1586
- case EnumKind::Unknown:
1587
- if (ED->hasAttr <clang::SwiftPrivateAttr>())
1588
- return true ;
1589
- if (auto *enumTypedef = ED->getTypedefNameForAnonDecl ())
1590
- if (enumTypedef->hasAttr <clang::SwiftPrivateAttr>())
1591
- return true ;
1592
- break ;
1593
-
1594
- case EnumKind::Enum:
1595
- case EnumKind::Options:
1596
- break ;
1597
- }
1598
- }
1599
-
1600
- return false ;
1601
- };
1602
-
1603
- if (hasSwiftPrivate (D) && D->getDeclName ().isIdentifier ()) {
1604
- SmallString<64 > name{" __" };
1605
- name += result.str ();
1606
- result = SwiftContext.getIdentifier (name.str ());
1607
- }
1608
-
1609
- // Omit needless words from properties.
1610
- if (OmitNeedlessWords) {
1611
- if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(D)) {
1612
- auto contextType = getClangDeclContextType (D->getDeclContext ());
1613
- if (!contextType.isNull ()) {
1614
- auto contextTypeName = getClangTypeNameForOmission (contextType);
1615
- auto propertyTypeName = getClangTypeNameForOmission (
1616
- objcProperty->getType ());
1617
- StringScratchSpace scratch;
1618
- StringRef name = result.str ();
1619
-
1620
- // Find the property names.
1621
- const InheritedNameSet *allPropertyNames = nullptr ;
1622
- if (!contextType.isNull ()) {
1623
- if (auto objcPtrType = contextType->getAsObjCInterfacePointerType ())
1624
- if (auto objcClassDecl = objcPtrType->getInterfaceDecl ())
1625
- allPropertyNames = SwiftContext.getAllPropertyNames (
1626
- objcClassDecl,
1627
- /* forInstance=*/ true );
1628
- }
1629
-
1630
- if (omitNeedlessWords (name, { }, " " , propertyTypeName, contextTypeName,
1631
- { }, /* returnsSelf=*/ false , /* isProperty=*/ true ,
1632
- allPropertyNames, scratch)) {
1633
- result = SwiftContext.getIdentifier (name);
1634
- }
1635
- }
1636
- }
1637
- }
1638
-
1639
- return result;
1640
- }
1641
-
1642
1797
// / Import an argument name.
1643
1798
static Identifier importArgName (ASTContext &ctx, StringRef name,
1644
1799
bool dropWith, bool isSwiftPrivate) {
0 commit comments