35#include "slang/syntax/AllSyntax.h"
36#include "slang/syntax/SyntaxTree.h"
38#include "mlir/IR/Attributes.h"
39#include "mlir/IR/BuiltinAttributes.h"
40#include "mlir/IR/Location.h"
41#include "mlir/Support/FileUtilities.h"
43#include "llvm/Support/ConvertUTF.h"
44#include "llvm/Support/FileSystem.h"
45#include "llvm/Support/LineIterator.h"
46#include "llvm/Support/Path.h"
48#include "../Utils/LSPUtils.h"
56using namespace llvm::lsp;
58static std::filesystem::path
61 std::filesystem::path path = std::filesystem::weakly_canonical(file, ec);
63 path = std::filesystem::absolute(file).lexically_normal();
71 const std::string &targetAbsStr) {
72 const std::filesystem::path targetAbs =
76 auto cmdFile = mlir::openInputFile(cmdfileStr, &error);
79 cmdfileStr +
": " + error);
83 const std::filesystem::path base =
84 std::filesystem::path(cmdFile->getBufferIdentifier().str()).parent_path();
87 for (llvm::line_iterator i(*cmdFile); !i.is_at_eof(); ++i) {
88 llvm::StringRef line = i->trim();
93 static constexpr llvm::StringRef commandPrefixes[] = {
"+",
"-"};
94 auto isCommand = [&line](llvm::StringRef s) {
return line.starts_with(s); };
95 if (llvm::any_of(commandPrefixes, isCommand))
98 auto candRel = std::filesystem::path(line.str());
100 candRel.is_absolute() ? candRel : (base / candRel));
102 if (candAbs == targetAbs)
110 StringRef contents, std::vector<llvm::lsp::Diagnostic> &diagnostics)
111 : globalContext(context), uri(uri) {
112 bool skipMainBufferSlangImport =
false;
114 llvm::SmallString<256> canonPath(uri.file());
115 if (std::error_code ec = llvm::sys::fs::real_path(uri.file(), canonPath))
116 canonPath = uri.file();
119 for (
const std::string &cmdFile : context.options.commandFiles) {
120 if (!driver.processCommandFiles(cmdFile,
false,
true)) {
124 skipMainBufferSlangImport |=
129 llvm::SmallString<32> uriDirectory(uri.file());
130 llvm::sys::path::remove_filename(uriDirectory);
132 std::vector<std::string> libDirs;
133 libDirs.push_back(uriDirectory.str().str());
137 auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
140 Twine(
"Failed to create memory buffer for file ") + uri.file());
148 slang::driver::Driver topDriver;
150 auto topSlangBuffer =
151 topDriver.sourceManager.assignText(uri.file(), memBuffer->getBuffer());
152 topDriver.sourceLoader.addBuffer(topSlangBuffer);
154 topDriver.addStandardArgs();
156 if (!topDriver.processOptions()) {
160 if (!topDriver.parseAllSources()) {
167 std::vector<std::string> topModules;
168 for (
auto &t : topDriver.syntaxTrees) {
170 t->root().as_if<slang::syntax::CompilationUnitSyntax>()) {
171 for (
auto *member : compUnit->members) {
174 if (
auto *moduleDecl =
175 member->as_if<slang::syntax::ModuleDeclarationSyntax>()) {
176 topModules.emplace_back(moduleDecl->header->name.valueText());
181 driver.options.topModules = std::move(topModules);
184 for (
const auto &libDir : libDirs) {
185 driver.sourceLoader.addSearchDirectories(libDir);
190 if (!skipMainBufferSlangImport) {
192 driver.sourceManager.assignText(uri.file(), memBuffer->getBuffer());
193 driver.sourceLoader.addBuffer(slangBuffer);
194 mainBufferId = slangBuffer.id;
197 auto diagClient = std::make_shared<LSPDiagnosticClient>(*
this, diagnostics);
198 driver.diagEngine.addClient(diagClient);
200 driver.options.compilationFlags.emplace(
201 slang::ast::CompilationFlags::LintMode,
false);
202 driver.options.compilationFlags.emplace(
203 slang::ast::CompilationFlags::DisableInstanceCaching,
false);
205 if (!driver.processOptions()) {
209 driver.diagEngine.setIgnoreAllWarnings(
false);
211 if (!driver.parseAllSources()) {
217 compilation = driver.createCompilation();
218 if (failed(compilation))
221 if (skipMainBufferSlangImport) {
225 llvm::SmallString<256> slangCanonPath;
226 bool mainBufferIdSet =
false;
230 auto *sourceManager = (**compilation).getSourceManager();
231 for (
auto slangBuffer : sourceManager->getAllBuffers()) {
232 std::string_view slangRawPath =
233 sourceManager->getRawFileName(slangBuffer);
234 if (std::error_code ec =
235 llvm::sys::fs::real_path(slangRawPath, slangCanonPath))
238 if (slangCanonPath == canonPath) {
239 mainBufferId = slangBuffer;
240 mainBufferIdSet =
true;
245 if (!mainBufferIdSet)
247 Twine(
"Failed to set main buffer id after compilation! "));
250 for (
auto &diag : (*compilation)->getAllDiagnostics())
251 driver.diagEngine.issue(diag);
253 computeLineOffsets(driver.sourceManager.getSourceText(mainBufferId));
255 index = std::make_unique<VerilogIndex>(mainBufferId, driver.sourceManager);
258 index->initialize(**compilation);
263 if (loc && loc.buffer() != slang::SourceLocation::NoLocation.buffer()) {
265 auto line = slangSourceManager.getLineNumber(loc) - 1;
266 auto column = slangSourceManager.getColumnNumber(loc) - 1;
267 auto it = loc.buffer();
269 return llvm::lsp::Location(
uri, llvm::lsp::Range(Position(line, column)));
271 llvm::StringRef fileName = slangSourceManager.getFileName(loc);
273 llvm::SmallString<256> abs(fileName);
274 if (!llvm::sys::path::is_absolute(abs)) {
276 if (std::error_code ec = llvm::sys::fs::real_path(fileName, abs)) {
278 llvm::sys::fs::current_path(abs);
279 llvm::sys::path::append(abs, fileName);
283 if (
auto uriOrErr = llvm::lsp::URIForFile::fromFile(abs)) {
284 if (
auto e = uriOrErr.takeError())
285 return llvm::lsp::Location();
286 return llvm::lsp::Location(*uriOrErr,
287 llvm::lsp::Range(Position(line, column)));
289 return llvm::lsp::Location();
291 return llvm::lsp::Location();
300 if (start.uri != end.uri)
301 return llvm::lsp::Location();
303 return llvm::lsp::Location(
304 start.uri, llvm::lsp::Range(start.range.start, end.range.end));
307std::optional<std::pair<slang::BufferID, SmallString<128>>>
312 return fileInfo->second;
314 auto getIfExist = [&](StringRef path)
315 -> std::optional<std::pair<slang::BufferID, SmallString<128>>> {
316 if (llvm::sys::fs::exists(path)) {
317 auto memoryBuffer = llvm::MemoryBuffer::getFile(path);
322 auto newSlangBuffer =
driver.sourceManager.assignText(
323 path.str(), memoryBuffer.get()->getBufferStart());
324 driver.sourceLoader.addBuffer(newSlangBuffer);
327 .insert(std::make_pair(
328 filePath, std::make_pair(newSlangBuffer.id, path)))
331 return fileInfo->second;
336 if (llvm::sys::path::is_absolute(filePath))
337 return getIfExist(filePath);
341 SmallString<128> lib(libRoot);
342 llvm::sys::path::append(lib, filePath);
343 if (
auto fileInfo = getIfExist(lib))
350static llvm::lsp::Range
getRange(
const mlir::FileLineColRange &fileLoc) {
351 return llvm::lsp::Range(
352 llvm::lsp::Position(fileLoc.getStartLine(), fileLoc.getStartColumn()),
353 llvm::lsp::Position(fileLoc.getEndLine(), fileLoc.getEndColumn()));
361 for (
size_t i = 0; i < text.size(); ++i) {
362 if (text[i] ==
'\n') {
363 lineOffsets.push_back(
static_cast<uint32_t
>(i + 1));
369std::optional<uint32_t>
381 size_t lineEnd = ((unsigned)(pos.line + 1) <
lineOffsets.size())
385 const llvm::UTF8 *src =
386 reinterpret_cast<const llvm::UTF8 *
>(text.data() + lineStart);
387 const llvm::UTF8 *srcEnd =
388 reinterpret_cast<const llvm::UTF8 *
>(text.data() + lineEnd);
391 const uint32_t target = pos.character;
393 return static_cast<uint32_t
>(
394 src -
reinterpret_cast<const llvm::UTF8 *
>(text.data()));
396 std::vector<llvm::UTF16> sink(target);
397 llvm::UTF16 *out = sink.data();
398 llvm::UTF16 *outEnd = out + sink.size();
400 (void)llvm::ConvertUTF8toUTF16(&src, srcEnd, &out, outEnd,
401 llvm::lenientConversion);
403 return static_cast<uint32_t
>(
reinterpret_cast<const char *
>(src) -
411 if (!slangBufferOffset.has_value())
414 uint32_t offset = slangBufferOffset.value();
419 const llvm::lsp::URIForFile &uri,
const llvm::lsp::Position &defPos,
420 std::vector<llvm::lsp::Location> &locations) {
427 const auto &intervalMap =
index->getIntervalMap();
428 auto it = intervalMap.find(slangBufferPointer);
431 if (!it.valid() || slangBufferPointer < it.start())
434 auto element = it.value();
435 if (
auto attr = dyn_cast<Attribute>(element)) {
438 if (
auto fileLoc = dyn_cast<mlir::FileLineColRange>(attr)) {
441 auto fileInfo =
getOrOpenFile(fileLoc.getFilename().getValue());
444 const auto &[bufferId, filePath] = *fileInfo;
445 auto uri = llvm::lsp::URIForFile::fromFile(filePath);
446 if (
auto e =
uri.takeError()) {
450 locations.emplace_back(
uri.get(),
getRange(fileLoc));
457 const auto *symbol = cast<const slang::ast::Symbol *>(element);
459 slang::SourceRange range(symbol->location,
461 (symbol->name.size() ? symbol->name.size() : 1));
466 const llvm::lsp::URIForFile &uri,
const llvm::lsp::Position &pos,
467 std::vector<llvm::lsp::Location> &references) {
473 const auto &intervalMap =
index->getIntervalMap();
474 auto intervalIt = intervalMap.find(slangBufferPointer);
476 if (!intervalIt.valid() || slangBufferPointer < intervalIt.start())
479 const auto *symbol = dyn_cast<const slang::ast::Symbol *>(intervalIt.value());
483 auto it =
index->getReferences().find(symbol);
484 if (it ==
index->getReferences().end())
486 for (
auto referenceRange : it->second)
static llvm::lsp::Range getRange(const mlir::FileLineColRange &fileLoc)
static bool mainBufferFileInCommandFileList(const std::string &cmdfileStr, const std::string &targetAbsStr)
static std::filesystem::path canonicalizeFileName(const std::filesystem::path &file)
std::unique_ptr< circt::lsp::VerilogIndex > index
The index of the parsed module.
slang::BufferID mainBufferId
VerilogDocument(VerilogServerContext &globalContext, const llvm::lsp::URIForFile &uri, llvm::StringRef contents, std::vector< llvm::lsp::Diagnostic > &diagnostics)
const char * getPointerFor(const llvm::lsp::Position &pos)
slang::driver::Driver driver
VerilogServerContext & globalContext
llvm::lsp::URIForFile uri
void getLocationsOf(const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &defPos, std::vector< llvm::lsp::Location > &locations)
void computeLineOffsets(std::string_view text)
Build a vector of line start offsets (0-based).
std::optional< std::pair< slang::BufferID, llvm::SmallString< 128 > > > getOrOpenFile(llvm::StringRef filePath)
void findReferencesOf(const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &pos, std::vector< llvm::lsp::Location > &references)
std::optional< uint32_t > lspPositionToOffset(const llvm::lsp::Position &pos)
const slang::SourceManager & getSlangSourceManager() const
llvm::StringMap< std::pair< slang::BufferID, llvm::SmallString< 128 > > > filePathMap
std::vector< uint32_t > lineOffsets
The precomputed line offsets for faster lookups.
llvm::lsp::Location getLspLocation(slang::SourceLocation loc) const
void error(Twine message)
const circt::lsp::VerilogServerOptions & options
const std::vector< std::string > & libDirs
Additional list of RTL directories to search.
const std::vector< std::string > & extraSourceLocationDirs
Additional list of external source directories to search.