Skip to content

Commit 106bf80

Browse files
committed
Omit needless words: don't prune "properties" of the context from the base name.
The properties of a context indicate those things that are considered "contained within" the context (among other things). This helps us avoid producing overly-generic names when we identify a redundancy in the base name. For example, NSView contains the following: var gestureRecognizers: [NSGestureRecognizer] func addGestureRecognizer(gestureRecognizer: NSGestureRecognizer) func removeGestureRecognizer(gestureRecognizer: NSGestureRecognizer) Normally, omit-needless-words would prune the two method names down to "add" and "remove", respectively, because they restate type information. However, this pruning is not ideal, because a view isn't primarily a collection of gesture recognizers. Use the presence of the property "gestureRecognizers" to indicate that we should not strip "gestureRecognizer" or "gestureRecognizers" from the base names of methods within that class (or its subclasses). Note that there is more work to do here to properly deal with API evolution: a newly-added property shouldn't have any effect on existing APIs. We should use availability information here, and only consider properties introduced no later than the entity under consideration.
1 parent 9491ec4 commit 106bf80

File tree

9 files changed

+274
-10
lines changed

9 files changed

+274
-10
lines changed

include/swift/AST/ASTContext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
namespace clang {
4545
class Decl;
4646
class MacroInfo;
47+
class ObjCInterfaceDecl;
4748
}
4849

