diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -237,6 +237,22 @@ return halfOpenToRange(SM, CharSourceRange::getCharRange(TokLoc, End)); } +static bool isIdentifierOrOverloadedOperator(const Token &Tok) { + switch (Tok.getKind()) { + case tok::raw_identifier: + return true; +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \ + case tok::Token: +#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly) +#include "clang/Basic/OperatorKinds.def" + return true; + + default: + break; + } + return false; +} + SourceLocation getBeginningOfIdentifier(const Position &Pos, const SourceManager &SM, const LangOptions &LangOpts) { @@ -248,24 +264,39 @@ } // GetBeginningOfToken(pos) is almost what we want, but does the wrong thing - // if the cursor is at the end of the identifier. - // Instead, we lex at GetBeginningOfToken(pos - 1). The cases are: - // 1) at the beginning of an identifier, we'll be looking at something - // that isn't an identifier. - // 2) at the middle or end of an identifier, we get the identifier. - // 3) anywhere outside an identifier, we'll get some non-identifier thing. + // if the cursor is at the end of the target token (identifier, overloaded + // operators). Instead, we lex at GetBeginningOfToken(pos - 1). The cases are: + // 1) at the beginning of a target token, we'll be looking at something + // that isn't a target token. + // 2) at the middle or end of a target token, we get the target token. + // 3) anywhere outside the target token, we'll get some non-target-token + // thing. // We can't actually distinguish cases 1 and 3, but returning the original // location is correct for both! SourceLocation InputLoc = SM.getComposedLoc(FID, *Offset); if (*Offset == 0) // Case 1 or 3. return InputLoc; - SourceLocation Before = SM.getComposedLoc(FID, *Offset - 1); - Before = Lexer::GetBeginningOfToken(Before, SM, LangOpts); Token Tok; + // As we use closed range [start, end] for identifiers, we encounter tricky + // cases when identifiers meet with overloaded operators: + // 1) ++^foo; => return the start location of foo. + // 2) ^foo^++; => return the start location of foo. + // ~~~~~ range for foo. + // so if we see the original location is the beginning of the identifier, we + // just return the the original location, otherwise we will return the + // beginning loc of "++" in 1). + if (InputLoc.isValid() && + InputLoc == Lexer::GetBeginningOfToken(InputLoc, SM, LangOpts) && + !Lexer::getRawToken(InputLoc, Tok, SM, LangOpts, false) && + Tok.is(tok::raw_identifier)) + return InputLoc; // original location is at the beginning of an identifier. + + SourceLocation Before = SM.getComposedLoc(FID, *Offset - 1); + Before = Lexer::GetBeginningOfToken(Before, SM, LangOpts); if (Before.isValid() && !Lexer::getRawToken(Before, Tok, SM, LangOpts, false) && - Tok.is(tok::raw_identifier)) + isIdentifierOrOverloadedOperator(Tok)) return Before; // Case 2. return InputLoc; // Case 1 or 3. } diff --git a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp --- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp +++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp @@ -326,6 +326,9 @@ "int foo(^);", // non-identifier "^int foo();", // beginning of file (can't back up) "int ^f0^0();", // after a digit (lexing at N-1 is wrong) + "void f(int abc) { ^a^b^c^++; }", // range of identifier + "void f(int abc) { ++^a^b^c^; }", // range of identifier + "void f(int abc) { ^+^+abc; }", // range of operator "int ^λλ^λ();", // UTF-8 handled properly when backing up // identifier in macro arg diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -434,6 +434,15 @@ auto x = m^akeX(); } )cpp", + + R"cpp( + struct X { + X& [[operator]]++() {} + }; + void foo(X& x) { + +^+x; + } + )cpp", }; for (const char *Test : Tests) { Annotations T(Test);