================
@@ -1029,5 +1172,151 @@ size_t renameRangeAdjustmentCost(ArrayRef<Range> 
Indexed, ArrayRef<Range> Lexed,
   return Cost;
 }
 
+static bool isMatchingSelectorName(const syntax::Token &Cur,
+                                   const syntax::Token &Next,
+                                   const SourceManager &SM,
+                                   llvm::StringRef SelectorName) {
+  if (SelectorName.empty())
+    return Cur.kind() == tok::colon;
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+         Cur.text(SM) == SelectorName &&
+         // We require the selector name and : to be contiguous.
+         // e.g. support `foo:` but not `foo :`.
+         Cur.endLocation() == Next.location();
+}
+
+static bool isSelectorLike(const syntax::Token &Cur,
+                           const syntax::Token &Next) {
+  return Cur.kind() == tok::identifier && Next.kind() == tok::colon &&
+         // We require the selector name and : to be contiguous.
+         // e.g. support `foo:` but not `foo :`.
+         Cur.endLocation() == Next.location();
+}
+
+static void
+lex(llvm::StringRef Code, const LangOptions &LangOpts,
+    llvm::function_ref<void(const syntax::Token &, const SourceManager &SM)>
+        Action) {
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Code.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto &SM = FileSM.get();
+  for (const auto &Tok : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
+    Action(Tok, SM);
+}
+
+std::vector<SymbolRange> collectRenameIdentifierRanges(
+    llvm::StringRef Identifier, llvm::StringRef Content,
+    const LangOptions &LangOpts, std::optional<Selector> Selector) {
+  std::vector<SymbolRange> Ranges;
+  if (!Selector) {
+    lex(Content, LangOpts,
+        [&](const syntax::Token &Tok, const SourceManager &SM) {
+          if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+            return;
+          Ranges.emplace_back(
+              halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+        });
+    return Ranges;
+  }
+  // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated!
+  std::string NullTerminatedCode = Content.str();
+  SourceManagerForFile FileSM("mock_file_name.cpp", NullTerminatedCode);
+  auto &SM = FileSM.get();
+
+  auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts);
+  unsigned Last = Tokens.size() - 1;
+
+  // One parser state for top level and each `[]` pair, can be nested.
+  // Technically we should have a state or recursion for each ()/{} as well,
+  // but since we're expecting well formed code it shouldn't matter in 
practice.
+  struct ParserState {
+    unsigned ParenCount = 0;
+    unsigned BraceCount = 0;
+    std::vector<Range> Pieces;
+  };
+
+  // We have to track square brackets, parens and braces as we want to skip the
+  // tokens inside them. This ensures that we don't use identical selector
+  // pieces in inner message sends, blocks, lambdas and @selector expressions.
+  std::vector<ParserState> States = {ParserState()};
+  unsigned NumPieces = Selector->getNumArgs();
+
+  for (unsigned Index = 0; Index < Last; ++Index) {
+    auto &State = States.back();
+    auto &Pieces = State.Pieces;
+    const auto &Tok = Tokens[Index];
+    const auto Kind = Tok.kind();
+    auto PieceCount = Pieces.size();
+
+    if (State.ParenCount == 0) {
+      // Check for matches until we find all selector pieces.
+      if (PieceCount < NumPieces &&
+          isMatchingSelectorName(Tok, Tokens[Index + 1], SM,
+                                 Selector->getNameForSlot(PieceCount))) {
+        if (!Selector->getNameForSlot(PieceCount).empty()) {
+          // Skip the ':' after the name. This ensures that it won't match a
+          // follow-up selector piece with an empty name.
+          ++Index;
+        }
+        Pieces.push_back(halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+        continue;
+      }
+      // If we've found all pieces, we still need to try to consume more pieces
+      // as it's possible the selector being renamed is a prefix of this method
+      // name.
----------------
DavidGoldman wrote:

Updated, parseMessageExpression can return early but the top level parse can't.

https://github.com/llvm/llvm-project/pull/76466
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to