4950
namespace swift {
@@ -65,6 +66,7 @@ namespace swift {
6566
class FunctionType;
6667
class ArchetypeType;
6768
class Identifier;
69+
class InheritedNameSet;
6870
class ModuleDecl;
6971
class ModuleLoader;
7072
class NominalTypeDecl;
@@ -798,6 +800,14 @@ class ASTContext {
798800
void setArchetypeBuilder(CanGenericSignature sig,
799801
ModuleDecl *mod,
800802
std::unique_ptr<ArchetypeBuilder> builder);
803+
804+
/// Retrieve the inherited name set for the given class.
805+
const InheritedNameSet *getAllPropertyNames(ClassDecl *classDecl);
806+
807+
/// Retrieve the inherited name set for the given Objective-C class.
808+
const InheritedNameSet *getAllPropertyNames(
809+
clang::ObjCInterfaceDecl *classDecl);
810+
801811
private:
802812
friend class Decl;
803813
Optional<RawComment> getRawComment(const Decl *D);

include/swift/Basic/StringExtras.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ADT/SmallVector.h"
2323
#include "llvm/ADT/StringMap.h"
2424
#include "llvm/ADT/StringRef.h"
25+
#include "llvm/ADT/StringSet.h"
2526
#include "llvm/Support/Allocator.h"
2627
#include <iterator>
2728
#include <string>
@@ -365,6 +366,22 @@ class StringScratchSpace {
365366
StringRef copyString(StringRef string);
366367
};
367368

369+
/// Describes a set of names with an inheritance relationship.
370+
class InheritedNameSet {
371+
const InheritedNameSet *Parent;
372+
llvm::StringSet<> Names;
373+
374+
public:
375+
/// Construct a new inherited name set with the given parent.
376+
explicit InheritedNameSet(const InheritedNameSet *parent) : Parent(parent) { }
377+
378+
// Add a new name to the set.
379+
void add(StringRef name);
380+
381+
/// Determine whether this set includes the given name.
382+
bool contains(StringRef name) const;
383+
};
384+
368385
/// Omit needless words for a declaration.
369386
///
370387
/// \param baseName The base name of the declaration. This value may be
@@ -388,6 +405,8 @@ class StringScratchSpace {
388405
///
389406
/// \param isProperty Whether this is the name of a property.
390407
///
408+
/// \param allPropertyNames The set of property names in the enclosing context.
409+
///
391410
/// \param scratch Scratch space that will be used for modifications beyond
392411
/// just chopping names.
393412
///
@@ -400,6 +419,7 @@ bool omitNeedlessWords(StringRef &baseName,
400419
ArrayRef<OmissionTypeName> paramTypes,
401420
bool returnsSelf,
402421
bool isProperty,
422+
const InheritedNameSet *allPropertyNames,
403423
StringScratchSpace &scratch);
404424
}
405425

lib/AST/ASTContext.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "swift/AST/RawComment.h"
3131
#include "swift/AST/TypeCheckerDebugConsumer.h"
3232
#include "swift/Basic/SourceManager.h"
33+
#include "swift/Basic/StringExtras.h"
34+
#include "clang/AST/DeclObjC.h"
3335
#include "clang/Lex/HeaderSearch.h"
3436
#include "clang/Lex/Preprocessor.h"
3537
#include "llvm/Support/Allocator.h"
@@ -232,6 +234,16 @@ struct ASTContext::Implementation {
232234
llvm::DenseMap<std::pair<GenericSignature *, ModuleDecl *>,
233235
std::unique_ptr<ArchetypeBuilder>> ArchetypeBuilders;
234236

237+
/// The set of property names that show up in the defining module of a
238+
/// class.
239+
llvm::DenseMap<const ClassDecl *, std::unique_ptr<InheritedNameSet>>
240+
AllProperties;
241+
242+
/// The set of property names that show up in the defining module of
243+
/// an Objective-C class.
244+
llvm::DenseMap<const clang::ObjCInterfaceDecl *,
245+
std::unique_ptr<InheritedNameSet>> AllPropertiesObjC;
246+
235247
/// \brief Structure that captures data that is segregated into different
236248
/// arenas.
237249
struct Arena {
@@ -3501,3 +3513,116 @@ void ASTContext::unregisterLazyArchetype(const ArchetypeType *archetype) {
35013513
assert(known != Impl.LazyArchetypes.end());
35023514
Impl.LazyArchetypes.erase(known);
35033515
}
3516+
3517+
const InheritedNameSet *ASTContext::getAllPropertyNames(ClassDecl *classDecl) {
3518+
// If this class was defined in Objective-C, perform the lookup based on
3519+
// the Objective-C class.
3520+
if (auto objcClass = dyn_cast_or_null<clang::ObjCInterfaceDecl>(
3521+
classDecl->getClangDecl())) {
3522+
return getAllPropertyNames(
3523+
const_cast<clang::ObjCInterfaceDecl *>(objcClass));
3524+
}
3525+
3526+
// If we already have this information, return it.
3527+
auto known = Impl.AllProperties.find(classDecl);
3528+
if (known != Impl.AllProperties.end()) return known->second.get();
3529+
3530+
// Otherwise, get information from our superclass first.
3531+
if (auto resolver = getLazyResolver())
3532+
resolver->resolveSuperclass(classDecl);
3533+
3534+
const InheritedNameSet *parentSet = nullptr;
3535+
if (auto superclass = classDecl->getSuperclass()) {
3536+
if (auto superclassDecl = superclass->getClassOrBoundGenericClass()) {
3537+
parentSet = getAllPropertyNames(superclassDecl);
3538+
}
3539+
}
3540+
3541+
// Create the set of properties.
3542+
known = Impl.AllProperties.insert(
3543+
{classDecl, llvm::make_unique<InheritedNameSet>(parentSet) }).first;
3544+
3545+
// Local function to add properties from the given set.
3546+
auto addProperties = [&](DeclRange members) {
3547+
for (auto member : members) {
3548+
auto var = dyn_cast<VarDecl>(member);
3549+
if (!var || var->getName().empty()) continue;
3550+
3551+
known->second->add(var->getName().str());
3552+
}
3553+
};
3554+
3555+
// Collect property names from the class.
3556+
addProperties(classDecl->getMembers());
3557+
3558+
// Collect property names from all extensions in the same module as the class.
3559+
auto module = classDecl->getParentModule();
3560+
for (auto ext : classDecl->getExtensions()) {
3561+
if (ext->getParentModule() != module) continue;
3562+
addProperties(ext->getMembers());
3563+
}
3564+
3565+
return known->second.get();
3566+
}
3567+
3568+
const InheritedNameSet *ASTContext::getAllPropertyNames(
3569+
clang::ObjCInterfaceDecl *classDecl) {
3570+
classDecl = classDecl->getCanonicalDecl();
3571+
3572+
// If we already have this information, return it.
3573+
auto known = Impl.AllPropertiesObjC.find(classDecl);
3574+
if (known != Impl.AllPropertiesObjC.end()) return known->second.get();
3575+
3576+
// Otherwise, get information from our superclass first.
3577+
const InheritedNameSet *parentSet = nullptr;
3578+
if (auto superclassDecl = classDecl->getSuperClass()) {
3579+
parentSet = getAllPropertyNames(superclassDecl);
3580+
}
3581+
3582+
// Create the set of properties.
3583+
known = Impl.AllPropertiesObjC.insert(
3584+
{classDecl, llvm::make_unique<InheritedNameSet>(parentSet) }).first;
3585+
3586+
// Local function to add properties from the given set.
3587+
auto addProperties = [&](clang::DeclContext::decl_range members) {
3588+
for (auto member : members) {
3589+
// Add Objective-C property names.
3590+
if (auto property = dyn_cast<clang::ObjCPropertyDecl>(member)) {
3591+
known->second->add(property->getName());
3592+
continue;
3593+
}
3594+
3595+
// Add no-parameter, non-void method names.
3596+
if (auto method = dyn_cast<clang::ObjCMethodDecl>(member)) {
3597+
if (method->getSelector().isUnarySelector() &&
3598+
!method->getReturnType()->isVoidType()) {
3599+
known->second->add(method->getSelector().getNameForSlot(0));
3600+
continue;
3601+
}
3602+
}
3603+
}
3604+
};
3605+
3606+
// Dig out the class definition.
3607+
auto classDef = classDecl->getDefinition();
3608+
if (!classDef) return known->second.get();
3609+
3610+
// Collect property names from the class definition.
3611+
addProperties(classDef->decls());
3612+
3613+
// Dig out the module that owns the class definition.
3614+
auto module = classDef->getImportedOwningModule();
3615+
if (module) module = module->getTopLevelModule();
3616+
3617+
// Collect property names from all categories and extensions in the same
3618+
// module as the class.
3619+
for (auto category : classDef->known_categories()) {
3620+
auto categoryModule = category->getImportedOwningModule();
3621+
if (categoryModule) categoryModule = categoryModule->getTopLevelModule();
3622+
if (module != categoryModule) continue;
3623+
3624+
addProperties(category->decls());
3625+
}
3626+
3627+
return known->second.get();
3628+
}

lib/Basic/StringExtras.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,20 @@ StringRef StringScratchSpace::copyString(StringRef string) {
421421
return StringRef(static_cast<char *>(memory), string.size());
422422
}
423423

424+
void InheritedNameSet::add(StringRef name) {
425+
Names.insert(name);
426+
}
427+
428+
bool InheritedNameSet::contains(StringRef name) const {
429+
auto set = this;
430+
do {
431+
if (set->Names.count(name) > 0) return true;
432+
set = set->Parent;
433+
} while (set);
434+
435+
return false;
436+
}
437+
424438
/// Wrapper for camel_case::toLowercaseWord that uses string scratch space.
425439
static StringRef toLowercaseWord(StringRef string, StringScratchSpace &scratch){
426440
llvm::SmallString<32> scratchStr;
@@ -477,6 +491,7 @@ static bool isVacuousName(StringRef name) {
477491
static StringRef omitNeedlessWords(StringRef name,
478492
OmissionTypeName typeName,
479493
NameRole role,
494+
const InheritedNameSet *allPropertyNames,
480495
StringScratchSpace &scratch) {
481496
// If we have no name or no type name, there is nothing to do.
482497
if (name.empty() || typeName.empty()) return name;
@@ -540,7 +555,7 @@ static StringRef omitNeedlessWords(StringRef name,
540555
= name.substr(0, nameWordRevIter.base().getPosition()-1);
541556
auto newShortenedNameWord
542557
= omitNeedlessWords(shortenedNameWord, typeName.CollectionElement,
543-
NameRole::Partial, scratch);
558+
NameRole::Partial, allPropertyNames, scratch);
544559
if (shortenedNameWord != newShortenedNameWord) {
545560
anyMatches = true;
546561
unsigned targetSize = newShortenedNameWord.size();
@@ -604,6 +619,38 @@ static StringRef omitNeedlessWords(StringRef name,
604619

605620
case PartOfSpeech::Verb:
606621
case PartOfSpeech::Gerund:
622+
// Don't prune redundant type information from the base name if
623+
// there is a corresponding property (either singular or plural).
624+
if (allPropertyNames && role == NameRole::BaseName) {
625+
SmallString<16> localScratch;
626+
auto removedText = name.substr(nameWordRevIter.base().getPosition());
627+
auto removedName = camel_case::toLowercaseWord(removedText,
628+
localScratch);
629+
630+
// A property with exactly this name.
631+
if (allPropertyNames->contains(removedName)) return name;
632+
633+
// From here on, we'll be working with scratch space.
634+
if (removedName.data() != localScratch.data())
635+
localScratch = removedName;
636+
637+
if (localScratch.back() == 'y') {
638+
// If the last letter is a 'y', try 'ies'.
639+
localScratch.pop_back();
640+
localScratch += "ies";
641+
if (allPropertyNames->contains(localScratch)) return name;
642+
} else {
643+
// Otherwise, add an 's' and try again.
644+
localScratch += 's';
645+
if (allPropertyNames->contains(localScratch)) return name;
646+
647+
// Alternatively, try to add 'es'.
648+
localScratch.pop_back();
649+
localScratch += "es";
650+
if (allPropertyNames->contains(localScratch)) return name;
651+
}
652+
}
653+
607654
// Strip off the part of the name that is redundant with
608655
// type information.
609656
name = name.substr(0, nameWordRevIter.base().getPosition());
@@ -693,6 +740,7 @@ bool swift::omitNeedlessWords(StringRef &baseName,
693740
ArrayRef<OmissionTypeName> paramTypes,
694741
bool returnsSelf,
695742
bool isProperty,
743+
const InheritedNameSet *allPropertyNames,
696744
StringScratchSpace &scratch) {
697745
bool anyChanges = false;
698746

@@ -735,6 +783,7 @@ bool swift::omitNeedlessWords(StringRef &baseName,
735783
baseName,
736784
returnsSelf ? contextType : resultType,
737785
NameRole::Property,
786+
allPropertyNames,
738787
scratch);
739788
if (newBaseName != baseName) {
740789
baseName = newBaseName;
@@ -768,7 +817,11 @@ bool swift::omitNeedlessWords(StringRef &baseName,
768817

769818
// Omit needless words from the name.
770819
StringRef name = role == NameRole::BaseName ? baseName : argNames[i];
771-
StringRef newName = ::omitNeedlessWords(name, paramTypes[i], role, scratch);
820+
StringRef newName = ::omitNeedlessWords(name, paramTypes[i], role,
821+
role == NameRole::BaseName
822+
? allPropertyNames
823+
: nullptr,
824+
scratch);
772825

773826
// Did the name change?
774827
if (name != newName)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1365,9 +1365,19 @@ ClangImporter::Implementation::importName(const clang::NamedDecl *D,
13651365
objcProperty->getType());
13661366
StringScratchSpace scratch;
13671367
StringRef name = result.str();
1368+
1369+
// Find the property names.
1370+
const InheritedNameSet *allPropertyNames = nullptr;
1371+
if (!contextType.isNull()) {
1372+
if (auto objcPtrType = contextType->getAsObjCInterfacePointerType())
1373+
if (auto objcClassDecl = objcPtrType->getInterfaceDecl())
1374+
allPropertyNames = SwiftContext.getAllPropertyNames(
1375+
objcClassDecl);
1376+
}
1377+
13681378
if (omitNeedlessWords(name, { }, "", propertyTypeName, contextTypeName,
13691379
{ }, /*returnsSelf=*/false, /*isProperty=*/true,
1370-
scratch)) {
1380+
allPropertyNames, scratch)) {
13711381
result = SwiftContext.getIdentifier(name);
13721382
}
13731383
}

lib/ClangImporter/ImportType.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,12 +2088,21 @@ DeclName ClangImporter::Implementation::omitNeedlessWordsInFunctionName(
20882088
// Omit needless words.
20892089
StringRef baseName = name.getBaseName().str();
20902090
StringScratchSpace scratch;
2091+
2092+
// Find the property names.
2093+
const InheritedNameSet *allPropertyNames = nullptr;
2094+
auto contextType = getClangDeclContextType(dc);
2095+
if (!contextType.isNull()) {
2096+
if (auto objcPtrType = contextType->getAsObjCInterfacePointerType())
2097+
if (auto objcClassDecl = objcPtrType->getInterfaceDecl())
2098+
allPropertyNames = SwiftContext.getAllPropertyNames(objcClassDecl);
2099+
}
2100+
20912101
if (!omitNeedlessWords(baseName, argNames, firstParamName,
20922102
getClangTypeNameForOmission(resultType),
2093-
getClangTypeNameForOmission(
2094-
getClangDeclContextType(dc)),
2103+
getClangTypeNameForOmission(contextType),
20952104
paramTypes, returnsSelf, /*isProperty=*/false,
2096-
scratch))
2105+
allPropertyNames, scratch))
20972106
return name;
20982107

20992108
/// Retrieve a replacement identifier.

0 commit comments

Comments
 (0)