15#include "clang/Basic/FileEntry.h"
16#include "clang/Basic/LangOptions.h"
17#include "clang/Basic/SourceLocation.h"
18#include "clang/Basic/SourceManager.h"
19#include "clang/Basic/TokenKinds.h"
20#include "clang/Driver/Types.h"
21#include "clang/Format/Format.h"
22#include "clang/Lex/Lexer.h"
23#include "clang/Lex/Preprocessor.h"
24#include "clang/Lex/Token.h"
25#include "clang/Tooling/Core/Replacement.h"
26#include "clang/Tooling/Syntax/Tokens.h"
27#include "llvm/ADT/ArrayRef.h"
28#include "llvm/ADT/BitVector.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/StringExtras.h"
31#include "llvm/ADT/StringMap.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/Support/Compiler.h"
34#include "llvm/Support/Errc.h"
35#include "llvm/Support/Error.h"
36#include "llvm/Support/ErrorHandling.h"
37#include "llvm/Support/LineIterator.h"
38#include "llvm/Support/MemoryBuffer.h"
39#include "llvm/Support/Path.h"
40#include "llvm/Support/VirtualFileSystem.h"
41#include "llvm/Support/xxhash.h"
62template <
typename Callback>
64 bool LoggedInvalid =
false;
67 for (
size_t I = 0; I < U8.size();) {
68 unsigned char C =
static_cast<unsigned char>(U8[I]);
69 if (LLVM_LIKELY(!(
C & 0x80))) {
76 size_t UTF8Length = llvm::countl_one(
C);
79 if (LLVM_UNLIKELY(UTF8Length < 2 || UTF8Length > 4)) {
81 elog(
"File has invalid UTF-8 near offset {0}: {1}", I, llvm::toHex(U8));
95 if (CB(UTF8Length, UTF8Length == 4 ? 2 : 1))
132 llvm_unreachable(
"unsupported encoding");
135 if (Result > U8.size()) {
168 llvm_unreachable(
"unsupported encoding");
174 bool AllowColumnsBeyondLineLength) {
176 return error(llvm::errc::invalid_argument,
177 "Line value can't be negative ({0})", P.
line);
179 return error(llvm::errc::invalid_argument,
180 "Character value can't be negative ({0})", P.
character);
181 size_t StartOfLine = 0;
182 for (
int I = 0; I != P.
line; ++I) {
183 size_t NextNL = Code.find(
'\n', StartOfLine);
184 if (NextNL == llvm::StringRef::npos)
185 return error(llvm::errc::invalid_argument,
186 "Line value is out of range ({0})", P.
line);
187 StartOfLine = NextNL + 1;
190 Code.substr(StartOfLine).take_until([](
char C) {
return C ==
'\n'; });
195 if (!Valid && !AllowColumnsBeyondLineLength)
196 return error(llvm::errc::invalid_argument,
197 "{0} offset {1} is invalid for line {2}",
lspEncoding(),
199 return StartOfLine + ByteInLine;
204 llvm::StringRef Before = Code.substr(0,
Offset);
205 int Lines = Before.count(
'\n');
206 size_t PrevNL = Before.rfind(
'\n');
207 size_t StartOfLine = (PrevNL == llvm::StringRef::npos) ? 0 : (PrevNL + 1);
218 std::tie(FID,
Offset) = SM.getDecomposedSpellingLoc(
Loc);
220 P.
line =
static_cast<int>(SM.getLineNumber(FID,
Offset)) - 1;
221 bool Invalid =
false;
222 llvm::StringRef Code = SM.getBufferData(FID, &Invalid);
224 auto ColumnInBytes = SM.getColumnNumber(FID,
Offset) - 1;
225 auto LineSoFar = Code.substr(
Offset - ColumnInBytes, ColumnInBytes);
234 auto Spelling = SM.getDecomposedSpellingLoc(
Loc);
235 bool InvalidSLocEntry =
false;
236 const auto SLocEntry = SM.getSLocEntry(Spelling.first, &InvalidSLocEntry);
237 if (InvalidSLocEntry)
239 StringRef SpellingFile = SLocEntry.getFile().getName();
240 if (SpellingFile ==
"<scratch space>")
242 if (SpellingFile ==
"<built-in>")
244 return !SM.isWrittenInCommandLineFile(
245 SM.getComposedLoc(Spelling.first, Spelling.second));
250 if (!R.getBegin().isValid() || !R.getEnd().isValid())
254 size_t BeginOffset = 0;
255 std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
258 size_t EndOffset = 0;
259 std::tie(EndFID, EndOffset) = Mgr.getDecomposedLoc(R.getEnd());
261 return BeginFID.isValid() && BeginFID == EndFID && BeginOffset <= EndOffset;
265 assert(SM.getLocForEndOfFile(IncludedFile).isFileID());
266 FileID IncludingFile;
268 std::tie(IncludingFile,
Offset) =
269 SM.getDecomposedExpansionLoc(SM.getIncludeLoc(IncludedFile));
270 bool Invalid =
false;
271 llvm::StringRef Buf = SM.getBufferData(IncludingFile, &Invalid);
273 return SourceLocation();
277 assert(
Offset < Buf.size());
280 return SM.getComposedLoc(IncludingFile,
Offset);
282 return SourceLocation();
287 const LangOptions &LangOpts) {
289 if (Lexer::getRawToken(
Loc, TheTok, SM, LangOpts))
296 if (TheTok.is(tok::greatergreater))
298 return TheTok.getLength();
303 const SourceManager &SM,
304 const LangOptions &LangOpts) {
306 return BeginLoc.getLocWithOffset(Len ? Len - 1 : 0);
311 const SourceManager &SM,
312 const LangOptions &LangOpts) {
313 return EndLoc.getLocWithOffset(
319 const LangOptions &LangOpts) {
320 if (!
Range.isTokenRange())
322 return Range.getAsRange();
328 const SourceManager &SM,
329 const LangOptions &LangOpts) {
330 SourceLocation Begin =
331 SM.isBeforeInTranslationUnit(R1.getBegin(), R2.getBegin())
339 return SourceRange(Begin, End);
346 const LangOptions &LangOpts) {
348 if (SM.isWrittenInSameFile(R.getBegin(), R.getEnd()))
351 llvm::DenseMap<FileID, SourceLocation> BeginExpansions;
352 for (SourceLocation Begin = R.getBegin(); Begin.isValid();
353 Begin = Begin.isFileID()
355 : SM.getImmediateExpansionRange(Begin).getBegin()) {
356 BeginExpansions[SM.getFileID(Begin)] = Begin;
360 for (SourceLocation End = R.getEnd(); End.isValid();
365 auto It = BeginExpansions.find(SM.getFileID(End));
366 if (It != BeginExpansions.end()) {
367 if (SM.getFileOffset(It->second) > SM.getFileOffset(End))
368 return SourceLocation();
369 return {It->second, End};
372 return SourceRange();
379 const LangOptions &LangOpts) {
396 const SourceManager &SM,
397 const LangOptions &LangOpts) {
398 SourceRange FileRange =
Loc;
399 while (!FileRange.getBegin().isFileID()) {
400 if (SM.isMacroArgExpansion(FileRange.getBegin())) {
402 SM.getImmediateSpellingLoc(FileRange.getBegin()),
403 SM.getImmediateSpellingLoc(FileRange.getEnd()), SM, LangOpts);
404 assert(SM.isWrittenInSameFile(FileRange.getBegin(), FileRange.getEnd()));
406 SourceRange ExpansionRangeForBegin =
408 SourceRange ExpansionRangeForEnd =
410 if (ExpansionRangeForBegin.isInvalid() ||
411 ExpansionRangeForEnd.isInvalid())
412 return SourceRange();
413 assert(SM.isWrittenInSameFile(ExpansionRangeForBegin.getBegin(),
414 ExpansionRangeForEnd.getBegin()) &&
415 "Both Expansion ranges should be in same file.");
416 FileRange =
unionTokenRange(ExpansionRangeForBegin, ExpansionRangeForEnd,
426 FileID FID = SM.getFileID(SM.getExpansionLoc(
Loc));
427 return FID == SM.getMainFileID() || FID == SM.getPreambleFileID();
431 const LangOptions &LangOpts,
445 Result.setEnd(Result.getEnd().getLocWithOffset(TokLen));
454 auto Buf = SM.getBufferOrNone(SM.getFileID(R.getBegin()));
457 size_t BeginOffset = SM.getFileOffset(R.getBegin());
458 size_t EndOffset = SM.getFileOffset(R.getEnd());
459 return Buf->getBuffer().substr(BeginOffset, EndOffset - BeginOffset);
464 llvm::StringRef Code = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
468 return Offset.takeError();
469 return SM.getLocForStartOfFile(SM.getMainFileID()).getLocWithOffset(*
Offset);
481 if (B.start < A.start)
490 llvm::StringRef Before = Code.substr(0,
Offset);
491 int Lines = Before.count(
'\n');
492 size_t PrevNL = Before.rfind(
'\n');
493 size_t StartOfLine = (PrevNL == llvm::StringRef::npos) ? 0 : (PrevNL + 1);
498 size_t Pos = QName.rfind(
"::");
499 if (
Pos == llvm::StringRef::npos)
500 return {llvm::StringRef(), QName};
501 return {QName.substr(0,
Pos + 2), QName.substr(
Pos + 2)};
505 const tooling::Replacement &R) {
506 Range ReplacementRange = {
509 return {ReplacementRange, std::string(R.getReplacementText())};
513 const tooling::Replacements &Repls) {
514 std::vector<TextEdit> Edits;
515 for (
const auto &R : Repls)
521 FileManager &FileMgr) {
522 llvm::SmallString<128> FilePath = F.getName();
523 if (!llvm::sys::path::is_absolute(FilePath)) {
525 FileMgr.getVirtualFileSystem().makeAbsolute(
527 elog(
"Could not turn relative path '{0}' to absolute: {1}", FilePath,
544 if (
auto Dir = FileMgr.getOptionalDirectoryRef(
545 llvm::sys::path::parent_path(FilePath))) {
546 llvm::SmallString<128> RealPath;
547 llvm::StringRef DirName = FileMgr.getCanonicalName(*
Dir);
548 llvm::sys::path::append(RealPath, DirName,
549 llvm::sys::path::filename(FilePath));
550 return RealPath.str().str();
553 return FilePath.str().str();
557 const LangOptions &L) {
561 Result.newText =
FixIt.CodeToInsert;
566 uint64_t Hash{llvm::xxh3_64bits(Content)};
568 for (
unsigned I = 0; I < Result.size(); ++I) {
569 Result[I] = uint8_t(Hash);
575std::optional<FileDigest>
digestFile(
const SourceManager &SM, FileID FID) {
576 bool Invalid =
false;
577 llvm::StringRef Content = SM.getBufferData(FID, &Invalid);
584 llvm::StringRef Content,
600 auto Style = format::getStyle(format::DefaultFormatStyle,
File,
601 format::DefaultFallbackStyle, Content,
602 TFS.
view(std::nullopt).get());
604 log(
"getStyle() failed for file {0}: {1}. Fallback is LLVM style.",
File,
606 return format::getLLVMStyle();
611llvm::Expected<tooling::Replacements>
613 const format::FormatStyle &Style) {
614 auto CleanReplaces = cleanupAroundReplacements(Code,
Replaces, Style);
616 return CleanReplaces;
617 return formatReplacements(Code, std::move(*CleanReplaces), Style);
621lex(llvm::StringRef Code,
const LangOptions &LangOpts,
622 llvm::function_ref<
void(
const syntax::Token &,
const SourceManager &SM)>
625 std::string NullTerminatedCode = Code.str();
626 SourceManagerForFile FileSM(
"mock_file_name.cpp", NullTerminatedCode);
627 auto &SM = FileSM.get();
628 for (
const auto &Tok : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
633 const format::FormatStyle &Style) {
634 llvm::StringMap<unsigned> Identifiers;
635 auto LangOpt = format::getFormattingLangOpts(Style);
636 lex(Content, LangOpt, [&](
const syntax::Token &Tok,
const SourceManager &SM) {
637 if (Tok.kind() == tok::identifier)
638 ++Identifiers[Tok.
text(SM)];
640 else if (
const auto *
Keyword = tok::getKeywordSpelling(Tok.kind()))
647 llvm::StringRef Content,
648 const LangOptions &LangOpts) {
649 std::vector<Range> Ranges;
650 lex(Content, LangOpts,
651 [&](
const syntax::Token &Tok,
const SourceManager &SM) {
659bool isKeyword(llvm::StringRef NewName,
const LangOptions &LangOpts) {
661 clang::IdentifierTable KeywordsTable(LangOpts);
662 return KeywordsTable.find(NewName) != KeywordsTable.end();
666struct NamespaceEvent {
677void parseNamespaceEvents(llvm::StringRef Code,
const LangOptions &LangOpts,
678 llvm::function_ref<
void(NamespaceEvent)>
Callback) {
681 std::vector<std::string> Enclosing;
683 llvm::BitVector BraceStack;
695 NamespaceEvent
Event;
696 lex(Code, LangOpts, [&](
const syntax::Token &Tok,
const SourceManager &SM) {
698 switch (Tok.kind()) {
700 State = State == Default ? Using : Default;
702 case tok::kw_namespace:
705 State = UsingNamespace;
715 case tok::identifier:
720 case UsingNamespaceName:
721 NSName.append(Tok.
text(SM).str());
722 State = UsingNamespaceName;
728 NSName.append(Tok.
text(SM).str());
737 case tok::coloncolon:
744 case UsingNamespaceName:
746 State = UsingNamespaceName;
761 if (State == NamespaceName) {
763 BraceStack.push_back(
true);
764 Enclosing.push_back(NSName);
765 Event.Trigger = NamespaceEvent::BeginNamespace;
766 Event.Payload = llvm::join(Enclosing,
"::");
771 BraceStack.push_back(
false);
778 if (!BraceStack.empty()) {
779 if (BraceStack.back()) {
781 Enclosing.pop_back();
782 Event.Trigger = NamespaceEvent::EndNamespace;
783 Event.Payload = llvm::join(Enclosing,
"::");
786 BraceStack.pop_back();
790 if (State == UsingNamespaceName) {
792 Event.Trigger = NamespaceEvent::UsingDirective;
793 Event.Payload = std::move(NSName);
806llvm::SmallVector<llvm::StringRef> ancestorNamespaces(llvm::StringRef NS) {
807 llvm::SmallVector<llvm::StringRef>
Results;
808 Results.push_back(NS.take_front(0));
809 NS.split(
Results,
"::", -1,
false);
810 for (llvm::StringRef &R :
Results)
811 R = NS.take_front(R.end() - NS.begin());
816bool isMainFile(llvm::StringRef
FileName,
const SourceManager &SM) {
817 auto FE = SM.getFileManager().getOptionalFileRef(
FileName);
818 return FE && FE == SM.getFileEntryRefForID(SM.getMainFileID());
824 const LangOptions &LangOpts) {
827 llvm::StringMap<llvm::StringSet<>> UsingDirectives;
829 parseNamespaceEvents(Code, LangOpts, [&](NamespaceEvent
Event) {
830 llvm::StringRef NS =
Event.Payload;
831 switch (
Event.Trigger) {
832 case NamespaceEvent::BeginNamespace:
833 case NamespaceEvent::EndNamespace:
834 Current = std::move(Event.Payload);
836 case NamespaceEvent::UsingDirective:
837 if (NS.consume_front(
"::"))
838 UsingDirectives[Current].insert(NS);
840 for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
841 if (Enclosing.empty())
842 UsingDirectives[Current].insert(NS);
844 UsingDirectives[Current].insert((Enclosing +
"::" + NS).str());
851 std::vector<std::string> Found;
852 for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
853 Found.push_back(std::string(Enclosing));
854 auto It = UsingDirectives.find(Enclosing);
855 if (It != UsingDirectives.end())
856 for (
const auto &Used : It->second)
857 Found.push_back(std::string(Used.getKey()));
860 llvm::sort(Found, [&](
const std::string &LHS,
const std::string &RHS) {
867 Found.erase(std::unique(Found.begin(), Found.end()), Found.end());
875 static constexpr int MinWordLength = 4;
877 std::vector<CharRole> Roles(Content.size());
880 llvm::StringSet<> Result;
881 llvm::SmallString<256>
Word;
883 if (
Word.size() >= MinWordLength) {
885 C = llvm::toLower(
C);
890 for (
unsigned I = 0; I < Content.size(); ++I) {
896 Word.push_back(Content[I]);
910 llvm::StringRef After) {
912 if (Before.ends_with(
"`") && After.starts_with(
"`"))
915 if (Before.ends_with(
"::") || After.starts_with(
"::"))
920 Before = Before.take_back(100);
921 auto Pos = Before.find_last_of(
"\\@");
922 if (
Pos != llvm::StringRef::npos) {
923 llvm::StringRef
Tag = Before.substr(
Pos + 1).rtrim(
' ');
924 if (
Tag ==
"p" ||
Tag ==
"c" ||
Tag ==
"class" ||
Tag ==
"tparam" ||
925 Tag ==
"param" ||
Tag ==
"param[in]" ||
Tag ==
"param[out]" ||
926 Tag ==
"param[in,out]" ||
Tag ==
"retval" ||
Tag ==
"throw" ||
927 Tag ==
"throws" ||
Tag ==
"link")
933 if (
Word.contains(
'_')) {
940 bool HasLower =
Word.find_if(clang::isLowercase) != StringRef::npos;
941 bool HasUpper =
Word.substr(1).find_if(clang::isUppercase) != StringRef::npos;
942 if (HasLower && HasUpper) {
949std::optional<SpelledWord> SpelledWord::touching(SourceLocation SpelledLoc,
950 const syntax::TokenBuffer &TB,
951 const LangOptions &LangOpts) {
952 const auto &SM = TB.sourceManager();
953 auto Touching = syntax::spelledTokensTouching(SpelledLoc, TB);
954 for (
const auto &T : Touching) {
956 if (tok::isAnyIdentifier(T.kind()) || tok::getKeywordSpelling(T.kind())) {
958 Result.Location = T.location();
959 Result.Text = T.text(SM);
960 Result.LikelyIdentifier = tok::isAnyIdentifier(T.kind());
961 Result.PartOfSpelledToken = &T;
962 Result.SpelledToken = &T;
964 TB.expandedTokens(SM.getMacroArgExpandedLocation(T.location()));
965 if (Expanded.size() == 1 && Expanded.front().text(SM) == Result.Text)
966 Result.ExpandedToken = &Expanded.front();
972 std::tie(File,
Offset) = SM.getDecomposedLoc(SpelledLoc);
973 bool Invalid =
false;
974 llvm::StringRef Code = SM.getBufferData(File, &Invalid);
978 while (B > 0 && isAsciiIdentifierContinue(Code[B - 1]))
980 while (
E < Code.size() && isAsciiIdentifierContinue(Code[
E]))
986 Result.Location = SM.getComposedLoc(File, B);
987 Result.Text = Code.slice(B,
E);
988 Result.LikelyIdentifier =
991 tok::isAnyIdentifier(
992 IdentifierTable(LangOpts).get(Result.Text).getTokenID());
993 for (
const auto &T : Touching)
994 if (T.location() <= Result.Location)
995 Result.PartOfSpelledToken = &T;
1002 return std::nullopt;
1004 assert(
Loc.isFileID());
1005 const auto &SM = PP.getSourceManager();
1006 IdentifierInfo *IdentifierInfo = PP.getIdentifierInfo(
SpelledTok.text(SM));
1007 if (!IdentifierInfo || !IdentifierInfo->hadMacroDefinition())
1008 return std::nullopt;
1018 FileID FID = SM.getFileID(
Loc);
1019 assert(
Loc != SM.getLocForEndOfFile(FID));
1020 SourceLocation JustAfterToken =
Loc.getLocWithOffset(1);
1022 PP.getMacroDefinitionAtLoc(IdentifierInfo, JustAfterToken).getMacroInfo();
1023 if (!MacroInfo && SM.getLocForStartOfFile(FID) !=
Loc) {
1024 SourceLocation JustBeforeToken =
Loc.getLocWithOffset(-1);
1025 MacroInfo = PP.getMacroDefinitionAtLoc(IdentifierInfo, JustBeforeToken)
1029 return std::nullopt;
1032 IdentifierInfo->getName(), MacroInfo,
1036llvm::Expected<std::string> Edit::apply()
const {
1037 return tooling::applyAllReplacements(InitialCode, Replacements);
1040std::vector<TextEdit> Edit::asTextEdits()
const {
1044bool Edit::canApplyTo(llvm::StringRef Code)
const {
1047 auto LHS = llvm::MemoryBuffer::getMemBuffer(Code);
1048 llvm::line_iterator LHSIt(*LHS,
false);
1050 auto RHS = llvm::MemoryBuffer::getMemBuffer(InitialCode);
1051 llvm::line_iterator RHSIt(*RHS,
false);
1057 while (!LHSIt.is_at_eof() && !RHSIt.is_at_eof()) {
1058 if (*LHSIt != *RHSIt)
1067 while (!LHSIt.is_at_eof()) {
1068 if (!LHSIt->empty())
1072 while (!RHSIt.is_at_eof()) {
1073 if (!RHSIt->empty())
1082 E.Replacements = std::move(*NewEdits);
1084 return NewEdits.takeError();
1085 return llvm::Error::success();
1110 if (!Contents.empty() && Contents.back() ==
'\n')
1112 if (
Pos.character != 0)
1114 if (
Pos.line != llvm::count(Contents,
'\n') + 1)
1116 log(
"Editor sent invalid change coordinates, inferring newline at EOF");
1117 Contents.push_back(
'\n');
1118 consumeError(Err.takeError());
1119 Err = Contents.size();
1124 if (!Change.
range) {
1125 Contents = Change.
text;
1126 return llvm::Error::success();
1130 llvm::Expected<size_t> StartIndex =
positionToOffset(Contents, Start,
false);
1133 return StartIndex.takeError();
1139 return EndIndex.takeError();
1141 if (*EndIndex < *StartIndex)
1142 return error(llvm::errc::invalid_argument,
1143 "Range's end position ({0}) is before start position ({1})",
1153 ssize_t ComputedRangeLength =
1154 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
1157 return error(llvm::errc::invalid_argument,
1158 "Change's rangeLength ({0}) doesn't match the "
1159 "computed range length ({1}).",
1162 Contents.replace(*StartIndex, *EndIndex - *StartIndex, Change.
text);
1164 return llvm::Error::success();
1168 llvm::StringRef FullyQualifiedName,
1169 const LangOptions &LangOpts) {
1172 std::vector<std::string> Enclosing = {
""};
1176 parseNamespaceEvents(Code, LangOpts, [&](NamespaceEvent
Event) {
1179 if (
Event.Trigger == NamespaceEvent::UsingDirective)
1182 if (!
Event.Payload.empty())
1183 Event.Payload.append(
"::");
1185 std::string CurrentNamespace;
1186 if (
Event.Trigger == NamespaceEvent::BeginNamespace) {
1187 Enclosing.emplace_back(std::move(Event.Payload));
1188 CurrentNamespace = Enclosing.back();
1191 ++Event.Pos.character;
1195 CurrentNamespace = std::move(Enclosing.back());
1196 Enclosing.pop_back();
1197 assert(Enclosing.back() == Event.Payload);
1201 if (!FullyQualifiedName.starts_with(CurrentNamespace))
1206 ER.EligiblePoints.clear();
1207 ER.EnclosingNamespace = CurrentNamespace;
1221 std::optional<LangOptions> LangOpts) {
1224 if (LangOpts && LangOpts->IsHeaderFile)
1226 namespace types = clang::driver::types;
1227 auto Lang = types::lookupTypeForExtension(
1228 llvm::sys::path::extension(
FileName).substr(1));
1229 return Lang != types::TY_INVALID && types::onlyPrecompileType(
Lang);
1236 auto FID = SM.getFileID(
Loc);
1238 static const char *ProtoHeaderComment =
1239 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
1241 return SM.getBufferData(FID).starts_with(ProtoHeaderComment);
1245 const SourceManager &SM) {
1246 auto DefFile = SM.getFileID(
Loc);
1247 if (
auto FE = SM.getFileEntryRefForID(DefFile)) {
1248 auto IncludeLoc = SM.getIncludeLoc(DefFile);
1250 if (IncludeLoc.isValid() && SM.isWrittenInBuiltinFile(IncludeLoc) &&
1251 FE->getName().ends_with(PreamblePatch::HeaderName)) {
1252 auto Presumed = SM.getPresumedLoc(
Loc);
1254 if (Presumed.isValid() && Presumed.getFileID().isInvalid() &&
1255 isMainFile(Presumed.getFilename(), SM)) {
1256 Loc = SM.translateLineCol(SM.getMainFileID(), Presumed.getLine(),
1257 Presumed.getColumn());
1269 Result.end.character +=
1271 return C ==
'\n' || C ==
'\r';
ArrayRef< const ParmVarDecl * > Tail
ArrayRef< const ParmVarDecl * > Head
enum clang::clangd::@1062::Bracket::Direction Dir
std::vector< CodeCompletionResult > Results
const google::protobuf::Message & M
enum clang::clangd::@1060::NamespaceEvent::@17 Trigger
std::optional< FixItHint > FixIt
static const Context & current()
Returns the context for the current thread, creating it if needed.
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
An Event<T> allows events of type T to be broadcast to listeners.
Values in a Context are indexed by typed keys.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
bool isValidFileRange(const SourceManager &Mgr, SourceRange R)
Returns true iff all of the following conditions hold:
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
static SourceLocation getLocForTokenBegin(SourceLocation EndLoc, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
SourceLocation includeHashLoc(FileID IncludedFile, const SourceManager &SM)
Returns the #include location through which IncludedFIle was loaded.
llvm::Error applyChange(std::string &Contents, const TextDocumentContentChangeEvent &Change)
Apply an incremental update to a text document.
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, const LangOptions &L)
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
size_t lspLength(llvm::StringRef Code)
Key< OffsetEncoding > kCurrentOffsetEncoding
static void lex(llvm::StringRef Code, const LangOptions &LangOpts, llvm::function_ref< void(const syntax::Token &, const SourceManager &SM)> Action)
FileDigest digest(llvm::StringRef Content)
static bool isLikelyIdentifier(llvm::StringRef Word, llvm::StringRef Before, llvm::StringRef After)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
llvm::Error reformatEdit(Edit &E, const format::FormatStyle &Style)
Formats the edits and code around it according to Style.
void unionRanges(Range &A, Range B)
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
std::array< uint8_t, 8 > FileDigest
static SourceRange rangeInCommonFile(SourceRange R, const SourceManager &SM, const LangOptions &LangOpts)
SourceLocation translatePreamblePatchLocation(SourceLocation Loc, const SourceManager &SM)
Translates locations inside preamble patch to their main-file equivalent using presumed locations.
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
llvm::StringMap< unsigned > collectIdentifiers(llvm::StringRef Content, const format::FormatStyle &Style)
Collects identifiers with counts in the source code.
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
std::optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
static void inferFinalNewline(llvm::Expected< size_t > &Err, std::string &Contents, const Position &Pos)
std::vector< std::string > visibleNamespaces(llvm::StringRef Code, const LangOptions &LangOpts)
Heuristically determine namespaces visible at a point, without parsing Code.
static unsigned getTokenLengthAtLoc(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
static SourceRange getTokenFileRange(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
static SourceRange toTokenRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
static bool iterateCodepoints(llvm::StringRef U8, const Callback &CB)
static SourceRange unionTokenRange(SourceRange R1, SourceRange R2, const SourceManager &SM, const LangOptions &LangOpts)
void log(const char *Fmt, Ts &&... Vals)
EligibleRegion getEligiblePoints(llvm::StringRef Code, llvm::StringRef FullyQualifiedName, const LangOptions &LangOpts)
Returns most eligible region to insert a definition for FullyQualifiedName in the Code.
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
std::vector< Range > collectIdentifierRanges(llvm::StringRef Identifier, llvm::StringRef Content, const LangOptions &LangOpts)
Collects all ranges of the given identifier in the source code.
static SourceRange getExpansionTokenRangeInSameFile(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringSet collectWords(llvm::StringRef Content)
Collects words from the source code.
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
static SourceLocation getLocForTokenEnd(SourceLocation BeginLoc, const SourceManager &SM, const LangOptions &LangOpts)
bool isKeyword(llvm::StringRef NewName, const LangOptions &LangOpts)
Return true if the TokenName is in the list of reversed keywords of the language.
CharTypeSet calculateRoles(llvm::StringRef Text, llvm::MutableArrayRef< CharRole > Roles)
llvm::Expected< tooling::Replacements > cleanupAndFormat(StringRef Code, const tooling::Replacements &Replaces, const format::FormatStyle &Style)
Cleanup and format the given replacements.
std::pair< size_t, size_t > offsetToClangLineColumn(llvm::StringRef Code, size_t Offset)
bool isSpelledInSource(SourceLocation Loc, const SourceManager &SM)
Returns true if the token at Loc is spelled in the source code.
clangd::Range rangeTillEOL(llvm::StringRef Code, unsigned HashOffset)
Returns the range starting at offset and spanning the whole line.
static OffsetEncoding lspEncoding()
static size_t measureUnits(llvm::StringRef U8, int Units, OffsetEncoding Enc, bool &Valid)
void elog(const char *Fmt, Ts &&... Vals)
bool isProtoFile(SourceLocation Loc, const SourceManager &SM)
Returns true if the given location is in a generated protobuf file.
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
std::vector< TextEdit > replacementsToEdits(llvm::StringRef Code, const tooling::Replacements &Repls)
TextEdit replacementToEdit(llvm::StringRef Code, const tooling::Replacement &R)
format::FormatStyle getFormatStyleForFile(llvm::StringRef File, llvm::StringRef Content, const ThreadsafeFS &TFS, bool FormatFile)
Choose the clang-format style we should apply to a certain file.
llvm::SmallString< 40 > NamespaceName
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
Represents locations that can accept a definition.
std::vector< Position > EligiblePoints
Offsets into the code marking eligible points to insert a function definition.
std::string EnclosingNamespace
Namespace that owns all of the EligiblePoints, e.g.
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
std::optional< Range > range
The range of the document that changed.
std::string text
The new text of the range/document.
std::optional< int > rangeLength
The length of the range that got replaced.
A single C++ or preprocessor token.
StringRef text() const
The token text.