diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-dictionary.h | 21 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.cpp | 36 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-preprocessor.cpp | 20 | ||||
| -rw-r--r-- | source/slang/slang-serialize-source-loc.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-source-loc.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-source-loc.h | 25 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 145 |
10 files changed, 256 insertions, 18 deletions
diff --git a/source/core/slang-dictionary.h b/source/core/slang-dictionary.h index 2bd58f1c6..df5ee520d 100644 --- a/source/core/slang-dictionary.h +++ b/source/core/slang-dictionary.h @@ -359,6 +359,27 @@ namespace Slang throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); } + /// This differs from TryGetValueOrAdd, in that it always returns the Value held in the Dictionary. + /// If there isn't already an entry for 'key', a value is added with defaultValue. + TValue& GetOrAddValue(const TKey& key, const TValue& defaultValue) + { + Rehash(); + auto pos = FindPosition(key); + if (pos.ObjectPosition != -1) + { + return hashMap[pos.ObjectPosition].Value; + } + else if (pos.InsertionPosition != -1) + { + // Make pair + KeyValuePair<TKey, TValue> kvPair(_Move(key), _Move(defaultValue)); + _count++; + return _Insert(_Move(kvPair), pos.InsertionPosition); + } + else + throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); + } + bool ContainsKey(const TKey& key) const { if (bucketSizeMinusOne == -1) diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 1710baa6e..649283f35 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1491,6 +1491,9 @@ namespace Slang bool shouldDumpAST = false; + /// If true will after lexical analysis output the hierarchy of includes to stdout + bool outputIncludes = false; + protected: CompileRequestBase( Linkage* linkage, diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 9b4d55c14..9c65250cd 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -43,6 +43,8 @@ DIAGNOSTIC(-1, Note, doYouForgetToMakeComponentAccessible, "do you forget to mak DIAGNOSTIC(-1, Note, seeDeclarationOf, "see declaration of '$0'") DIAGNOSTIC(-1, Note, seeOtherDeclarationOf, "see other declaration of '$0'") DIAGNOSTIC(-1, Note, seePreviousDeclarationOf, "see previous declaration of '$0'") +DIAGNOSTIC(-1, Note, seeTokenPasteLocation, "see token pasted location") +DIAGNOSTIC(-1, Note, includeOutput, "include $0") // // 0xxxx - Command line and interaction with host platform APIs. diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp index c3b7e27a2..04ada6fc4 100644 --- a/source/slang/slang-diagnostics.cpp +++ b/source/slang/slang-diagnostics.cpp @@ -177,6 +177,42 @@ static void formatDiagnostic( humaneLoc = sourceView->getHumaneLoc(sourceLoc); } formatDiagnostic(humaneLoc, diagnostic, sb); + + { + SourceView* currentView = sourceView; + + while (currentView && currentView->getInitiatingSourceLoc().isValid() && currentView->getSourceFile()->getPathInfo().type == PathInfo::Type::TokenPaste) + { + SourceView* initiatingView = sourceManager->findSourceView(currentView->getInitiatingSourceLoc()); + if (initiatingView == nullptr) + { + break; + } + + const DiagnosticInfo& diagnosticInfo = Diagnostics::seeTokenPasteLocation; + + // Turn the message format into a message. For the moment it assumes no parameters. + StringBuilder msg; + formatDiagnosticMessage(msg, diagnosticInfo.messageFormat, 0, nullptr); + + // Set up the diagnostic. + Diagnostic initiationDiagnostic; + initiationDiagnostic.ErrorID = diagnosticInfo.id; + initiationDiagnostic.Message = msg.ProduceString(); + initiationDiagnostic.loc = sourceView->getInitiatingSourceLoc(); + initiationDiagnostic.severity = diagnosticInfo.severity; + + // TODO(JS): + // Not 100% clear what the best sourceLoc type is most useful here - we will go with default for now + HumaneSourceLoc pasteHumaneLoc = initiatingView->getHumaneLoc(sourceView->getInitiatingSourceLoc()); + + // Okay we should output where the token paste took place + formatDiagnostic(pasteHumaneLoc, initiationDiagnostic, sb); + + // Make the initiatingView the current view + currentView = initiatingView; + } + } } if (sourceView && sink->isFlagSet(DiagnosticSink::Flag::VerbosePath)) diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index db56e8115..8897a6549 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -441,7 +441,7 @@ struct OptionsParser char const* arg = *argCursor++; if (arg[0] == '-') { - String argStr = String(arg); + UnownedStringSlice argStr = UnownedStringSlice(arg); if(argStr == "-no-mangle" ) { @@ -461,6 +461,10 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, prefix)); requestImpl->getBackEndReq()->m_dumpIntermediatePrefix = prefix; } + else if (argStr == "-output-includes") + { + requestImpl->getFrontEndReq()->outputIncludes = true; + } else if(argStr == "-dump-ir" ) { requestImpl->getFrontEndReq()->shouldDumpIR = true; @@ -972,9 +976,10 @@ struct OptionsParser String name; SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - String slice = argStr.subString(1, index - 1); + // Skip the initial -, up to the last - + UnownedStringSlice passThruSlice(argStr.begin() + 1, argStr.begin() + index); SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE; - if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(slice.getUnownedSlice(), passThrough))) + if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(passThruSlice, passThrough))) { session->setDownstreamCompilerPath(passThrough, name.getBuffer()); continue; diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp index b5861d986..41b2c5bdf 100644 --- a/source/slang/slang-preprocessor.cpp +++ b/source/slang/slang-preprocessor.cpp @@ -1013,10 +1013,12 @@ top: StringBuilder sb; sb << token.getContent(); + Token poundPoundToken; + while (PeekRawTokenType(preprocessor) == TokenType::PoundPound) { // Consume the `##` - AdvanceRawToken(preprocessor); + poundPoundToken = AdvanceRawToken(preprocessor); // Possibly macro-expand the next token MaybeBeginMacroExpansion(preprocessor); @@ -1035,7 +1037,7 @@ top: PathInfo pathInfo = PathInfo::makeTokenPaste(); SourceFile* sourceFile = sourceManager->createSourceFileWithString(pathInfo, sb.ProduceString()); - SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, poundPoundToken.getLoc()); Lexer lexer; lexer.initialize(sourceView, GetSink(preprocessor), preprocessor->getNamePool(), sourceManager->getMemoryArena()); @@ -1880,8 +1882,8 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) sourceManager->addSourceFile(filePathInfo.uniqueIdentity, sourceFile); } - // This is a new parse (even if it's a pre-existing source file), so create a new SourceUnit - SourceView* sourceView = sourceManager->createSourceView(sourceFile, &filePathInfo); + // This is a new parse (even if it's a pre-existing source file), so create a new SourceView + SourceView* sourceView = sourceManager->createSourceView(sourceFile, &filePathInfo, directiveLoc); PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, sourceView); inputStream->parent = context->preprocessor->inputStream; @@ -2411,8 +2413,10 @@ static void DefineMacro( SourceFile* keyFile = sourceManager->createSourceFileWithString(pathInfo, key); SourceFile* valueFile = sourceManager->createSourceFileWithString(pathInfo, value); - SourceView* keyView = sourceManager->createSourceView(keyFile, nullptr); - SourceView* valueView = sourceManager->createSourceView(valueFile, nullptr); + // Note that we don't need to pass a special source loc to identify that these are defined on the command line + // because the PathInfo on the SourceFile, is marked 'command line'. + SourceView* keyView = sourceManager->createSourceView(keyFile, nullptr, SourceLoc::fromRaw(0)); + SourceView* valueView = sourceManager->createSourceView(valueFile, nullptr, SourceLoc::fromRaw(0)); // Use existing `Lexer` to generate a token stream. Lexer lexer; @@ -2546,7 +2550,9 @@ TokenList preprocessSource( } } - SourceView* sourceView = sourceManager->createSourceView(file, nullptr); + // This is the originating source we are compiling - there is no 'initiating' source loc, + // so pass SourceLoc(0) - meaning it has no initiating location. + SourceView* sourceView = sourceManager->createSourceView(file, nullptr, SourceLoc::fromRaw(0)); // create an initial input stream based on the provided buffer preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, sourceView); diff --git a/source/slang/slang-serialize-source-loc.cpp b/source/slang/slang-serialize-source-loc.cpp index 1de3f9a27..7e9cca106 100644 --- a/source/slang/slang-serialize-source-loc.cpp +++ b/source/slang/slang-serialize-source-loc.cpp @@ -247,7 +247,10 @@ SlangResult SerialSourceLocReader::read(const SerialSourceLocData* serialData, S pathInfo.foundPath = debugStringSlices[UInt(srcSourceInfo.m_pathIndex)]; SourceFile* sourceFile = sourceManager->createSourceFileWithSize(pathInfo, srcSourceInfo.m_range.getCount()); - SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr); + + // Here the initiatingSourecLoc is passed as 0, as that information is not currently saved + // This simplifies the serialization - as currently for this data we save only a single view per file. + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc::fromRaw(0)); // We need to accumulate all line numbers, for this source file, both adjusted and unadjusted List<SerialSourceLocData::LineInfo> lineInfos; diff --git a/source/slang/slang-source-loc.cpp b/source/slang/slang-source-loc.cpp index 7eca23d8d..f6b0afcc1 100644 --- a/source/slang/slang-source-loc.cpp +++ b/source/slang/slang-source-loc.cpp @@ -405,7 +405,7 @@ SourceFile* SourceManager::createSourceFileWithBlob(const PathInfo& pathInfo, IS return sourceFile; } -SourceView* SourceManager::createSourceView(SourceFile* sourceFile, const PathInfo* pathInfo) +SourceView* SourceManager::createSourceView(SourceFile* sourceFile, const PathInfo* pathInfo, SourceLoc initiatingSourceLoc) { SourceRange range = allocateSourceRange(sourceFile->getContentSize()); @@ -413,11 +413,11 @@ SourceView* SourceManager::createSourceView(SourceFile* sourceFile, const PathIn if (pathInfo && (pathInfo->foundPath.getLength() && sourceFile->getPathInfo().foundPath != pathInfo->foundPath)) { - sourceView = new SourceView(sourceFile, range, &pathInfo->foundPath); + sourceView = new SourceView(sourceFile, range, &pathInfo->foundPath, initiatingSourceLoc); } else { - sourceView = new SourceView(sourceFile, range, nullptr); + sourceView = new SourceView(sourceFile, range, nullptr, initiatingSourceLoc); } m_sourceViews.add(sourceView); diff --git a/source/slang/slang-source-loc.h b/source/slang/slang-source-loc.h index c00120476..1e91a9d62 100644 --- a/source/slang/slang-source-loc.h +++ b/source/slang/slang-source-loc.h @@ -95,6 +95,9 @@ public: : raw(loc.raw) {} + SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return raw == rhs.raw; } + SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(raw == rhs.raw); } + RawValue getRaw() const { return raw; } void setRaw(RawValue value) { raw = value; } @@ -286,10 +289,18 @@ class SourceView /// Get the path associated with a location PathInfo getPathInfo(SourceLoc loc, SourceLocType type = SourceLocType::Nominal); + /// Get the initiating source location - that is the source location that caused the this SourceView to be created + /// Can be SourceLoc(0) if there is no initiating location. + /// For example for a #include - the view's initiating source loc for the view that is the contents of the view + /// will be the location of the #include in the source. + /// For the original source file (ie not an include) - the view will have an initiating source loc of SourceLoc(0) + SourceLoc getInitiatingSourceLoc() const { return m_initiatingSourceLoc; } + /// Ctor - SourceView(SourceFile* sourceFile, SourceRange range, const String* viewPath): + SourceView(SourceFile* sourceFile, SourceRange range, const String* viewPath, SourceLoc initiatingSourceLoc): m_range(range), - m_sourceFile(sourceFile) + m_sourceFile(sourceFile), + m_initiatingSourceLoc(initiatingSourceLoc) { if (viewPath) { @@ -304,7 +315,9 @@ class SourceView /// overridden by m_viewPath PathInfo _getPathInfo() const; - String m_viewPath; ///< Path to this view. If empty the path is the path to the SourceView + String m_viewPath; ///< Path to this view. If empty the path is the path to the SourceView + + SourceLoc m_initiatingSourceLoc; ///< An optional source loc that defines where this view was initiated from. SourceLoc(0) if not defined. SourceRange m_range; ///< The range that this SourceView applies to SourceFile* m_sourceFile; ///< The source file. Can hold the line breaks @@ -333,7 +346,8 @@ struct SourceManager /// Create a new source view from a file /// @param sourceFile is the source file that contains the source /// @param pathInfo is path used to read the file from - SourceView* createSourceView(SourceFile* sourceFile, const PathInfo* pathInfo); + /// @param initiatingSourceLoc the (optional) location in the source that led the the creation of this view. If there isn't an initiating source location pass SourceLoc(0)s + SourceView* createSourceView(SourceFile* sourceFile, const PathInfo* pathInfo, SourceLoc initiatingSourceLoc); /// Find a view by a source file location. /// If not found in this manager will look in the parent SourceManager @@ -382,6 +396,9 @@ struct SourceManager /// Get all of the source files const List<SourceFile*>& getSourceFiles() const { return m_sourceFiles; } + /// Get the source views + const List<SourceView*>& getSourceViews() const { return m_sourceViews; } + SourceManager() : m_memoryArena(2048), m_slicePool(StringSlicePool::Style::Default) diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index a66441948..ea17a0cf7 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1185,6 +1185,146 @@ protected: }; +// Holds the hierarchy of views, the children being views that were 'initiated' (have an initiating SourceLoc) in the parent. +typedef Dictionary<SourceView*, List<SourceView*>> ViewInitiatingHierarchy; + +// Calculate the hierarchy from the sourceManager +static void _calcViewInitiatingHierarchy(SourceManager* sourceManager, ViewInitiatingHierarchy& outHierarchy) +{ + const List<SourceView*> emptyList; + outHierarchy.Clear(); + + // Iterate over all managers + for (SourceManager* curManager = sourceManager; curManager; curManager = curManager->getParent()) + { + // Iterate over all views + for (SourceView* view : curManager->getSourceViews()) + { + if (view->getInitiatingSourceLoc().isValid()) + { + // Look up the view it came from + SourceView* parentView = sourceManager->findSourceViewRecursively(view->getInitiatingSourceLoc()); + if (parentView) + { + List<SourceView*>& children = outHierarchy.GetOrAddValue(parentView, emptyList); + // It shouldn't have already been added + SLANG_ASSERT(children.indexOf(view) < 0); + children.add(view); + } + } + } + } + + // Order all the children, by their raw SourceLocs. This is desirable, so that a trivial traversal + // will traverse children in the order they are initiated in the parent source. + // This assumes they increase in SourceLoc implies an later within a source file - this is true currently. + for (auto& pair : outHierarchy) + { + pair.Value.sort([](SourceView* a, SourceView* b) { return a->getInitiatingSourceLoc().getRaw() < b->getInitiatingSourceLoc().getRaw(); }); + } +} + +// Given a source file, find the view that is the initial SourceView use of the source. It must have +// an initiating SourceLoc that is not valid. +static SourceView* _findInitialSourceView(SourceFile* sourceFile) +{ + // TODO(JS): + // This might be overkill - presumably the SourceView would belong to the same manager as it's SourceFile? + // That is not enforced by the SourceManager in any way though so we just search all managers, and all views. + for (SourceManager* sourceManager = sourceFile->getSourceManager(); sourceManager; sourceManager = sourceManager->getParent()) + { + for (SourceView* view : sourceManager->getSourceViews()) + { + if (view->getSourceFile() == sourceFile && !view->getInitiatingSourceLoc().isValid()) + { + return view; + } + } + } + + return nullptr; +} + +static void _outputInclude(SourceFile* sourceFile, Index depth, DiagnosticSink* sink) +{ + StringBuilder buf; + + for (Index i = 0; i < depth; ++i) + { + buf << " "; + } + + // Output the found path for now + // TODO(JS). We could use the verbose paths flag to control what path is output -> as it may be useful to output the full path + // for example + + const PathInfo& pathInfo = sourceFile->getPathInfo(); + buf << "'" << pathInfo.foundPath << "'"; + + // TODO(JS)? + // You might want to know where this include was from. + // If I output this though there will be a problem... as the indenting won't be clearly shown. + // Perhaps I output in two sections, one the hierarchy and the other the locations of the includes? + + sink->diagnose(SourceLoc(), Diagnostics::includeOutput, buf); +} + +static void _outputIncludesRec(SourceView* sourceView, Index depth, ViewInitiatingHierarchy& hierarchy, DiagnosticSink* sink) +{ + SourceFile* sourceFile = sourceView->getSourceFile(); + const PathInfo& pathInfo = sourceFile->getPathInfo(); + + switch (pathInfo.type) + { + case PathInfo::Type::TokenPaste: + case PathInfo::Type::CommandLine: + case PathInfo::Type::TypeParse: + { + // If any of these types we don't output + return; + } + default: break; + } + + // Okay output this file at the current depth + _outputInclude(sourceFile, depth, sink); + + // Now recurse to all of the children at the next depth + List<SourceView*>* children = hierarchy.TryGetValue(sourceView); + if (children) + { + for (SourceView* child : *children) + { + _outputIncludesRec(child, depth + 1, hierarchy, sink); + } + } +} + +static void _outputIncludes(const List<SourceFile*>& sourceFiles, SourceManager* sourceManager, DiagnosticSink* sink) +{ + // Set up the hierarchy to know how all the source views relate. This could be argued as overkill, but makes recursive + // output pretty simple + ViewInitiatingHierarchy hierarchy; + _calcViewInitiatingHierarchy(sourceManager, hierarchy); + + // For all the source files + for (SourceFile* sourceFile : sourceFiles) + { + // Find an initial view (this is the view of this file, that doesn't have an initiating loc) + SourceView* sourceView = _findInitialSourceView(sourceFile); + if (!sourceView) + { + // Okay, didn't find one, so just output the file + _outputInclude(sourceFile, 0, sink); + } + else + { + // Output from this view recursively + _outputIncludesRec(sourceView, 0, hierarchy, sink); + } + } +} + void FrontEndCompileRequest::parseTranslationUnit( TranslationUnitRequest* translationUnit) { @@ -1266,6 +1406,11 @@ void FrontEndCompileRequest::parseTranslationUnit( getLinkage(), &preprocessorHandler); + if (outputIncludes) + { + _outputIncludes(translationUnit->getSourceFiles(), getSink()->getSourceManager(), getSink()); + } + parseSourceFile( astBuilder, translationUnit, |
