static void AddObjCKeyValueCompletions()

in tools/clang/lib/Sema/SemaCodeComplete.cpp [6395:7023]


static void AddObjCKeyValueCompletions(ObjCPropertyDecl *Property,
                                       bool IsInstanceMethod,
                                       QualType ReturnType,
                                       ASTContext &Context,
                                       VisitedSelectorSet &KnownSelectors,
                                       ResultBuilder &Results) {
  IdentifierInfo *PropName = Property->getIdentifier();
  if (!PropName || PropName->getLength() == 0)
    return;
  
  PrintingPolicy Policy = getCompletionPrintingPolicy(Results.getSema());

  // Builder that will create each code completion.
  typedef CodeCompletionResult Result;
  CodeCompletionAllocator &Allocator = Results.getAllocator();
  CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo());
  
  // The selector table.
  SelectorTable &Selectors = Context.Selectors;
  
  // The property name, copied into the code completion allocation region
  // on demand.
  struct KeyHolder {
    CodeCompletionAllocator &Allocator;
    StringRef Key;
    const char *CopiedKey;

    KeyHolder(CodeCompletionAllocator &Allocator, StringRef Key)
    : Allocator(Allocator), Key(Key), CopiedKey(nullptr) {}

    operator const char *() {
      if (CopiedKey)
        return CopiedKey;
      
      return CopiedKey = Allocator.CopyString(Key);
    }
  } Key(Allocator, PropName->getName());
  
  // The uppercased name of the property name.
  std::string UpperKey = PropName->getName();
  if (!UpperKey.empty())
    UpperKey[0] = toUppercase(UpperKey[0]);
  
  bool ReturnTypeMatchesProperty = ReturnType.isNull() ||
    Context.hasSameUnqualifiedType(ReturnType.getNonReferenceType(), 
                                   Property->getType());
  bool ReturnTypeMatchesVoid 
    = ReturnType.isNull() || ReturnType->isVoidType();
  
  // Add the normal accessor -(type)key.
  if (IsInstanceMethod &&
      KnownSelectors.insert(Selectors.getNullarySelector(PropName)).second &&
      ReturnTypeMatchesProperty && !Property->getGetterMethodDecl()) {
    if (ReturnType.isNull())
      AddObjCPassingTypeChunk(Property->getType(), /*Quals=*/0,
                              Context, Policy, Builder);
    
    Builder.AddTypedTextChunk(Key);
    Results.AddResult(Result(Builder.TakeString(), CCP_CodePattern, 
                             CXCursor_ObjCInstanceMethodDecl));
  }
  
  // If we have an integral or boolean property (or the user has provided
  // an integral or boolean return type), add the accessor -(type)isKey.
  if (IsInstanceMethod &&
      ((!ReturnType.isNull() && 
        (ReturnType->isIntegerType() || ReturnType->isBooleanType())) ||
       (ReturnType.isNull() && 
        (Property->getType()->isIntegerType() || 
         Property->getType()->isBooleanType())))) {
    std::string SelectorName = (Twine("is") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getNullarySelector(SelectorId))
            .second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("BOOL");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(
                                Allocator.CopyString(SelectorId->getName()));
      Results.AddResult(Result(Builder.TakeString(), CCP_CodePattern, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Add the normal mutator.
  if (IsInstanceMethod && ReturnTypeMatchesVoid && 
      !Property->getSetterMethodDecl()) {
    std::string SelectorName = (Twine("set") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(
                                Allocator.CopyString(SelectorId->getName()));
      Builder.AddTypedTextChunk(":");
      AddObjCPassingTypeChunk(Property->getType(), /*Quals=*/0,
                              Context, Policy, Builder);
      Builder.AddTextChunk(Key);
      Results.AddResult(Result(Builder.TakeString(), CCP_CodePattern, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Indexed and unordered accessors
  unsigned IndexedGetterPriority = CCP_CodePattern;
  unsigned IndexedSetterPriority = CCP_CodePattern;
  unsigned UnorderedGetterPriority = CCP_CodePattern;
  unsigned UnorderedSetterPriority = CCP_CodePattern;
  if (const ObjCObjectPointerType *ObjCPointer 
                    = Property->getType()->getAs<ObjCObjectPointerType>()) {
    if (ObjCInterfaceDecl *IFace = ObjCPointer->getInterfaceDecl()) {
      // If this interface type is not provably derived from a known
      // collection, penalize the corresponding completions.
      if (!InheritsFromClassNamed(IFace, "NSMutableArray")) {
        IndexedSetterPriority += CCD_ProbablyNotObjCCollection;            
        if (!InheritsFromClassNamed(IFace, "NSArray"))
          IndexedGetterPriority += CCD_ProbablyNotObjCCollection;
      }

      if (!InheritsFromClassNamed(IFace, "NSMutableSet")) {
        UnorderedSetterPriority += CCD_ProbablyNotObjCCollection;            
        if (!InheritsFromClassNamed(IFace, "NSSet"))
          UnorderedGetterPriority += CCD_ProbablyNotObjCCollection;
      }
    }
  } else {
    IndexedGetterPriority += CCD_ProbablyNotObjCCollection;
    IndexedSetterPriority += CCD_ProbablyNotObjCCollection;
    UnorderedGetterPriority += CCD_ProbablyNotObjCCollection;
    UnorderedSetterPriority += CCD_ProbablyNotObjCCollection;
  }
  
  // Add -(NSUInteger)countOf<key>
  if (IsInstanceMethod &&  
      (ReturnType.isNull() || ReturnType->isIntegerType())) {
    std::string SelectorName = (Twine("countOf") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getNullarySelector(SelectorId))
            .second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("NSUInteger");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(
                                Allocator.CopyString(SelectorId->getName()));
      Results.AddResult(Result(Builder.TakeString(), 
                               std::min(IndexedGetterPriority, 
                                        UnorderedGetterPriority),
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Indexed getters
  // Add -(id)objectInKeyAtIndex:(NSUInteger)index
  if (IsInstanceMethod &&
      (ReturnType.isNull() || ReturnType->isObjCObjectPointerType())) {
    std::string SelectorName
      = (Twine("objectIn") + UpperKey + "AtIndex").str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("id");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSUInteger");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("index");
      Results.AddResult(Result(Builder.TakeString(), IndexedGetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Add -(NSArray *)keyAtIndexes:(NSIndexSet *)indexes
  if (IsInstanceMethod &&
      (ReturnType.isNull() || 
       (ReturnType->isObjCObjectPointerType() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl()
                                                ->getName() == "NSArray"))) {
    std::string SelectorName
      = (Twine(Property->getName()) + "AtIndexes").str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("NSArray *");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
       
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSIndexSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("indexes");
      Results.AddResult(Result(Builder.TakeString(), IndexedGetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Add -(void)getKey:(type **)buffer range:(NSRange)inRange
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("get") + UpperKey).str();
    IdentifierInfo *SelectorIds[2] = {
      &Context.Idents.get(SelectorName),
      &Context.Idents.get("range")
    };
    
    if (KnownSelectors.insert(Selectors.getSelector(2, SelectorIds)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("object-type");
      Builder.AddTextChunk(" **");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("buffer");
      Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
      Builder.AddTypedTextChunk("range:");
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSRange");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("inRange");
      Results.AddResult(Result(Builder.TakeString(), IndexedGetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Mutable indexed accessors
  
  // - (void)insertObject:(type *)object inKeyAtIndex:(NSUInteger)index
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("in") + UpperKey + "AtIndex").str();
    IdentifierInfo *SelectorIds[2] = {
      &Context.Idents.get("insertObject"),
      &Context.Idents.get(SelectorName)
    };
    
    if (KnownSelectors.insert(Selectors.getSelector(2, SelectorIds)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk("insertObject:");
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("object-type");
      Builder.AddTextChunk(" *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("object");
      Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("NSUInteger");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("index");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // - (void)insertKey:(NSArray *)array atIndexes:(NSIndexSet *)indexes
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("insert") + UpperKey).str();
    IdentifierInfo *SelectorIds[2] = {
      &Context.Idents.get(SelectorName),
      &Context.Idents.get("atIndexes")
    };
    
    if (KnownSelectors.insert(Selectors.getSelector(2, SelectorIds)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSArray *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("array");
      Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
      Builder.AddTypedTextChunk("atIndexes:");
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("NSIndexSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("indexes");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // -(void)removeObjectFromKeyAtIndex:(NSUInteger)index
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName
      = (Twine("removeObjectFrom") + UpperKey + "AtIndex").str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);        
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSUInteger");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("index");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // -(void)removeKeyAtIndexes:(NSIndexSet *)indexes
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName
      = (Twine("remove") + UpperKey + "AtIndexes").str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);        
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSIndexSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("indexes");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // - (void)replaceObjectInKeyAtIndex:(NSUInteger)index withObject:(id)object
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName
      = (Twine("replaceObjectIn") + UpperKey + "AtIndex").str();
    IdentifierInfo *SelectorIds[2] = {
      &Context.Idents.get(SelectorName),
      &Context.Idents.get("withObject")
    };
    
    if (KnownSelectors.insert(Selectors.getSelector(2, SelectorIds)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("NSUInteger");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("index");
      Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
      Builder.AddTypedTextChunk("withObject:");
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("id");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("object");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // - (void)replaceKeyAtIndexes:(NSIndexSet *)indexes withKey:(NSArray *)array
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName1 
      = (Twine("replace") + UpperKey + "AtIndexes").str();
    std::string SelectorName2 = (Twine("with") + UpperKey).str();
    IdentifierInfo *SelectorIds[2] = {
      &Context.Idents.get(SelectorName1),
      &Context.Idents.get(SelectorName2)
    };
    
    if (KnownSelectors.insert(Selectors.getSelector(2, SelectorIds)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName1 + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("NSIndexSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("indexes");
      Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName2 + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSArray *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("array");
      Results.AddResult(Result(Builder.TakeString(), IndexedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }  
  
  // Unordered getters
  // - (NSEnumerator *)enumeratorOfKey
  if (IsInstanceMethod && 
      (ReturnType.isNull() || 
       (ReturnType->isObjCObjectPointerType() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl()
          ->getName() == "NSEnumerator"))) {
    std::string SelectorName = (Twine("enumeratorOf") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getNullarySelector(SelectorId))
            .second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("NSEnumerator *");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
       
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName));
      Results.AddResult(Result(Builder.TakeString(), UnorderedGetterPriority, 
                              CXCursor_ObjCInstanceMethodDecl));
    }
  }

  // - (type *)memberOfKey:(type *)object
  if (IsInstanceMethod && 
      (ReturnType.isNull() || ReturnType->isObjCObjectPointerType())) {
    std::string SelectorName = (Twine("memberOf") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddPlaceholderChunk("object-type");
        Builder.AddTextChunk(" *");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      if (ReturnType.isNull()) {
        Builder.AddPlaceholderChunk("object-type");
        Builder.AddTextChunk(" *");
      } else {
        Builder.AddTextChunk(GetCompletionTypeString(ReturnType, Context, 
                                                     Policy,
                                                     Builder.getAllocator()));
      }
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("object");
      Results.AddResult(Result(Builder.TakeString(), UnorderedGetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }
  
  // Mutable unordered accessors
  // - (void)addKeyObject:(type *)object
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName
      = (Twine("add") + UpperKey + Twine("Object")).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("object-type");
      Builder.AddTextChunk(" *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("object");
      Results.AddResult(Result(Builder.TakeString(), UnorderedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }  

  // - (void)addKey:(NSSet *)objects
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("add") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("objects");
      Results.AddResult(Result(Builder.TakeString(), UnorderedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }  
  
  // - (void)removeKeyObject:(type *)object
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName
      = (Twine("remove") + UpperKey + Twine("Object")).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddPlaceholderChunk("object-type");
      Builder.AddTextChunk(" *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("object");
      Results.AddResult(Result(Builder.TakeString(), UnorderedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }  
  
  // - (void)removeKey:(NSSet *)objects
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("remove") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("objects");
      Results.AddResult(Result(Builder.TakeString(), UnorderedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }    

  // - (void)intersectKey:(NSSet *)objects
  if (IsInstanceMethod && ReturnTypeMatchesVoid) {
    std::string SelectorName = (Twine("intersect") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getUnarySelector(SelectorId)).second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("void");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
      
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName + ":"));
      Builder.AddChunk(CodeCompletionString::CK_LeftParen);
      Builder.AddTextChunk("NSSet *");
      Builder.AddChunk(CodeCompletionString::CK_RightParen);
      Builder.AddTextChunk("objects");
      Results.AddResult(Result(Builder.TakeString(), UnorderedSetterPriority, 
                               CXCursor_ObjCInstanceMethodDecl));
    }
  }  
  
  // Key-Value Observing
  // + (NSSet *)keyPathsForValuesAffectingKey
  if (!IsInstanceMethod && 
      (ReturnType.isNull() || 
       (ReturnType->isObjCObjectPointerType() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl() &&
        ReturnType->getAs<ObjCObjectPointerType>()->getInterfaceDecl()
                                                    ->getName() == "NSSet"))) {
    std::string SelectorName 
      = (Twine("keyPathsForValuesAffecting") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getNullarySelector(SelectorId))
            .second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("NSSet *");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
       
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName));
      Results.AddResult(Result(Builder.TakeString(), CCP_CodePattern, 
                              CXCursor_ObjCClassMethodDecl));
    }
  }

  // + (BOOL)automaticallyNotifiesObserversForKey
  if (!IsInstanceMethod &&
      (ReturnType.isNull() ||
       ReturnType->isIntegerType() || 
       ReturnType->isBooleanType())) {
    std::string SelectorName 
      = (Twine("automaticallyNotifiesObserversOf") + UpperKey).str();
    IdentifierInfo *SelectorId = &Context.Idents.get(SelectorName);
    if (KnownSelectors.insert(Selectors.getNullarySelector(SelectorId))
            .second) {
      if (ReturnType.isNull()) {
        Builder.AddChunk(CodeCompletionString::CK_LeftParen);
        Builder.AddTextChunk("BOOL");
        Builder.AddChunk(CodeCompletionString::CK_RightParen);
      }
       
      Builder.AddTypedTextChunk(Allocator.CopyString(SelectorName));
      Results.AddResult(Result(Builder.TakeString(), CCP_CodePattern, 
                              CXCursor_ObjCClassMethodDecl));
    }
  }
}