================ @@ -538,11 +564,222 @@ std::optional<InvalidName> checkName(const NamedDecl &RenameDecl, Conflict->getLocation().printToString(ASTCtx.getSourceManager())}; } } - if (Result) + if (Result) { InvalidNameMetric.record(1, toString(Result->K)); + return makeError(*Result); + } + return llvm::Error::success(); +} + +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(); +} + +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 isSelectorLike(Cur, Next) && Cur.text(SM) == SelectorName; +} + +// Scan through Tokens to find ranges for each selector fragment in Sel at the +// top level (not nested in any () or {} or []). The search will terminate upon +// seeing Terminator or a ; at the top level. +std::optional<SymbolRange> +findAllSelectorPieces(llvm::ArrayRef<syntax::Token> Tokens, + const SourceManager &SM, Selector Sel, + tok::TokenKind Terminator) { + + unsigned NumArgs = Sel.getNumArgs(); + llvm::SmallVector<tok::TokenKind, 8> Closes; + std::vector<Range> SelectorPieces; + unsigned Last = Tokens.size() - 1; + for (unsigned Index = 0; Index < Last; ++Index) { + const auto &Tok = Tokens[Index]; + + if (Closes.empty()) { + auto PieceCount = SelectorPieces.size(); + if (PieceCount < NumArgs && + isMatchingSelectorName(Tok, Tokens[Index + 1], SM, + Sel.getNameForSlot(PieceCount))) { + // If 'foo:' instead of ':' (empty selector), we need to skip the ':' + // token after the name. + if (!Sel.getNameForSlot(PieceCount).empty()) + ++Index; + SelectorPieces.push_back( + halfOpenToRange(SM, Tok.range(SM).toCharRange(SM))); + continue; + } + // If we've found all pieces but the current token looks like another + // selector piece, it means the method being renamed is a strict prefix of + // the selector we've found - should be skipped. + if (SelectorPieces.size() >= NumArgs && + isSelectorLike(Tok, Tokens[Index + 1])) + return std::nullopt; + } + + if (Closes.empty() && Tok.kind() == Terminator) + return SelectorPieces.size() == NumArgs + ? std::optional(SymbolRange(SelectorPieces)) + : std::nullopt; + + switch (Tok.kind()) { + case tok::l_square: + Closes.push_back(tok::r_square); + break; + case tok::l_paren: + Closes.push_back(tok::r_paren); + break; + case tok::l_brace: + Closes.push_back(tok::r_brace); + break; + case tok::r_square: + case tok::r_paren: + case tok::r_brace: + if (Closes.empty() || Closes.back() != Tok.kind()) + return std::nullopt; + Closes.pop_back(); + break; + case tok::semi: + // top level ; terminates all statements. + if (Closes.empty()) + return SelectorPieces.size() == NumArgs + ? std::optional(SymbolRange(SelectorPieces)) + : std::nullopt; + break; + default: + break; + } + } + return std::nullopt; +} + +/// Collects all ranges of the given identifier/selector in the source code. +/// +/// If a selector is given, this does a full lex of the given source code in +/// order to identify all selector fragments (e.g. in method exprs/decls) since +/// they are non-contiguous. +std::vector<SymbolRange> collectRenameIdentifierRanges( + llvm::StringRef Identifier, llvm::StringRef Content, + const LangOptions &LangOpts, std::optional<Selector> Selector) { + std::vector<SymbolRange> Ranges; + if (!Selector) { + auto IdentifierRanges = + collectIdentifierRanges(Identifier, Content, LangOpts); + for (const auto &R : IdentifierRanges) + Ranges.emplace_back(R); + return Ranges; + } + // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated! ---------------- ahoppen wrote:
In my rename PR I found that this is no longer an issue. See https://github.com/apple/llvm-project/pull/7915#discussion_r1442402332 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