@@ -515,16 +515,26 @@ static StringRef omitNeedlessWords(StringRef name,
515515 // name.
516516 auto nameWordRevIter = nameWords.rbegin (),
517517 nameWordRevIterBegin = nameWordRevIter,
518+ firstMatchingNameWordRevIter = nameWordRevIter,
518519 nameWordRevIterEnd = nameWords.rend ();
519520 auto typeWordRevIter = typeWords.rbegin (),
520521 typeWordRevIterEnd = typeWords.rend ();
522+
523+
521524 bool anyMatches = false ;
525+ auto matched = [&] {
526+ if (anyMatches) return ;
527+
528+ anyMatches = true ;
529+ firstMatchingNameWordRevIter = nameWordRevIter;
530+ };
531+
522532 while (nameWordRevIter != nameWordRevIterEnd &&
523533 typeWordRevIter != typeWordRevIterEnd) {
524534 // If the names match, continue.
525535 auto nameWord = *nameWordRevIter;
526536 if (matchNameWordToTypeWord (nameWord, *typeWordRevIter)) {
527- anyMatches = true ;
537+ matched () ;
528538 ++nameWordRevIter;
529539 ++typeWordRevIter;
530540 continue ;
@@ -539,7 +549,7 @@ static StringRef omitNeedlessWords(StringRef name,
539549 ++nextTypeWordRevIter;
540550 if (nextTypeWordRevIter != typeWordRevIterEnd &&
541551 matchNameWordToTypeWord (" Index" , *nextTypeWordRevIter)) {
542- anyMatches = true ;
552+ matched () ;
543553 ++nameWordRevIter;
544554 typeWordRevIter = nextTypeWordRevIter;
545555 ++typeWordRevIter;
@@ -551,7 +561,7 @@ static StringRef omitNeedlessWords(StringRef name,
551561 if (matchNameWordToTypeWord (nameWord, " Index" ) &&
552562 (matchNameWordToTypeWord (" Int" , *typeWordRevIter) ||
553563 matchNameWordToTypeWord (" Integer" , *typeWordRevIter))) {
554- anyMatches = true ;
564+ matched () ;
555565 ++nameWordRevIter;
556566 ++typeWordRevIter;
557567 continue ;
@@ -560,15 +570,15 @@ static StringRef omitNeedlessWords(StringRef name,
560570 // Special case: if the word in the name ends in 's', and we have
561571 // a collection element type, see if this is a plural.
562572 if (!typeName.CollectionElement .empty () && nameWord.size () > 2 &&
563- nameWord.back () == ' s' ) {
573+ nameWord.back () == ' s' && role != NameRole::BaseNameSelf ) {
564574 // Check <element name>s.
565575 auto shortenedNameWord
566576 = name.substr (0 , nameWordRevIter.base ().getPosition ()-1 );
567577 auto newShortenedNameWord
568578 = omitNeedlessWords (shortenedNameWord, typeName.CollectionElement ,
569579 NameRole::Partial, allPropertyNames, scratch);
570580 if (shortenedNameWord != newShortenedNameWord) {
571- anyMatches = true ;
581+ matched () ;
572582 unsigned targetSize = newShortenedNameWord.size ();
573583 while (nameWordRevIter.base ().getPosition () > targetSize)
574584 ++nameWordRevIter;
@@ -587,6 +597,14 @@ static StringRef omitNeedlessWords(StringRef name,
587597 }
588598 }
589599
600+ // If we're matching the base name of a method against the type of
601+ // 'Self', and we haven't matched anything yet, skip over words in
602+ // the name.
603+ if (role == NameRole::BaseNameSelf && !anyMatches) {
604+ ++nameWordRevIter;
605+ continue ;
606+ }
607+
590608 break ;
591609 }
592610
@@ -616,6 +634,28 @@ static StringRef omitNeedlessWords(StringRef name,
616634 name = name.substr (0 , nameWordRevIter.base ().getPosition ());
617635 break ;
618636
637+ case NameRole::BaseNameSelf:
638+ switch (getPartOfSpeech (*nameWordRevIter)) {
639+ case PartOfSpeech::Verb: {
640+ // Splice together the parts before and after the matched
641+ // type. For example, if we matched "ViewController" in
642+ // "dismissViewControllerAnimated", stitch together
643+ // "dismissAnimated".
644+ SmallString<16 > newName =
645+ name.substr (0 , nameWordRevIter.base ().getPosition ());
646+ newName
647+ += name.substr (firstMatchingNameWordRevIter.base ().getPosition ());
648+ name = scratch.copyString (newName);
649+ break ;
650+ }
651+
652+ case PartOfSpeech::Preposition:
653+ case PartOfSpeech::Gerund:
654+ case PartOfSpeech::Unknown:
655+ return name;
656+ }
657+ break ;
658+
619659 case NameRole::BaseName:
620660 case NameRole::FirstParameter:
621661 case NameRole::Partial:
@@ -689,6 +729,7 @@ static StringRef omitNeedlessWords(StringRef name,
689729
690730 switch (role) {
691731 case NameRole::BaseName:
732+ case NameRole::BaseNameSelf:
692733 case NameRole::Property:
693734 // If we ended up with a keyword for a property name or base name,
694735 // do nothing.
@@ -783,7 +824,17 @@ bool swift::omitNeedlessWords(StringRef &baseName,
783824 }
784825 }
785826
786- // Treat zero-parameter methods and properties the same way.
827+ // Strip the context type from the base name of a method.
828+ if (!isProperty) {
829+ StringRef newBaseName = ::omitNeedlessWords (baseName, contextType,
830+ NameRole::BaseNameSelf,
831+ nullptr , scratch);
832+ if (newBaseName != baseName) {
833+ baseName = newBaseName;
834+ anyChanges = true ;
835+ }
836+ }
837+
787838 if (paramTypes.empty ()) {
788839 if (resultTypeMatchesContext) {
789840 StringRef newBaseName = ::omitNeedlessWords (
0 commit comments