Skip to content

Commit feef80c

Browse files
authored
[clang][tooling] Fix getFileRange false negative (llvm#171555)
When an expression is in a single macro argument but also contains a macro, `getFileRange` would incorrectly reject that expression, concluding that it came from two different macro arguments because they came from two different expansions. We adjust the logic to look at the full path of macro argument expansion locations instead, tracking that if our traversal up the macro expansions continues all the way through macro arguments all the way to the top. This is similar to the technique used by `makeFileCharRange`. We also add some test cases to ensure we don't introduce any false positives.
1 parent c4ff1f3 commit feef80c

File tree

2 files changed

+51
-23
lines changed

2 files changed

+51
-23
lines changed

clang/lib/Tooling/Transformer/SourceCode.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,32 +86,35 @@ llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
8686
return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
8787
}
8888

89-
// Returns the location of the top-level macro argument that is the spelling for
90-
// the expansion `Loc` is from. If `Loc` is spelled in the macro definition,
91-
// returns an invalid `SourceLocation`.
92-
static SourceLocation getMacroArgumentSpellingLoc(SourceLocation Loc,
93-
const SourceManager &SM) {
89+
// Returns the full set of expansion locations of `Loc` from bottom to top-most
90+
// macro, if `Loc` is spelled in a macro argument. If `Loc` is spelled in the
91+
// macro definition, returns an empty vector.
92+
static llvm::SmallVector<SourceLocation, 2>
93+
getMacroArgumentExpansionLocs(SourceLocation Loc, const SourceManager &SM) {
9494
assert(Loc.isMacroID() && "Location must be in a macro");
95+
llvm::SmallVector<SourceLocation, 2> ArgLocs;
9596
while (Loc.isMacroID()) {
9697
const auto &Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
9798
if (Expansion.isMacroArgExpansion()) {
9899
// Check the spelling location of the macro arg, in case the arg itself is
99100
// in a macro expansion.
100101
Loc = Expansion.getSpellingLoc();
102+
ArgLocs.push_back(Expansion.getExpansionLocStart());
101103
} else {
102104
return {};
103105
}
104106
}
105-
return Loc;
107+
return ArgLocs;
106108
}
107109

108110
static bool spelledInMacroDefinition(CharSourceRange Range,
109111
const SourceManager &SM) {
110112
if (Range.getBegin().isMacroID() && Range.getEnd().isMacroID()) {
111-
// Check whether the range is entirely within a single macro argument.
112-
auto B = getMacroArgumentSpellingLoc(Range.getBegin(), SM);
113-
auto E = getMacroArgumentSpellingLoc(Range.getEnd(), SM);
114-
return B.isInvalid() || B != E;
113+
// Check whether the range is entirely within a single macro argument by
114+
// checking if they are in the same macro argument at every level.
115+
auto B = getMacroArgumentExpansionLocs(Range.getBegin(), SM);
116+
auto E = getMacroArgumentExpansionLocs(Range.getEnd(), SM);
117+
return B.empty() || B != E;
115118
}
116119

117120
return Range.getBegin().isMacroID() || Range.getEnd().isMacroID();

clang/unittests/Tooling/SourceCodeTest.cpp

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -507,17 +507,21 @@ TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
507507
// If we specify to ignore macro expansions, none of these call expressions
508508
// should have an editable range.
509509
llvm::Annotations Code(R"cpp(
510+
#define NOOP(x) x
510511
#define M1(x) x(1)
511-
#define M2(x, y) x ## y
512+
#define M2(x, y) NOOP(M2_IMPL(x, y))
513+
#define M2_IMPL(x, y) x ## y
512514
#define M3(x) foobar(x)
513-
#define M4(x, y) x y
515+
#define M4(x, y) NOOP(x y)
514516
#define M5(x) x
517+
#define M6(...) M4(__VA_ARGS__)
515518
int foobar(int);
516519
int a = M1(foobar);
517520
int b = M2(foo, bar(2));
518521
int c = M3(3);
519522
int d = M4(foobar, (4));
520523
int e = M5(foobar) (5);
524+
int f = M6(foobar, (6));
521525
)cpp");
522526

523527
CallsVisitor Visitor;
@@ -592,18 +596,39 @@ int c = BAR 3.0;
592596
}
593597

594598
TEST_P(GetFileRangeForEditTest, EditWholeMacroArgShouldSucceed) {
595-
llvm::Annotations Code(R"cpp(
596-
#define FOO(a) a + 7.0;
597-
int a = FOO($r[[10]]);
598-
)cpp");
599+
{
600+
llvm::Annotations Code(R"cpp(
601+
#define FOO(a) a + 7.0;
602+
int a = FOO($r[[10]]);
603+
)cpp");
604+
605+
IntLitVisitor Visitor;
606+
Visitor.OnIntLit = [&Code](IntegerLiteral *Expr, ASTContext *Context) {
607+
auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
608+
EXPECT_THAT(
609+
getFileRangeForEdit(Range, *Context, GetParam()),
610+
ValueIs(AsRange(Context->getSourceManager(), Code.range("r"))));
611+
};
612+
Visitor.runOver(Code.code());
613+
}
599614

600-
IntLitVisitor Visitor;
601-
Visitor.OnIntLit = [&Code](IntegerLiteral *Expr, ASTContext *Context) {
602-
auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
603-
EXPECT_THAT(getFileRangeForEdit(Range, *Context, GetParam()),
604-
ValueIs(AsRange(Context->getSourceManager(), Code.range("r"))));
605-
};
606-
Visitor.runOver(Code.code());
615+
{
616+
llvm::Annotations Code(R"cpp(
617+
#define FOO(a) a + 7.0;
618+
#define DO_NOTHING(x) x
619+
int Frob(int x);
620+
int a = FOO($r[[Frob(DO_NOTHING(40))]]);
621+
)cpp");
622+
623+
CallsVisitor Visitor;
624+
Visitor.OnCall = [&Code](CallExpr *Expr, ASTContext *Context) {
625+
auto Range = CharSourceRange::getTokenRange(Expr->getSourceRange());
626+
EXPECT_THAT(
627+
getFileRangeForEdit(Range, *Context, GetParam()),
628+
ValueIs(AsRange(Context->getSourceManager(), Code.range("r"))));
629+
};
630+
Visitor.runOver(Code.code());
631+
}
607632
}
608633

609634
TEST_P(GetFileRangeForEditTest, EditPartialMacroArgShouldSucceed) {

0 commit comments

Comments
 (0)