Skip to content

Conversation

@tJener
Copy link
Contributor

@tJener tJener commented Nov 27, 2025

When the start and end token are both spelled in macro arguments, we still want to reject the range if they come from two separate macro arguments, as the original specified range is not precisely spelled in a single sequence of characters in source.

@tJener tJener requested review from legrosbuffle and ymand November 27, 2025 04:40
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Nov 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 27, 2025

@llvm/pr-subscribers-clang

Author: Eric Li (tJener)

Changes

When the start and end token are both spelled in macro arguments, we still want to reject the range if they come from two separate macro arguments, as the original specified range is not precisely spelled in a single sequence of characters in source.


Full diff: https://github.com/llvm/llvm-project/pull/169757.diff

2 Files Affected:

  • (modified) clang/lib/Tooling/Transformer/SourceCode.cpp (+25-5)
  • (modified) clang/unittests/Tooling/SourceCodeTest.cpp (+2)
diff --git a/clang/lib/Tooling/Transformer/SourceCode.cpp b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 922dafeddf416..fa9bf3427b8a0 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -86,8 +86,12 @@ llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
   return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
 }
 
-static bool spelledInMacroDefinition(SourceLocation Loc,
-                                     const SourceManager &SM) {
+// Returns the location of the top-level macro argument that is the spelling for
+// the expansion `Loc` is from. If `Loc` is spelled in the macro definition,
+// returns an invalid `SourceLocation`.
+static SourceLocation getMacroArgumentSpellingLoc(SourceLocation Loc,
+                                                  const SourceManager &SM) {
+  assert(Loc.isMacroID() && "Location must be in a macro");
   while (Loc.isMacroID()) {
     const auto &Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
     if (Expansion.isMacroArgExpansion()) {
@@ -95,9 +99,26 @@ static bool spelledInMacroDefinition(SourceLocation Loc,
       // in a macro expansion.
       Loc = Expansion.getSpellingLoc();
     } else {
-      return true;
+      return {};
     }
   }
+  return Loc;
+}
+
+static bool spelledInMacroDefinition(CharSourceRange Range,
+                                     const SourceManager &SM) {
+  if (Range.getBegin().isMacroID() && Range.getEnd().isMacroID()) {
+    // Check whether the range is entirely within a single macro argument.
+    auto B = getMacroArgumentSpellingLoc(Range.getBegin(), SM);
+    auto E = getMacroArgumentSpellingLoc(Range.getEnd(), SM);
+    return B.isInvalid() || B != E;
+  }
+
+  if (Range.getBegin().isMacroID())
+    return getMacroArgumentSpellingLoc(Range.getBegin(), SM).isInvalid();
+  if (Range.getEnd().isMacroID())
+    return getMacroArgumentSpellingLoc(Range.getEnd(), SM).isInvalid();
+
   return false;
 }
 
@@ -158,8 +179,7 @@ static CharSourceRange getRange(const CharSourceRange &EditRange,
     Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
   } else {
     auto AdjustedRange = getRangeForSplitTokens(EditRange, SM, LangOpts);
-    if (spelledInMacroDefinition(AdjustedRange.getBegin(), SM) ||
-        spelledInMacroDefinition(AdjustedRange.getEnd(), SM))
+    if (spelledInMacroDefinition(AdjustedRange, SM))
       return {};
 
     auto B = SM.getSpellingLoc(AdjustedRange.getBegin());
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp b/clang/unittests/Tooling/SourceCodeTest.cpp
index 549b77752f1c2..4932e0bd9431b 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -510,10 +510,12 @@ TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
 #define M1(x) x(1)
 #define M2(x, y) x ## y
 #define M3(x) foobar(x)
+#define M4(x, y) x y
 int foobar(int);
 int a = M1(foobar);
 int b = M2(foo, bar(2));
 int c = M3(3);
+int d = M4(foobar, (2));
 )cpp");
 
   CallsVisitor Visitor;

… macro arguments

When the start and end token are both spelled in macro arguments, we
still want to reject the range if they come from two separate macro
arguments, as the original specified range is not precisely spelled in
a single sequence of characters in source.
@tJener tJener enabled auto-merge (squash) November 27, 2025 04:54
@tJener tJener merged commit 4099121 into llvm:main Nov 27, 2025
9 of 10 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Nov 27, 2025

LLVM Buildbot has detected a new failure on builder openmp-s390x-linux running on systemz-1 while building clang at step 6 "test-openmp".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/88/builds/18515

Here is the relevant piece of the build log for the reference
Step 6 (test-openmp) failure: test (failure)
******************** TEST 'libomp :: tasking/issue-94260-2.c' FAILED ********************
Exit Code: -11

Command Output (stdout):
--
# RUN: at line 1
/home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp   -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic && /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/./bin/clang -fopenmp -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test -L /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -mbackchain -I /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/ompt /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic
# executed command: /home/uweigand/sandbox/buildbot/openmp-s390x-linux/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp
# note: command had no output on stdout or stderr
# error: command failed with exit status: -11

--

********************


GeneraluseAI pushed a commit to GeneraluseAI/llvm-project that referenced this pull request Nov 27, 2025
… macro arguments (llvm#169757)

When the start and end token are both spelled in macro arguments, we
still want to reject the range if they come from two separate macro
arguments, as the original specified range is not precisely spelled in a
single sequence of characters in source.
@tJener
Copy link
Contributor Author

tJener commented Nov 27, 2025

Had not realized that auto-merge would merge once tests finished running; I had assumed it'd be triggered by reviewer LGTM. That said, if you have any comments I can address them in a separate commit.

@tJener tJener deleted the macros branch November 27, 2025 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants