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 StringRef SpellingFile = SM.getSLocEntry(Spelling.first).getFile().getName();
236 if (SpellingFile ==
"<scratch space>")
238 if (SpellingFile ==
"<built-in>")
240 return !SM.isWrittenInCommandLineFile(
241 SM.getComposedLoc(Spelling.first, Spelling.second));
246 if (!R.getBegin().isValid() || !R.getEnd().isValid())
250 size_t BeginOffset = 0;
251 std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
254 size_t EndOffset = 0;
255 std::tie(EndFID, EndOffset) = Mgr.getDecomposedLoc(R.getEnd());
257 return BeginFID.isValid() && BeginFID == EndFID && BeginOffset <= EndOffset;
261 assert(SM.getLocForEndOfFile(IncludedFile).isFileID());
262 FileID IncludingFile;
264 std::tie(IncludingFile,
Offset) =
265 SM.getDecomposedExpansionLoc(SM.getIncludeLoc(IncludedFile));
266 bool Invalid =
false;
267 llvm::StringRef Buf = SM.getBufferData(IncludingFile, &Invalid);
269 return SourceLocation();
273 assert(
Offset < Buf.size());
276 return SM.getComposedLoc(IncludingFile,
Offset);
278 return SourceLocation();
283 const LangOptions &LangOpts) {
285 if (Lexer::getRawToken(
Loc, TheTok, SM, LangOpts))
292 if (TheTok.is(tok::greatergreater))
294 return TheTok.getLength();
299 const SourceManager &SM,
300 const LangOptions &LangOpts) {
302 return BeginLoc.getLocWithOffset(Len ? Len - 1 : 0);
307 const SourceManager &SM,
308 const LangOptions &LangOpts) {
309 return EndLoc.getLocWithOffset(
315 const LangOptions &LangOpts) {
316 if (!
Range.isTokenRange())
318 return Range.getAsRange();
324 const SourceManager &SM,
325 const LangOptions &LangOpts) {
326 SourceLocation Begin =
327 SM.isBeforeInTranslationUnit(R1.getBegin(), R2.getBegin())
335 return SourceRange(Begin, End);
342 const LangOptions &LangOpts) {
344 if (SM.isWrittenInSameFile(R.getBegin(), R.getEnd()))
347 llvm::DenseMap<FileID, SourceLocation> BeginExpansions;
348 for (SourceLocation Begin = R.getBegin(); Begin.isValid();
349 Begin = Begin.isFileID()
351 : SM.getImmediateExpansionRange(Begin).getBegin()) {
352 BeginExpansions[SM.getFileID(Begin)] = Begin;
356 for (SourceLocation End = R.getEnd(); End.isValid();
361 auto It = BeginExpansions.find(SM.getFileID(End));
362 if (It != BeginExpansions.end()) {
363 if (SM.getFileOffset(It->second) > SM.getFileOffset(End))
364 return SourceLocation();
365 return {It->second, End};
368 return SourceRange();
375 const LangOptions &LangOpts) {
392 const SourceManager &SM,
393 const LangOptions &LangOpts) {
394 SourceRange FileRange =
Loc;
395 while (!FileRange.getBegin().isFileID()) {
396 if (SM.isMacroArgExpansion(FileRange.getBegin())) {
398 SM.getImmediateSpellingLoc(FileRange.getBegin()),
399 SM.getImmediateSpellingLoc(FileRange.getEnd()), SM, LangOpts);
400 assert(SM.isWrittenInSameFile(FileRange.getBegin(), FileRange.getEnd()));
402 SourceRange ExpansionRangeForBegin =
404 SourceRange ExpansionRangeForEnd =
406 if (ExpansionRangeForBegin.isInvalid() ||
407 ExpansionRangeForEnd.isInvalid())
408 return SourceRange();
409 assert(SM.isWrittenInSameFile(ExpansionRangeForBegin.getBegin(),
410 ExpansionRangeForEnd.getBegin()) &&
411 "Both Expansion ranges should be in same file.");
412 FileRange =
unionTokenRange(ExpansionRangeForBegin, ExpansionRangeForEnd,
422 FileID FID = SM.getFileID(SM.getExpansionLoc(
Loc));
423 return FID == SM.getMainFileID() || FID == SM.getPreambleFileID();
427 const LangOptions &LangOpts,
441 Result.setEnd(Result.getEnd().getLocWithOffset(TokLen));
450 auto Buf = SM.getBufferOrNone(SM.getFileID(R.getBegin()));
453 size_t BeginOffset = SM.getFileOffset(R.getBegin());
454 size_t EndOffset = SM.getFileOffset(R.getEnd());
455 return Buf->getBuffer().substr(BeginOffset, EndOffset - BeginOffset);
460 llvm::StringRef
Code = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
464 return Offset.takeError();
465 return SM.getLocForStartOfFile(SM.getMainFileID()).getLocWithOffset(*
Offset);
477 if (B.start < A.start)
486 llvm::StringRef Before =
Code.substr(0,
Offset);
487 int Lines = Before.count(
'\n');
488 size_t PrevNL = Before.rfind(
'\n');
489 size_t StartOfLine = (PrevNL == llvm::StringRef::npos) ? 0 : (PrevNL + 1);
494 size_t Pos = QName.rfind(
"::");
495 if (
Pos == llvm::StringRef::npos)
496 return {llvm::StringRef(), QName};
497 return {QName.substr(0,
Pos + 2), QName.substr(
Pos + 2)};
501 const tooling::Replacement &R) {
502 Range ReplacementRange = {
505 return {ReplacementRange, std::string(R.getReplacementText())};
509 const tooling::Replacements &Repls) {
510 std::vector<TextEdit> Edits;
511 for (
const auto &R : Repls)
517 FileManager &FileMgr) {
518 llvm::SmallString<128> FilePath = F.getName();
519 if (!llvm::sys::path::is_absolute(FilePath)) {
521 FileMgr.getVirtualFileSystem().makeAbsolute(
523 elog(
"Could not turn relative path '{0}' to absolute: {1}", FilePath,
540 if (
auto Dir = FileMgr.getDirectory(
541 llvm::sys::path::parent_path(FilePath))) {
542 llvm::SmallString<128> RealPath;
543 llvm::StringRef DirName = FileMgr.getCanonicalName(*Dir);
544 llvm::sys::path::append(RealPath, DirName,
545 llvm::sys::path::filename(FilePath));
546 return RealPath.str().str();
549 return FilePath.str().str();
553 const LangOptions &L) {
557 Result.newText =
FixIt.CodeToInsert;
562 uint64_t Hash{llvm::xxHash64(Content)};
564 for (
unsigned I = 0; I < Result.size(); ++I) {
565 Result[I] = uint8_t(Hash);
571std::optional<FileDigest>
digestFile(
const SourceManager &SM, FileID FID) {
572 bool Invalid =
false;
573 llvm::StringRef Content = SM.getBufferData(FID, &Invalid);
580 llvm::StringRef Content,
582 auto Style = format::getStyle(format::DefaultFormatStyle,
File,
583 format::DefaultFallbackStyle, Content,
584 TFS.
view(std::nullopt).get());
586 log(
"getStyle() failed for file {0}: {1}. Fallback is LLVM style.",
File,
588 return format::getLLVMStyle();
593llvm::Expected<tooling::Replacements>
595 const format::FormatStyle &Style) {
596 auto CleanReplaces = cleanupAroundReplacements(
Code, Replaces, Style);
598 return CleanReplaces;
599 return formatReplacements(
Code, std::move(*CleanReplaces), Style);
603lex(llvm::StringRef
Code,
const LangOptions &LangOpts,
604 llvm::function_ref<
void(
const syntax::Token &,
const SourceManager &SM)>
607 std::string NullTerminatedCode =
Code.str();
608 SourceManagerForFile FileSM(
"mock_file_name.cpp", NullTerminatedCode);
609 auto &SM = FileSM.get();
610 for (
const auto &Tok : syntax::tokenize(SM.getMainFileID(), SM, LangOpts))
615 const format::FormatStyle &Style) {
616 llvm::StringMap<unsigned> Identifiers;
617 auto LangOpt = format::getFormattingLangOpts(Style);
618 lex(Content, LangOpt, [&](
const syntax::Token &Tok,
const SourceManager &SM) {
619 if (Tok.kind() == tok::identifier)
620 ++Identifiers[Tok.text(SM)];
622 else if (
const auto *
Keyword = tok::getKeywordSpelling(Tok.kind()))
629 llvm::StringRef Content,
630 const LangOptions &LangOpts) {
631 std::vector<Range> Ranges;
632 lex(Content, LangOpts,
633 [&](
const syntax::Token &Tok,
const SourceManager &SM) {
634 if (Tok.kind() != tok::identifier || Tok.text(SM) !=
Identifier)
641bool isKeyword(llvm::StringRef NewName,
const LangOptions &LangOpts) {
643 clang::IdentifierTable KeywordsTable(LangOpts);
644 return KeywordsTable.find(NewName) != KeywordsTable.end();
648struct NamespaceEvent {
659void parseNamespaceEvents(llvm::StringRef
Code,
const LangOptions &LangOpts,
660 llvm::function_ref<
void(NamespaceEvent)>
Callback) {
663 std::vector<std::string> Enclosing;
665 llvm::BitVector BraceStack;
677 NamespaceEvent
Event;
678 lex(
Code, LangOpts, [&](
const syntax::Token &Tok,
const SourceManager &SM) {
680 switch (Tok.kind()) {
682 State = State == Default ? Using : Default;
684 case tok::kw_namespace:
687 State = UsingNamespace;
697 case tok::identifier:
702 case UsingNamespaceName:
703 NSName.append(Tok.text(SM).str());
704 State = UsingNamespaceName;
710 NSName.append(Tok.text(SM).str());
719 case tok::coloncolon:
726 case UsingNamespaceName:
728 State = UsingNamespaceName;
743 if (State == NamespaceName) {
745 BraceStack.push_back(
true);
746 Enclosing.push_back(NSName);
747 Event.Trigger = NamespaceEvent::BeginNamespace;
748 Event.Payload = llvm::join(Enclosing,
"::");
753 BraceStack.push_back(
false);
760 if (!BraceStack.empty()) {
761 if (BraceStack.back()) {
763 Enclosing.pop_back();
764 Event.Trigger = NamespaceEvent::EndNamespace;
765 Event.Payload = llvm::join(Enclosing,
"::");
768 BraceStack.pop_back();
772 if (State == UsingNamespaceName) {
774 Event.Trigger = NamespaceEvent::UsingDirective;
775 Event.Payload = std::move(NSName);
788llvm::SmallVector<llvm::StringRef> ancestorNamespaces(llvm::StringRef NS) {
789 llvm::SmallVector<llvm::StringRef>
Results;
790 Results.push_back(NS.take_front(0));
791 NS.split(
Results,
"::", -1,
false);
792 for (llvm::StringRef &R :
Results)
793 R = NS.take_front(R.end() - NS.begin());
798bool isMainFile(llvm::StringRef
FileName,
const SourceManager &SM) {
799 auto FE = SM.getFileManager().getFile(
FileName);
800 return FE && *FE == SM.getFileEntryForID(SM.getMainFileID());
806 const LangOptions &LangOpts) {
809 llvm::StringMap<llvm::StringSet<>> UsingDirectives;
811 parseNamespaceEvents(
Code, LangOpts, [&](NamespaceEvent
Event) {
812 llvm::StringRef NS =
Event.Payload;
813 switch (
Event.Trigger) {
814 case NamespaceEvent::BeginNamespace:
815 case NamespaceEvent::EndNamespace:
816 Current = std::move(Event.Payload);
818 case NamespaceEvent::UsingDirective:
819 if (NS.consume_front(
"::"))
820 UsingDirectives[Current].insert(NS);
822 for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
823 if (Enclosing.empty())
824 UsingDirectives[Current].insert(NS);
826 UsingDirectives[Current].insert((Enclosing +
"::" + NS).str());
833 std::vector<std::string> Found;
834 for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
835 Found.push_back(std::string(Enclosing));
836 auto It = UsingDirectives.find(Enclosing);
837 if (It != UsingDirectives.end())
838 for (
const auto &Used : It->second)
839 Found.push_back(std::string(Used.getKey()));
842 llvm::sort(Found, [&](
const std::string &LHS,
const std::string &RHS) {
849 Found.erase(std::unique(Found.begin(), Found.end()), Found.end());
857 static constexpr int MinWordLength = 4;
859 std::vector<CharRole> Roles(Content.size());
862 llvm::StringSet<> Result;
863 llvm::SmallString<256>
Word;
865 if (
Word.size() >= MinWordLength) {
867 C = llvm::toLower(
C);
872 for (
unsigned I = 0; I < Content.size(); ++I) {
878 Word.push_back(Content[I]);
892 llvm::StringRef After) {
894 if (Before.endswith(
"`") && After.startswith(
"`"))
897 if (Before.endswith(
"::") || After.startswith(
"::"))
902 Before = Before.take_back(100);
903 auto Pos = Before.find_last_of(
"\\@");
904 if (
Pos != llvm::StringRef::npos) {
905 llvm::StringRef
Tag = Before.substr(
Pos + 1).rtrim(
' ');
906 if (
Tag ==
"p" ||
Tag ==
"c" ||
Tag ==
"class" ||
Tag ==
"tparam" ||
907 Tag ==
"param" ||
Tag ==
"param[in]" ||
Tag ==
"param[out]" ||
908 Tag ==
"param[in,out]" ||
Tag ==
"retval" ||
Tag ==
"throw" ||
909 Tag ==
"throws" ||
Tag ==
"link")
915 if (
Word.contains(
'_')) {
922 bool HasLower =
Word.find_if(clang::isLowercase) != StringRef::npos;
923 bool HasUpper =
Word.substr(1).find_if(clang::isUppercase) != StringRef::npos;
924 if (HasLower && HasUpper) {
931std::optional<SpelledWord> SpelledWord::touching(SourceLocation SpelledLoc,
932 const syntax::TokenBuffer &TB,
933 const LangOptions &LangOpts) {
934 const auto &SM = TB.sourceManager();
935 auto Touching = syntax::spelledTokensTouching(SpelledLoc, TB);
936 for (
const auto &T : Touching) {
938 if (tok::isAnyIdentifier(T.kind()) || tok::getKeywordSpelling(T.kind())) {
940 Result.Location = T.location();
941 Result.Text = T.text(SM);
942 Result.LikelyIdentifier = tok::isAnyIdentifier(T.kind());
943 Result.PartOfSpelledToken = &T;
944 Result.SpelledToken = &T;
946 TB.expandedTokens(SM.getMacroArgExpandedLocation(T.location()));
947 if (Expanded.size() == 1 && Expanded.front().text(SM) == Result.Text)
948 Result.ExpandedToken = &Expanded.front();
954 std::tie(File,
Offset) = SM.getDecomposedLoc(SpelledLoc);
955 bool Invalid =
false;
956 llvm::StringRef
Code = SM.getBufferData(File, &Invalid);
960 while (B > 0 && isAsciiIdentifierContinue(
Code[B - 1]))
962 while (
E <
Code.size() && isAsciiIdentifierContinue(
Code[
E]))
968 Result.Location = SM.getComposedLoc(File, B);
969 Result.Text =
Code.slice(B,
E);
970 Result.LikelyIdentifier =
973 tok::isAnyIdentifier(
974 IdentifierTable(LangOpts).get(Result.Text).getTokenID());
975 for (
const auto &T : Touching)
976 if (T.location() <= Result.Location)
977 Result.PartOfSpelledToken = &T;
986 assert(
Loc.isFileID());
987 const auto &SM = PP.getSourceManager();
988 IdentifierInfo *IdentifierInfo = PP.getIdentifierInfo(
SpelledTok.text(SM));
989 if (!IdentifierInfo || !IdentifierInfo->hadMacroDefinition())
1000 FileID FID = SM.getFileID(
Loc);
1001 assert(
Loc != SM.getLocForEndOfFile(FID));
1002 SourceLocation JustAfterToken =
Loc.getLocWithOffset(1);
1004 PP.getMacroDefinitionAtLoc(IdentifierInfo, JustAfterToken).getMacroInfo();
1005 if (!MacroInfo && SM.getLocForStartOfFile(FID) !=
Loc) {
1006 SourceLocation JustBeforeToken =
Loc.getLocWithOffset(-1);
1007 MacroInfo = PP.getMacroDefinitionAtLoc(IdentifierInfo, JustBeforeToken)
1011 return std::nullopt;
1014 IdentifierInfo->getName(), MacroInfo,
1018llvm::Expected<std::string> Edit::apply()
const {
1019 return tooling::applyAllReplacements(InitialCode, Replacements);
1022std::vector<TextEdit> Edit::asTextEdits()
const {
1026bool Edit::canApplyTo(llvm::StringRef
Code)
const {
1029 auto LHS = llvm::MemoryBuffer::getMemBuffer(
Code);
1030 llvm::line_iterator LHSIt(*LHS,
false);
1032 auto RHS = llvm::MemoryBuffer::getMemBuffer(InitialCode);
1033 llvm::line_iterator RHSIt(*RHS,
false);
1039 while (!LHSIt.is_at_eof() && !RHSIt.is_at_eof()) {
1040 if (*LHSIt != *RHSIt)
1049 while (!LHSIt.is_at_eof()) {
1050 if (!LHSIt->empty())
1054 while (!RHSIt.is_at_eof()) {
1055 if (!RHSIt->empty())
1064 E.Replacements = std::move(*NewEdits);
1066 return NewEdits.takeError();
1067 return llvm::Error::success();
1092 if (!Contents.empty() && Contents.back() ==
'\n')
1094 if (
Pos.character != 0)
1096 if (
Pos.line != llvm::count(Contents,
'\n') + 1)
1098 log(
"Editor sent invalid change coordinates, inferring newline at EOF");
1099 Contents.push_back(
'\n');
1100 consumeError(Err.takeError());
1101 Err = Contents.size();
1106 if (!Change.
range) {
1107 Contents = Change.
text;
1108 return llvm::Error::success();
1112 llvm::Expected<size_t> StartIndex =
positionToOffset(Contents, Start,
false);
1115 return StartIndex.takeError();
1121 return EndIndex.takeError();
1123 if (*EndIndex < *StartIndex)
1124 return error(llvm::errc::invalid_argument,
1125 "Range's end position ({0}) is before start position ({1})",
1135 ssize_t ComputedRangeLength =
1136 lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
1139 return error(llvm::errc::invalid_argument,
1140 "Change's rangeLength ({0}) doesn't match the "
1141 "computed range length ({1}).",
1144 Contents.replace(*StartIndex, *EndIndex - *StartIndex, Change.
text);
1146 return llvm::Error::success();
1150 llvm::StringRef FullyQualifiedName,
1151 const LangOptions &LangOpts) {
1154 std::vector<std::string> Enclosing = {
""};
1158 parseNamespaceEvents(
Code, LangOpts, [&](NamespaceEvent
Event) {
1161 if (
Event.Trigger == NamespaceEvent::UsingDirective)
1164 if (!
Event.Payload.empty())
1165 Event.Payload.append(
"::");
1167 std::string CurrentNamespace;
1168 if (
Event.Trigger == NamespaceEvent::BeginNamespace) {
1169 Enclosing.emplace_back(std::move(Event.Payload));
1170 CurrentNamespace = Enclosing.back();
1173 ++Event.Pos.character;
1177 CurrentNamespace = std::move(Enclosing.back());
1178 Enclosing.pop_back();
1179 assert(Enclosing.back() == Event.Payload);
1183 if (!FullyQualifiedName.startswith(CurrentNamespace))
1188 ER.EligiblePoints.clear();
1189 ER.EnclosingNamespace = CurrentNamespace;
1203 std::optional<LangOptions> LangOpts) {
1206 if (LangOpts && LangOpts->IsHeaderFile)
1208 namespace types = clang::driver::types;
1209 auto Lang = types::lookupTypeForExtension(
1210 llvm::sys::path::extension(
FileName).substr(1));
1211 return Lang != types::TY_INVALID && types::onlyPrecompileType(
Lang);
1218 auto FID = SM.getFileID(
Loc);
1220 static const char *ProtoHeaderComment =
1221 "// Generated by the protocol buffer compiler. DO NOT EDIT!";
1223 return SM.getBufferData(FID).startswith(ProtoHeaderComment);
1227 const SourceManager &SM) {
1228 auto DefFile = SM.getFileID(
Loc);
1229 if (
auto FE = SM.getFileEntryRefForID(DefFile)) {
1230 auto IncludeLoc = SM.getIncludeLoc(DefFile);
1232 if (IncludeLoc.isValid() && SM.isWrittenInBuiltinFile(IncludeLoc) &&
1233 FE->getName().endswith(PreamblePatch::HeaderName)) {
1234 auto Presumed = SM.getPresumedLoc(
Loc);
1236 if (Presumed.isValid() && Presumed.getFileID().isInvalid() &&
1237 isMainFile(Presumed.getFilename(), SM)) {
1238 Loc = SM.translateLineCol(SM.getMainFileID(), Presumed.getLine(),
1239 Presumed.getColumn());
ArrayRef< const ParmVarDecl * > Tail
ArrayRef< const ParmVarDecl * > Head
std::vector< CodeCompletionResult > Results
const google::protobuf::Message & M
enum clang::clangd::@955::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)
format::FormatStyle getFormatStyleForFile(llvm::StringRef File, llvm::StringRef Content, const ThreadsafeFS &TFS)
Choose the clang-format style we should apply to a certain file.
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.
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)
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.