MLIR 22.0.0git
SourceMgrUtils.cpp
Go to the documentation of this file.
1//===--- SourceMgrUtils.cpp - SourceMgr LSP Utils -------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "llvm/ADT/StringExtras.h"
11#include "llvm/Support/Path.h"
12#include <optional>
13
14using namespace mlir;
15using namespace mlir::lsp;
16
17using llvm::lsp::Hover;
18using llvm::lsp::Range;
19using llvm::lsp::URIForFile;
20
21//===----------------------------------------------------------------------===//
22// Utils
23//===----------------------------------------------------------------------===//
24
25/// Find the end of a string whose contents start at the given `curPtr`. Returns
26/// the position at the end of the string, after a terminal or invalid character
27/// (e.g. `"` or `\0`).
28static const char *lexLocStringTok(const char *curPtr) {
29 while (char c = *curPtr++) {
30 // Check for various terminal characters.
31 if (StringRef("\"\n\v\f").contains(c))
32 return curPtr;
33
34 // Check for escape sequences.
35 if (c == '\\') {
36 // Check a few known escapes and \xx hex digits.
37 if (*curPtr == '"' || *curPtr == '\\' || *curPtr == 'n' || *curPtr == 't')
38 ++curPtr;
39 else if (llvm::isHexDigit(*curPtr) && llvm::isHexDigit(curPtr[1]))
40 curPtr += 2;
41 else
42 return curPtr;
43 }
44 }
45
46 // If we hit this point, we've reached the end of the buffer. Update the end
47 // pointer to not point past the buffer.
48 return curPtr - 1;
49}
50
51SMRange lsp::convertTokenLocToRange(SMLoc loc, StringRef identifierChars) {
52 if (!loc.isValid())
53 return SMRange();
54 const char *curPtr = loc.getPointer();
55
56 // Check if this is a string token.
57 if (*curPtr == '"') {
58 curPtr = lexLocStringTok(curPtr + 1);
59
60 // Otherwise, default to handling an identifier.
61 } else {
62 // Return if the given character is a valid identifier character.
63 auto isIdentifierChar = [=](char c) {
64 return isalnum(c) || c == '_' || identifierChars.contains(c);
65 };
66
67 while (*curPtr && isIdentifierChar(*(++curPtr)))
68 continue;
69 }
70
71 return SMRange(loc, SMLoc::getFromPointer(curPtr));
72}
73
74std::optional<std::string>
75lsp::extractSourceDocComment(llvm::SourceMgr &sourceMgr, SMLoc loc) {
76 // This is a heuristic, and isn't intended to cover every case, but should
77 // cover the most common. We essentially look for a comment preceding the
78 // line, and if we find one, use that as the documentation.
79 if (!loc.isValid())
80 return std::nullopt;
81 int bufferId = sourceMgr.FindBufferContainingLoc(loc);
82 if (bufferId == 0)
83 return std::nullopt;
84 const char *bufferStart =
85 sourceMgr.getMemoryBuffer(bufferId)->getBufferStart();
86 StringRef buffer(bufferStart, loc.getPointer() - bufferStart);
87
88 // Pop the last line from the buffer string.
89 auto popLastLine = [&]() -> std::optional<StringRef> {
90 size_t newlineOffset = buffer.find_last_of('\n');
91 if (newlineOffset == StringRef::npos)
92 return std::nullopt;
93 StringRef lastLine = buffer.drop_front(newlineOffset).trim();
94 buffer = buffer.take_front(newlineOffset);
95 return lastLine;
96 };
97
98 // Try to pop the current line.
99 if (!popLastLine())
100 return std::nullopt;
101
102 // Try to parse a comment string from the source file.
103 SmallVector<StringRef> commentLines;
104 while (std::optional<StringRef> line = popLastLine()) {
105 // Check for a comment at the beginning of the line.
106 if (!line->starts_with("//"))
107 break;
108
109 // Extract the document string from the comment.
110 commentLines.push_back(line->ltrim('/'));
111 }
112
113 if (commentLines.empty())
114 return std::nullopt;
115 return llvm::join(llvm::reverse(commentLines), "\n");
116}
117
118bool lsp::contains(SMRange range, SMLoc loc) {
119 return range.Start.getPointer() <= loc.getPointer() &&
120 loc.getPointer() < range.End.getPointer();
121}
122
123//===----------------------------------------------------------------------===//
124// SourceMgrInclude
125//===----------------------------------------------------------------------===//
126
128 Hover hover(range);
129 {
130 llvm::raw_string_ostream hoverOS(hover.contents.value);
131 hoverOS << "`" << llvm::sys::path::filename(uri.file()) << "`\n***\n"
132 << uri.file();
133 }
134 return hover;
135}
136
137void lsp::gatherIncludeFiles(llvm::SourceMgr &sourceMgr,
139 for (unsigned i = 1, e = sourceMgr.getNumBuffers(); i < e; ++i) {
140 // Check to see if this file was included by the main file.
141 SMLoc includeLoc = sourceMgr.getBufferInfo(i + 1).IncludeLoc;
142 if (!includeLoc.isValid() || sourceMgr.FindBufferContainingLoc(
143 includeLoc) != sourceMgr.getMainFileID())
144 continue;
145
146 // Try to build a URI for this file path.
147 auto *buffer = sourceMgr.getMemoryBuffer(i + 1);
148 llvm::SmallString<256> path(buffer->getBufferIdentifier());
149 llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true);
150
151 llvm::Expected<URIForFile> includedFileURI = URIForFile::fromFile(path);
152 if (!includedFileURI)
153 continue;
154
155 // Find the end of the include token.
156 const char *includeStart = includeLoc.getPointer() - 2;
157 while (*(--includeStart) != '\"')
158 continue;
159
160 // Push this include.
161 SMRange includeRange(SMLoc::getFromPointer(includeStart), includeLoc);
162 includes.emplace_back(*includedFileURI, Range(sourceMgr, includeRange));
163 }
164}
static const char * lexLocStringTok(const char *curPtr)
Find the end of a string whose contents start at the given curPtr.
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
std::optional< std::string > extractSourceDocComment(llvm::SourceMgr &sourceMgr, SMLoc loc)
Extract a documentation comment for the given location within the source manager.
SMRange convertTokenLocToRange(SMLoc loc, StringRef identifierChars="")
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
Include the generated interface declarations.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
llvm::lsp::Hover buildHover() const
Build a hover for the current include file.
llvm::lsp::URIForFile uri
The URI of the file that is included.
llvm::lsp::Range range
The range of the include directive.