================
@@ -26,6 +26,129 @@ AST_MATCHER(clang::LinkageSpecDecl, isExternCLinkage) {
 
 namespace clang::tidy::modernize {
 
+namespace lexer = clang::tidy::utils::lexer;
+
+namespace {
+struct RangeTextInfo {
+  std::string Text;
+  lexer::TokenRangeInfo Tokens;
+};
+} // namespace
+
+static bool hasNonWhitespace(llvm::StringRef Text) {
+  return Text.find_first_not_of(" \t\n\r\f\v") != llvm::StringRef::npos;
+}
+
+static RangeTextInfo getRangeTextInfo(clang::SourceLocation Begin,
+                                      clang::SourceLocation End,
+                                      const clang::SourceManager &SM,
+                                      const clang::LangOptions &LangOpts) {
+  RangeTextInfo Info;
+  if (!Begin.isValid() || !End.isValid() || Begin.isMacroID() ||
+      End.isMacroID())
+    return Info;
+
+  const clang::CharSourceRange Range =
+      clang::CharSourceRange::getCharRange(Begin, End);
+  Info.Text = lexer::getSourceText(Range, SM, LangOpts);
+  Info.Tokens = lexer::analyzeTokenRange(Range, SM, LangOpts);
+  return Info;
+}
+
+static std::string getFunctionPointerTypeText(clang::SourceRange TypeRange,
+                                              clang::SourceLocation NameLoc,
+                                              const clang::SourceManager &SM,
+                                              const clang::LangOptions &LO) {
+  clang::SourceLocation StartLoc = NameLoc;
+  clang::SourceLocation EndLoc = NameLoc;
+
+  while (true) {
+    const std::optional<clang::Token> Prev =
+        lexer::getPreviousToken(StartLoc, SM, LO);
+    const std::optional<clang::Token> Next =
+        lexer::findNextTokenSkippingComments(EndLoc, SM, LO);
+    if (!Prev || Prev->isNot(clang::tok::l_paren) || !Next ||
+        Next->isNot(clang::tok::r_paren))
+      break;
+
+    StartLoc = Prev->getLocation();
+    EndLoc = Next->getLocation();
+  }
+
+  const clang::CharSourceRange RangeLeftOfIdentifier =
+      clang::CharSourceRange::getCharRange(TypeRange.getBegin(), StartLoc);
+  const clang::CharSourceRange RangeRightOfIdentifier =
+      clang::CharSourceRange::getCharRange(
+          clang::Lexer::getLocForEndOfToken(EndLoc, 0, SM, LO),
+          clang::Lexer::getLocForEndOfToken(TypeRange.getEnd(), 0, SM, LO));
+  return lexer::getSourceText(RangeLeftOfIdentifier, SM, LO) +
+         lexer::getSourceText(RangeRightOfIdentifier, SM, LO);
+}
+
+static RangeTextInfo getLeadingTextInfo(bool IsFirstTypedefInGroup,
+                                        clang::SourceRange ReplaceRange,
+                                        clang::SourceRange TypeRange,
+                                        const clang::SourceManager &SM,
+                                        const clang::LangOptions &LO) {
+  RangeTextInfo Info;
+  if (!IsFirstTypedefInGroup)
+    return Info;
+
+  const clang::SourceLocation TypedefEnd =
+      clang::Lexer::getLocForEndOfToken(ReplaceRange.getBegin(), 0, SM, LO);
+  Info = getRangeTextInfo(TypedefEnd, TypeRange.getBegin(), SM, LO);
+  // Keep leading trivia only when it actually contains comments. This avoids
+  // shifting plain whitespace from between 'typedef' and the type into the
+  // replacement, preserving formatting for un-commented declarations.
+  if (!Info.Tokens.HasComment)
+    Info.Text.clear();
+  return Info;
+}
+
+static RangeTextInfo getSuffixTextInfo(bool FunctionPointerCase,
+                                       bool IsFirstTypedefInGroup,
+                                       clang::SourceLocation 
PrevReplacementEnd,
+                                       clang::SourceRange TypeRange,
+                                       clang::SourceLocation NameLoc,
+                                       const clang::SourceManager &SM,
+                                       const clang::LangOptions &LO) {
+  RangeTextInfo Info;
+  if (FunctionPointerCase)
+    return Info;
+
+  // Capture the raw text between type and name to preserve trailing comments,
+  // including multi-line // blocks, without re-lexing individual comment
+  // tokens.
+  if (IsFirstTypedefInGroup) {
+    const clang::SourceLocation AfterType =
+        clang::Lexer::getLocForEndOfToken(TypeRange.getEnd(), 0, SM, LO);
+    return getRangeTextInfo(AfterType, NameLoc, SM, LO);
+  }
+
+  if (!PrevReplacementEnd.isValid() || PrevReplacementEnd.isMacroID())
+    return Info;
+
+  clang::SourceLocation AfterComma = PrevReplacementEnd;
+  if (const std::optional<clang::Token> NextTok =
+          lexer::findNextTokenSkippingComments(AfterComma, SM, LO)) {
+    if (NextTok->is(clang::tok::comma)) {
+      AfterComma =
+          clang::Lexer::getLocForEndOfToken(NextTok->getLocation(), 0, SM, LO);
+    }
+  }
+  return getRangeTextInfo(AfterComma, NameLoc, SM, LO);
+}
+
+static void stripLeadingComma(RangeTextInfo &Info) {
+  if (Info.Text.empty())
+    return;
+  // Overlapping ranges in multi-declarator typedefs can leave a leading comma
+  // in the captured suffix. Drop it so the replacement doesn't reintroduce it.
+  const size_t NonWs = Info.Text.find_first_not_of(" \t\n\r\f\v");
----------------
vbvictor wrote:

`hasNonWhitespace`?

https://github.com/llvm/llvm-project/pull/180372
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to