2323#include " mlir/IR/MLIRContext.h"
2424#include " slang/ast/ASTVisitor.h"
2525#include " slang/ast/Compilation.h"
26+ #include " slang/ast/Scope.h"
27+ #include " slang/ast/symbols/CompilationUnitSymbols.h"
2628#include " slang/diagnostics/DiagnosticClient.h"
2729#include " slang/diagnostics/Diagnostics.h"
2830#include " slang/driver/Driver.h"
31+ #include " slang/syntax/AllSyntax.h"
2932#include " slang/syntax/SyntaxTree.h"
3033#include " slang/text/SourceLocation.h"
3134#include " slang/text/SourceManager.h"
4245#include " llvm/Support/SMLoc.h"
4346#include " llvm/Support/SourceMgr.h"
4447
48+ #include < filesystem>
49+ #include < fstream>
4550#include < memory>
4651#include < optional>
4752#include < string>
@@ -247,13 +252,107 @@ void LSPDiagnosticClient::report(const slang::ReportedDiagnostic &slangDiag) {
247252// VerilogDocument
248253// ===----------------------------------------------------------------------===//
249254
255+ // Filter out the main buffer file from the command file list, if it is in
256+ // there.
257+ static inline std::string removeFileToTemp (const std::string &cmdfileStr,
258+ const std::string &targetAbsStr) {
259+ namespace fs = std::filesystem;
260+
261+ fs::path cmdfile (cmdfileStr);
262+ fs::path targetAbs (targetAbsStr);
263+ const fs::path base = cmdfile.parent_path ();
264+ std::error_code ec;
265+
266+ // Normalize target
267+ fs::path target = fs::weakly_canonical (targetAbs, ec);
268+ if (ec) {
269+ ec.clear ();
270+ target = targetAbs.lexically_normal ();
271+ }
272+
273+ std::ifstream in (cmdfile);
274+ if (!in) {
275+ // return the original path so the caller still has a valid file to use
276+ return cmdfileStr;
277+ }
278+
279+ std::string line, outbuf;
280+ bool removed = false ;
281+
282+ while (std::getline (in, line)) {
283+ std::string s = line;
284+ s.erase (0 , s.find_first_not_of (" \t\r\n " )); // left-trim
285+
286+ // Keep plusargs
287+ if (!s.empty () &&
288+ (s.rfind (" +incdir+" , 0 ) == 0 || s.rfind (" +define+" , 0 ) == 0 ||
289+ s.rfind (" -I" , 0 ) == 0 || s.rfind (" -D" , 0 ) == 0 )) {
290+ outbuf += line;
291+ outbuf.push_back (' \n ' );
292+ continue ;
293+ }
294+ // Preserve blank lines
295+ if (s.empty ()) {
296+ outbuf.push_back (' \n ' );
297+ continue ;
298+ }
299+
300+ // Treat everything else as a path (relative entries resolved vs. cmdfile
301+ // dir)
302+ fs::path candRel = s;
303+ fs::path candAbs = candRel.is_absolute () ? candRel : (base / candRel);
304+
305+ fs::path cand = fs::weakly_canonical (candAbs, ec);
306+ if (ec) {
307+ ec.clear ();
308+ cand = candAbs.lexically_normal ();
309+ }
310+
311+ if (cand == target) {
312+ removed = true ; // skip writing this line
313+ } else {
314+ outbuf += line;
315+ outbuf.push_back (' \n ' );
316+ }
317+ }
318+
319+ if (!removed) {
320+ // Nothing changed: return the original commandfile path
321+ return cmdfileStr;
322+ }
323+ // Write updated content to a temp file next to the original
324+ fs::path tmp = base / (cmdfile.filename ().string () + " .tmp" );
325+ std::ofstream out (tmp, std::ios::trunc);
326+ out << outbuf;
327+ return tmp.string ();
328+ }
329+
330+ static inline std::string removeFileToTemp (llvm::StringRef cmdfile,
331+ llvm::StringRef target_abs) {
332+ return removeFileToTemp (cmdfile.str (), target_abs.str ());
333+ }
334+
250335VerilogDocument::VerilogDocument (
251336 VerilogServerContext &context, const llvm::lsp::URIForFile &uri,
252337 StringRef contents, std::vector<llvm::lsp::Diagnostic> &diagnostics)
253338 : globalContext(context), index(*this ), uri(uri) {
254339 unsigned int bufferId;
340+
341+ llvm::SmallString<256 > canonPath (uri.file ());
342+ if (std::error_code ec = llvm::sys::fs::real_path (uri.file (), canonPath))
343+ canonPath = uri.file (); // fall back, but try to keep it absolute
344+
345+ // --- Apply project command files (the “-C”s) to this per-buffer driver ---
346+ for (const std::string &cmdFile : context.options .commandFiles ) {
347+ if (!driver.processCommandFiles (removeFileToTemp (cmdFile, canonPath), false ,
348+ true )) {
349+ circt::lsp::Logger::error (Twine (" Failed to open command file " ) +
350+ cmdFile);
351+ }
352+ }
353+
255354 if (auto memBufferOwn =
256- llvm::MemoryBuffer::getMemBufferCopy (contents, uri. file () )) {
355+ llvm::MemoryBuffer::getMemBufferCopy (contents, canonPath )) {
257356
258357 bufferId = sourceMgr.AddNewSourceBuffer (std::move (memBufferOwn), SMLoc ());
259358 } else {
@@ -303,6 +402,18 @@ VerilogDocument::VerilogDocument(
303402 return ;
304403 }
305404
405+ compilation = driver.createCompilation ();
406+ if (failed (compilation))
407+ return ;
408+
409+ std::vector<std::string> topModules;
410+ for (const auto *defs : (*compilation)->getDefinitions ())
411+ topModules.emplace_back (defs->name );
412+
413+ // Make sure that all possible definitions in the file / project scope are
414+ // topModules!
415+ driver.options .topModules = topModules;
416+
306417 compilation = driver.createCompilation ();
307418 if (failed (compilation))
308419 return ;
@@ -321,19 +432,28 @@ VerilogDocument::getLspLocation(slang::SourceLocation loc) const {
321432 auto line = slangSourceManager.getLineNumber (loc) - 1 ;
322433 auto column = slangSourceManager.getColumnNumber (loc) - 1 ;
323434 auto it = bufferIDMap.find (loc.buffer ().getId ());
324- // Check if the current buffer is the main file.
325435 if (it != bufferIDMap.end () && it->second == sourceMgr.getMainFileID ())
326436 return llvm::lsp::Location (uri, llvm::lsp::Range (Position (line, column)));
327437
328- // Otherwise, construct URI from slang source manager.
329- auto fileName = slangSourceManager.getFileName (loc);
330- auto loc = llvm::lsp::URIForFile::fromFile (fileName);
331- if (auto e = loc.takeError ())
332- return llvm::lsp::Location ();
333- return llvm::lsp::Location (loc.get (),
334- llvm::lsp::Range (Position (line, column)));
335- }
438+ llvm::StringRef fileName = slangSourceManager.getFileName (loc);
439+ // Ensure absolute path for LSP:
440+ llvm::SmallString<256 > abs (fileName);
441+ if (!llvm::sys::path::is_absolute (abs)) {
442+ // Try realPath first
443+ if (std::error_code ec = llvm::sys::fs::real_path (fileName, abs)) {
444+ // Fallback: make it absolute relative to the process CWD
445+ llvm::sys::fs::current_path (abs); // abs = CWD
446+ llvm::sys::path::append (abs, fileName);
447+ }
448+ }
336449
450+ if (auto uriOrErr = llvm::lsp::URIForFile::fromFile (abs)) {
451+ return llvm::lsp::Location (*uriOrErr,
452+ llvm::lsp::Range (Position (line, column)));
453+ }
454+ // If we can't form a URI, return empty (avoids crashing)
455+ return llvm::lsp::Location ();
456+ }
337457 return llvm::lsp::Location ();
338458}
339459
@@ -357,8 +477,24 @@ llvm::SMLoc VerilogDocument::getSMLoc(slang::SourceLocation loc) {
357477 auto bufferIDMapIt = bufferIDMap.find (bufferID);
358478 if (bufferIDMapIt == bufferIDMap.end ()) {
359479 // If not, open the source file and add it to the LLVM source manager.
360- auto path = getSlangSourceManager ().getFullPath (loc.buffer ());
361- auto memBuffer = llvm::MemoryBuffer::getFile (path.string ());
480+ const slang::SourceManager &ssm = getSlangSourceManager ();
481+ auto path = ssm.getFullPath (loc.buffer ());
482+
483+ std::string name;
484+ if (path.empty ()) {
485+ // Get filename from slang source manager - might've been loaded already!
486+ llvm::StringRef fname = ssm.getFileName (loc);
487+ llvm::SmallString<256 > realPath;
488+ if (!llvm::sys::fs::real_path (fname, realPath)) {
489+ name = realPath.str ().str ();
490+ } else {
491+ name = fname.str ();
492+ }
493+ } else {
494+ name = path.string ();
495+ }
496+
497+ auto memBuffer = llvm::MemoryBuffer::getFile (name);
362498 if (!memBuffer) {
363499 circt::lsp::Logger::error (
364500 " Failed to open file: " + path.filename ().string () +
@@ -523,6 +659,13 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
523659 return ;
524660 assert (range.start ().valid () && range.end ().valid () &&
525661 " range must be valid" );
662+
663+ // TODO: This implementation does not handle expanded MACROs. Return
664+ // instead.
665+ if (range.start () >= range.end ()) {
666+ return ;
667+ }
668+
526669 index.insertSymbol (symbol, range, isDefinition);
527670 }
528671
@@ -557,6 +700,17 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
557700 visitDefault (expr);
558701 }
559702
703+ void visit (const slang::ast::InstanceSymbol &expr) {
704+ auto *def = &expr.getDefinition ();
705+ if (!def)
706+ return ;
707+
708+ // Add the module definition
709+ insertSymbol (def, def->location , /* isDefinition=*/ true );
710+ // Link the module name back to the module definition
711+ insertSymbol (def, expr.location , /* isDefinition=*/ false );
712+ visitDefault (expr);
713+ }
560714 void visit (const slang::ast::VariableDeclStatement &expr) {
561715 insertSymbol (&expr.symbol , expr.sourceRange , /* isDefinition=*/ true );
562716 visitDefault (expr);
@@ -580,7 +734,13 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
580734void VerilogIndex::initialize (slang::ast::Compilation &compilation) {
581735 const auto &root = compilation.getRoot ();
582736 VerilogIndexer visitor (*this );
737+
583738 for (auto *inst : root.topInstances ) {
739+
740+ // Skip all modules, interfaces, etc. that are not defined in this files
741+ if (!(document.getLspLocation (inst->location ).uri == document.getURI ()))
742+ continue ;
743+
584744 // Visit the body of the instance.
585745 inst->body .visit (visitor);
586746
@@ -752,10 +912,17 @@ void circt::lsp::VerilogServer::findReferencesOf(
752912void VerilogIndex::insertSymbol (const slang::ast::Symbol *symbol,
753913 slang::SourceRange from, bool isDefinition) {
754914 assert (from.start ().valid () && from.end ().valid ());
915+
916+ // TODO: Currently doesn't handle expanded macros
917+ if (!from.start ().valid () || !from.end ().valid () ||
918+ from.start () >= from.end ())
919+ return ;
920+
755921 const char *startLoc = getDocument ().getSMLoc (from.start ()).getPointer ();
756922 const char *endLoc = getDocument ().getSMLoc (from.end ()).getPointer () + 1 ;
757- if (!startLoc || !endLoc)
923+ if (!startLoc || !endLoc || startLoc >= endLoc )
758924 return ;
925+
759926 assert (startLoc && endLoc);
760927
761928 if (startLoc != endLoc && !intervalMap.overlaps (startLoc, endLoc)) {
0 commit comments