summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-dictionary.h21
-rwxr-xr-xsource/slang/slang-compiler.h3
-rw-r--r--source/slang/slang-diagnostic-defs.h2
-rw-r--r--source/slang/slang-diagnostics.cpp36
-rw-r--r--source/slang/slang-options.cpp11
-rw-r--r--source/slang/slang-preprocessor.cpp20
-rw-r--r--source/slang/slang-serialize-source-loc.cpp5
-rw-r--r--source/slang/slang-source-loc.cpp6
-rw-r--r--source/slang/slang-source-loc.h25
-rw-r--r--source/slang/slang.cpp145
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,