23
23
#include " mlir/IR/MLIRContext.h"
24
24
#include " slang/ast/ASTVisitor.h"
25
25
#include " slang/ast/Compilation.h"
26
+ #include " slang/ast/Scope.h"
27
+ #include " slang/ast/symbols/CompilationUnitSymbols.h"
26
28
#include " slang/diagnostics/DiagnosticClient.h"
27
29
#include " slang/diagnostics/Diagnostics.h"
28
30
#include " slang/driver/Driver.h"
31
+ #include " slang/syntax/AllSyntax.h"
29
32
#include " slang/syntax/SyntaxTree.h"
30
33
#include " slang/text/SourceLocation.h"
31
34
#include " slang/text/SourceManager.h"
42
45
#include " llvm/Support/SMLoc.h"
43
46
#include " llvm/Support/SourceMgr.h"
44
47
48
+ #include < filesystem>
49
+ #include < fstream>
45
50
#include < memory>
46
51
#include < optional>
47
52
#include < string>
@@ -247,13 +252,107 @@ void LSPDiagnosticClient::report(const slang::ReportedDiagnostic &slangDiag) {
247
252
// VerilogDocument
248
253
// ===----------------------------------------------------------------------===//
249
254
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
+
250
335
VerilogDocument::VerilogDocument (
251
336
VerilogServerContext &context, const llvm::lsp::URIForFile &uri,
252
337
StringRef contents, std::vector<llvm::lsp::Diagnostic> &diagnostics)
253
338
: globalContext(context), index(*this ), uri(uri) {
254
339
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
+
255
354
if (auto memBufferOwn =
256
- llvm::MemoryBuffer::getMemBufferCopy (contents, uri. file () )) {
355
+ llvm::MemoryBuffer::getMemBufferCopy (contents, canonPath )) {
257
356
258
357
bufferId = sourceMgr.AddNewSourceBuffer (std::move (memBufferOwn), SMLoc ());
259
358
} else {
@@ -303,6 +402,18 @@ VerilogDocument::VerilogDocument(
303
402
return ;
304
403
}
305
404
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
+
306
417
compilation = driver.createCompilation ();
307
418
if (failed (compilation))
308
419
return ;
@@ -321,19 +432,28 @@ VerilogDocument::getLspLocation(slang::SourceLocation loc) const {
321
432
auto line = slangSourceManager.getLineNumber (loc) - 1 ;
322
433
auto column = slangSourceManager.getColumnNumber (loc) - 1 ;
323
434
auto it = bufferIDMap.find (loc.buffer ().getId ());
324
- // Check if the current buffer is the main file.
325
435
if (it != bufferIDMap.end () && it->second == sourceMgr.getMainFileID ())
326
436
return llvm::lsp::Location (uri, llvm::lsp::Range (Position (line, column)));
327
437
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
+ }
336
449
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
+ }
337
457
return llvm::lsp::Location ();
338
458
}
339
459
@@ -357,8 +477,24 @@ llvm::SMLoc VerilogDocument::getSMLoc(slang::SourceLocation loc) {
357
477
auto bufferIDMapIt = bufferIDMap.find (bufferID);
358
478
if (bufferIDMapIt == bufferIDMap.end ()) {
359
479
// 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);
362
498
if (!memBuffer) {
363
499
circt::lsp::Logger::error (
364
500
" Failed to open file: " + path.filename ().string () +
@@ -523,6 +659,13 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
523
659
return ;
524
660
assert (range.start ().valid () && range.end ().valid () &&
525
661
" 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
+
526
669
index.insertSymbol (symbol, range, isDefinition);
527
670
}
528
671
@@ -557,6 +700,17 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
557
700
visitDefault (expr);
558
701
}
559
702
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
+ }
560
714
void visit (const slang::ast::VariableDeclStatement &expr) {
561
715
insertSymbol (&expr.symbol , expr.sourceRange , /* isDefinition=*/ true );
562
716
visitDefault (expr);
@@ -580,7 +734,13 @@ struct VerilogIndexer : slang::ast::ASTVisitor<VerilogIndexer, true, true> {
580
734
void VerilogIndex::initialize (slang::ast::Compilation &compilation) {
581
735
const auto &root = compilation.getRoot ();
582
736
VerilogIndexer visitor (*this );
737
+
583
738
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
+
584
744
// Visit the body of the instance.
585
745
inst->body .visit (visitor);
586
746
@@ -752,10 +912,17 @@ void circt::lsp::VerilogServer::findReferencesOf(
752
912
void VerilogIndex::insertSymbol (const slang::ast::Symbol *symbol,
753
913
slang::SourceRange from, bool isDefinition) {
754
914
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
+
755
921
const char *startLoc = getDocument ().getSMLoc (from.start ()).getPointer ();
756
922
const char *endLoc = getDocument ().getSMLoc (from.end ()).getPointer () + 1 ;
757
- if (!startLoc || !endLoc)
923
+ if (!startLoc || !endLoc || startLoc >= endLoc )
758
924
return ;
925
+
759
926
assert (startLoc && endLoc);
760
927
761
928
if (startLoc != endLoc && !intervalMap.overlaps (startLoc, endLoc)) {
0 commit comments