Skip to content

Commit 1a2c0b0

Browse files
committed
[Clang importer] Be more careful when stripping module prefixes.
Clean up our handling of the removal of the "NS" prefix from Foundation-defined entities in a few ways: * If the "NS" is followed by a "_", strip that too * Make sure that the result is still an identifier ("NS123" shouldn't become "123"!) * Don't lowercase ALL_CAPS_NAMES
1 parent 8e000fb commit 1a2c0b0

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,13 +2009,49 @@ considerErrorImport(ClangImporter::Implementation &importer,
20092009
return None;
20102010
}
20112011

2012-
/// Determine whether we are allowed to strip the module prefix from
2013-
/// an entity with the given name.
2014-
static bool canStripModulePrefix(StringRef name) {
2015-
if (auto known = getKnownFoundationEntity(name))
2016-
return !nameConflictsWithStandardLibrary(*known);
2017-
2018-
return true;
2012+
// Determine whether we should strip the module prefix from a global-scope
2013+
// entity in the given module and with the given base name.
2014+
static unsigned stripModulePrefixLength(
2015+
const llvm::StringMap<std::string> &modulePrefixes,
2016+
StringRef moduleName,
2017+
StringRef baseName) {
2018+
// Do we have a module prefix for this module?
2019+
auto prefixPos = modulePrefixes.find(moduleName);
2020+
if (prefixPos == modulePrefixes.end()) return 0;
2021+
2022+
// Is the prefix actually there?
2023+
if (!baseName.startswith(prefixPos->second)) return 0;
2024+
2025+
// Check whether this is a known Foundation entity that conflicts with the
2026+
// standard library.
2027+
if (auto known = getKnownFoundationEntity(baseName))
2028+
if (nameConflictsWithStandardLibrary(*known))
2029+
return 0;
2030+
2031+
// If the character following the prefix is a '_', eat that, too.
2032+
unsigned prefixLen = prefixPos->second.size();
2033+
if (prefixLen < baseName.size() && baseName[prefixLen] == '_')
2034+
++prefixLen;
2035+
2036+
// Make sure we're left with an identifier.
2037+
if (prefixLen == baseName.size() ||
2038+
!Lexer::isIdentifier(baseName.substr(prefixLen)))
2039+
return 0;
2040+
2041+
// We can strip the module prefix.
2042+
return prefixLen;
2043+
}
2044+
2045+
/// Determine whether we should lowercase the first word of the given value
2046+
/// name.
2047+
static bool shouldLowercaseValueName(StringRef name) {
2048+
// If we see any lowercase characters, we can lowercase.
2049+
for (auto c : name) {
2050+
if (clang::isLowercase(c)) return true;
2051+
}
2052+
2053+
// Otherwise, lowercasing will either be a no-op or we have ALL_CAPS.
2054+
return false;
20192055
}
20202056

20212057
/// Returns true if it is expected that the macro is ignored.
@@ -2529,15 +2565,13 @@ auto ClangImporter::Implementation::importFullName(
25292565
moduleName = module->getTopLevelModuleName();
25302566
else
25312567
moduleName = owningD->getASTContext().getLangOpts().CurrentModule;
2532-
auto prefixPos = ModulePrefixes.find(moduleName);
2533-
if (prefixPos != ModulePrefixes.end() &&
2534-
canStripModulePrefix(baseName) &&
2535-
baseName.startswith(prefixPos->second)) {
2568+
if (unsigned prefixLen = stripModulePrefixLength(ModulePrefixes,
2569+
moduleName, baseName)) {
25362570
// Strip off the prefix.
2537-
baseName = baseName.substr(prefixPos->second.size());
2571+
baseName = baseName.substr(prefixLen);
25382572

25392573
// If the result is a value, lowercase it.
2540-
if (isa<clang::ValueDecl>(D))
2574+
if (isa<clang::ValueDecl>(D) && shouldLowercaseValueName(baseName))
25412575
baseName = camel_case::toLowercaseWord(baseName,
25422576
omitNeedlessWordsScratch);
25432577
}

test/IDE/print_omit_needless_words.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,18 @@
160160
// CHECK-FOUNDATION: let globalConstant: String
161161
// CHECK-FOUNDATION: func globalFunction()
162162

163+
// Cannot strip because we end up with something that isn't an identifier
164+
// CHECK-FOUNDATION: func NS123()
165+
166+
// Strip prefix, but don't lowercase ALL_CAPS.
167+
// CHECK-FOUNDATION: func YELLING()
168+
169+
// Strip prefix along with '_', but don't lowercase ALL_CAPS.
170+
// CHECK-FOUNDATION: func SCREAMING()
171+
172+
// Don't leave just a '_'.
173+
// CHECK-FOUNDATION: func NS_()
174+
163175
// Lowercasing initialisms with plurals.
164176
// CHECK-FOUNDATION: var urlsInText: [URL] { get }
165177

test/Inputs/clang-importer-sdk/usr/include/Foundation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,10 @@ int variadicFunc2(int A, ...);
992992

993993
extern NSString *NSGlobalConstant;
994994
extern void NSGlobalFunction(void);
995+
extern void NS123(void);
996+
extern void NSYELLING(void);
997+
extern void NS_SCREAMING(void);
998+
extern void NS_(void);
995999

9961000
@interface NSString (URLExtraction)
9971001
@property (nonnull,copy,readonly) NSArray<NSURL *> *URLsInText;

0 commit comments

Comments
 (0)