Index: clang/lib/Rewrite/Rewriter.cpp =================================================================== --- clang/lib/Rewrite/Rewriter.cpp +++ clang/lib/Rewrite/Rewriter.cpp @@ -168,10 +168,11 @@ // start of the last token if this is a token range. if (Range.isTokenRange()) EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); - +// TODO: This means that CharSourceRange is half-open otherwise we're getting the wrong number here (think about a single character range). return EndOff-StartOff; } +// TODO: This means that "the input SourceRange" is half-open token range which is conflicting with how we use SourceRange in translateCXSourceRange. int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { return getRangeSize(CharSourceRange::getTokenRange(Range), opts); } @@ -299,6 +300,7 @@ unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); RewriteOptions rangeOpts; rangeOpts.IncludeInsertsAtBeginOfRange = false; + // TODO: This means getRangeSize(SourceRange&) really expects half-closed token range. StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true); return false; Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -158,7 +158,7 @@ Lexer::MeasureTokenLength(SM.getSpellingLoc(EndLoc), SM, LangOpts); EndLoc = EndLoc.getLocWithOffset(Length); } - + // TODO: This means that CharSourceRange is half-open even when not token range because the return type is half-open (per doxygen) and for non-token ranges we're not converting the end in any way. CXSourceRange Result = { {&SM, &LangOpts}, R.getBegin().getRawEncoding(), EndLoc.getRawEncoding()}; return Result; @@ -6827,6 +6827,7 @@ Buffer.data() + BeginLocInfo.second, Buffer.end()); Lex.SetCommentRetentionState(true); + // TODO: This means SourceRange is half-open as we stop before the end. // Lex tokens until we hit the end of the range. const char *EffectiveBufferEnd = Buffer.data() + EndLocInfo.second; Token Tok; Index: clang/tools/libclang/CXSourceLocation.h =================================================================== --- clang/tools/libclang/CXSourceLocation.h +++ clang/tools/libclang/CXSourceLocation.h @@ -66,6 +66,7 @@ return SourceLocation::getFromRawEncoding(L.int_data); } +// TODO: This means that SourceRange is just a half-open interval of chars because that's the semantics we specify in libclang API. static inline SourceRange translateCXSourceRange(CXSourceRange R) { return SourceRange(SourceLocation::getFromRawEncoding(R.begin_int_data), SourceLocation::getFromRawEncoding(R.end_int_data)); Index: clang/unittests/Rewrite/RewriterTest.cpp =================================================================== --- clang/unittests/Rewrite/RewriterTest.cpp +++ clang/unittests/Rewrite/RewriterTest.cpp @@ -75,6 +75,21 @@ EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(42, 47)), "bar;"); T.Rewrite.ReplaceText(T.SRange, "0"); EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(42, 47)), "0;"); + +TEST(Rewriter, RangeSize) { + StringRef Code = "void foo(){ }"; + + RangeTypeTest T(Code, 0, 13); + const auto Len = T.Rewrite.getRangeSize(T.makeCharRange(5, 8).getAsRange(), Rewriter::RewriteOptions{}); + EXPECT_EQ(Len, 3); +} + +TEST(Rewriter, RewriteRegression) { + StringRef Code = "void foo(){ }"; + + RangeTypeTest T(Code, 0, 13); + T.Rewrite.ReplaceText(T.makeCharRange(5, 8).getAsRange(), "bar"); + EXPECT_EQ(T.Rewrite.getRewrittenText(T.makeCharRange(0, 13)), "void bar(){ }"); } } // anonymous namespace Index: clang/unittests/libclang/LibclangTest.cpp =================================================================== --- clang/unittests/libclang/LibclangTest.cpp +++ clang/unittests/libclang/LibclangTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" @@ -736,3 +737,21 @@ CheckTokenKinds(); } + +TEST_F(LibclangParseTest, TranslateSourceRange) { + std::string Main = "main.c"; + WriteFile(Main, "int main(void);\n"); + + ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr, 0, TUFlags); + + CXFile File = clang_getFile(ClangTU, Main.c_str()); + CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 4); + CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 8); + + CXSourceRange Rng = clang_getRange(B, E); + + CXToken *Tokens; + unsigned int NumTokens; + clang_tokenize(ClangTU, Rng, &Tokens, &NumTokens); + ASSERT_EQ(NumTokens, 1); +}