summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorRonan <ro.cailleau@gmail.com>2025-04-25 00:48:37 +0200
committerGitHub <noreply@github.com>2025-04-24 15:48:37 -0700
commit5f632cd204b7a85f3a97b6c316c5a34f9fc8193e (patch)
tree7dc74fbe8c80870a4c485bbaa5765ff379914779 /source
parentc7ecf3039d2cc8680f1ea5f4bee2d13521ae34f7 (diff)
Implemented #pragma warning (#6748)
* Implemented #pragma warning Based on https://learn.microsoft.com/en-us/cpp/preprocessor/warning?view=msvc-170 * Make #pragma warning work with #includes. - SourceLoc are not sorted by inclusion order. - Construct a mapping from SourceLoc to "absolute locations" that are sorted by inclusion order (roughly represents a location in a raw file with all #include resolved). - The absolute location can be used in the pragma warning timeline * Added preprocessor #pragma warning tests. - Fixed #pragma warning (push / pop) SourceLoc - Fixed unused directiveLoc in #pragma warning parsing * #pragma warning: Added some comments and fixed some typos * Cleaned #pragma warning preprocessor implementation. --------- Co-authored-by: Yong He <yonghe@outlook.com>
Diffstat (limited to 'source')
-rw-r--r--source/compiler-core/slang-diagnostic-sink.cpp14
-rw-r--r--source/compiler-core/slang-diagnostic-sink.h18
-rw-r--r--source/compiler-core/slang-lexer.cpp37
-rw-r--r--source/compiler-core/slang-lexer.h2
-rw-r--r--source/compiler-core/slang-source-loc.cpp40
-rw-r--r--source/compiler-core/slang-source-loc.h55
-rw-r--r--source/slang/slang-diagnostic-defs.h28
-rw-r--r--source/slang/slang-preprocessor.cpp519
8 files changed, 702 insertions, 11 deletions
diff --git a/source/compiler-core/slang-diagnostic-sink.cpp b/source/compiler-core/slang-diagnostic-sink.cpp
index f9a7d9c88..28a98266a 100644
--- a/source/compiler-core/slang-diagnostic-sink.cpp
+++ b/source/compiler-core/slang-diagnostic-sink.cpp
@@ -612,10 +612,20 @@ bool DiagnosticSink::diagnoseImpl(
return true;
}
-Severity DiagnosticSink::getEffectiveMessageSeverity(DiagnosticInfo const& info)
+Severity DiagnosticSink::getEffectiveMessageSeverity(
+ DiagnosticInfo const& info,
+ SourceLoc const& location)
{
Severity effectiveSeverity = info.severity;
+ if (effectiveSeverity <= Severity::Warning && m_sourceWarningStateTracker)
+ {
+ effectiveSeverity = m_sourceWarningStateTracker->consumeWarningSeverity(
+ location,
+ info.id,
+ effectiveSeverity);
+ }
+
Severity* pSeverityOverride = m_severityOverrides.tryGetValue(info.id);
// See if there is an override
@@ -639,7 +649,7 @@ bool DiagnosticSink::diagnoseImpl(
DiagnosticArg const* args)
{
// Override the severity in the 'info' structure to pass it further into formatDiagnostics
- info.severity = getEffectiveMessageSeverity(info);
+ info.severity = getEffectiveMessageSeverity(info, pos);
if (info.severity == Severity::Disable)
return false;
diff --git a/source/compiler-core/slang-diagnostic-sink.h b/source/compiler-core/slang-diagnostic-sink.h
index 90f1bfccf..2d60747d9 100644
--- a/source/compiler-core/slang-diagnostic-sink.h
+++ b/source/compiler-core/slang-diagnostic-sink.h
@@ -81,6 +81,11 @@ public:
}
};
+struct SourceWarningStateTrackerBase : public RefObject
+{
+ virtual Severity consumeWarningSeverity(SourceLoc loc, int id, Severity severity) = 0;
+};
+
class Name;
void printDiagnosticArg(StringBuilder& sb, char const* str);
@@ -249,6 +254,15 @@ public:
void setParentSink(DiagnosticSink* parentSink) { m_parentSink = parentSink; }
DiagnosticSink* getParentSink() const { return m_parentSink; }
+ void setSourceWarningStateTracker(SourceWarningStateTrackerBase* ptr)
+ {
+ m_sourceWarningStateTracker = ptr;
+ }
+ RefPtr<SourceWarningStateTrackerBase> getSourceWarningStateTracker() const
+ {
+ return m_sourceWarningStateTracker;
+ }
+
/// Reset state.
/// Resets error counts. Resets the output buffer.
void reset();
@@ -283,7 +297,7 @@ protected:
DiagnosticArg const* args);
bool diagnoseImpl(DiagnosticInfo const& info, const UnownedStringSlice& formattedMessage);
- Severity getEffectiveMessageSeverity(DiagnosticInfo const& info);
+ Severity getEffectiveMessageSeverity(DiagnosticInfo const& info, SourceLoc const& location);
/// If set all diagnostics (as formatted by *this* sink, will be routed to the parent).
DiagnosticSink* m_parentSink = nullptr;
@@ -304,6 +318,8 @@ protected:
// Configuration that allows the user to control the severity of certain diagnostic messages
Dictionary<int, Severity> m_severityOverrides;
+
+ RefPtr<SourceWarningStateTrackerBase> m_sourceWarningStateTracker = nullptr;
};
/// An `ISlangWriter` that writes directly to a diagnostic sink.
diff --git a/source/compiler-core/slang-lexer.cpp b/source/compiler-core/slang-lexer.cpp
index 048c266ca..c2ee5cd69 100644
--- a/source/compiler-core/slang-lexer.cpp
+++ b/source/compiler-core/slang-lexer.cpp
@@ -374,9 +374,14 @@ static void _lexIdentifier(Lexer* lexer)
}
}
-static SourceLoc _getSourceLoc(Lexer* lexer)
+static SourceLoc _getSourceLoc(const Lexer& lexer, const char* it)
{
- return lexer->m_startLoc + (lexer->m_cursor - lexer->m_begin);
+ return lexer.m_startLoc + (it - lexer.m_begin);
+}
+
+static SourceLoc _getSourceLoc(const Lexer* lexer)
+{
+ return _getSourceLoc(*lexer, lexer->m_cursor);
}
static void _lexDigits(Lexer* lexer, int base)
@@ -1857,4 +1862,32 @@ TokenList Lexer::lexAllTokens()
return UnownedStringSlice(in.begin() + offset, in.begin() + offset + tok.charsCount);
}
+SourceLoc Lexer::findNextLineEnd(SourceLoc from, UInt& lineCount) const
+{
+ const char* it = m_begin + (from.getRaw() - m_startLoc.getRaw());
+ if (it >= m_begin && it < m_end)
+ {
+ while (it != m_end)
+ {
+ const char c = *it;
+ if (c == '\n' || c == '\r')
+ {
+ const char next = ((it + 1) == m_end) ? char(kEOF) : *(it + 1);
+ if ((next ^ c) == ('\n' ^ '\r'))
+ {
+ ++it;
+ }
+ --lineCount;
+ if (lineCount == 0)
+ {
+ SourceLoc res = _getSourceLoc(*this, it);
+ return res;
+ }
+ }
+ ++it;
+ }
+ }
+ return {};
+}
+
} // namespace Slang
diff --git a/source/compiler-core/slang-lexer.h b/source/compiler-core/slang-lexer.h
index 0152ff6f5..c39d130b7 100644
--- a/source/compiler-core/slang-lexer.h
+++ b/source/compiler-core/slang-lexer.h
@@ -149,6 +149,8 @@ struct Lexer
return ((m_lexerFlags & kLexerFlag_SuppressDiagnostics) == 0) ? m_sink : nullptr;
}
+ SourceLoc findNextLineEnd(SourceLoc from, UInt& lineCount) const;
+
SourceView* m_sourceView;
DiagnosticSink* m_sink;
NamePool* m_namePool;
diff --git a/source/compiler-core/slang-source-loc.cpp b/source/compiler-core/slang-source-loc.cpp
index 5058a1522..cfde5f1da 100644
--- a/source/compiler-core/slang-source-loc.cpp
+++ b/source/compiler-core/slang-source-loc.cpp
@@ -1018,4 +1018,44 @@ PathInfo SourceManager::getPathInfo(SourceLoc loc, SourceLocType type)
}
}
+SourceLoc::RawValue SourceView::getAbsoluteLocation(SourceLoc location) const
+{
+ AbsoluteSegment segment;
+ if (m_absSegments.getCount())
+ {
+ if (m_absSegments.getFirst().begin > location)
+ {
+ segment.begin = m_range.begin;
+ segment.absoluteBegin = m_absoluteLocationBase;
+ }
+ else
+ {
+ auto it = std::upper_bound(
+ m_absSegments.begin(),
+ m_absSegments.end(),
+ location,
+ [](SourceLoc const& loc, AbsoluteSegment const& seg)
+ { return loc < seg.begin; }) -
+ 1;
+ segment = *it;
+ }
+ }
+ else
+ {
+ segment = getLastSegment();
+ }
+ auto offset = SourceRange(segment.begin, location).getSize();
+ return segment.absoluteBegin + offset;
+}
+
+SourceLoc::RawValue SourceManager::getAbsoluteLocation(SourceLoc location) const
+{
+ SourceLoc::RawValue res = 0;
+ if (const SourceView* view = findSourceView(location))
+ {
+ res = view->getAbsoluteLocation(location);
+ }
+ return res;
+}
+
} // namespace Slang
diff --git a/source/compiler-core/slang-source-loc.h b/source/compiler-core/slang-source-loc.h
index c46c9063a..674ed2076 100644
--- a/source/compiler-core/slang-source-loc.h
+++ b/source/compiler-core/slang-source-loc.h
@@ -147,6 +147,10 @@ public:
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); }
+ 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; }
+ 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; }
@@ -177,7 +181,7 @@ struct SourceRange
return rawLoc >= begin.getRaw() && rawLoc <= end.getRaw();
}
/// Get the total size
- UInt getSize() const { return UInt(end.getRaw() - begin.getRaw()); }
+ SourceLoc::RawValue getSize() const { return end.getRaw() - begin.getRaw(); }
/// Get the offset of a loc in this range
int getOffset(SourceLoc loc) const
@@ -190,7 +194,7 @@ struct SourceRange
SourceLoc getSourceLocFromOffset(uint32_t offset) const
{
SLANG_ASSERT(offset <= getSize());
- return begin + Int(offset);
+ return begin + offset;
}
SourceRange() {}
@@ -406,6 +410,48 @@ public:
///< locations. Relative to the line number in the underlying file.
};
+ // Represents a segment of a source.
+ // All SourceLoc in segment are linearly mapped relative to absoluteBegin
+ struct AbsoluteSegment
+ {
+ // SourceLoc in the file range.
+ SourceLoc begin = {};
+ // Location in the absolute mapping of locations ordered by includes.
+ SourceLoc::RawValue absoluteBegin = {};
+ };
+
+ // Set the base of the absolute location mapping for this SourceView.
+ void setAbsoluteLocationBase(SourceLoc::RawValue absLoc) { m_absoluteLocationBase = absLoc; }
+
+ AbsoluteSegment getLastSegment() const
+ {
+ AbsoluteSegment res;
+ if (m_absSegments.getCount())
+ {
+ res = m_absSegments.getLast();
+ }
+ else
+ {
+ res.begin = m_range.begin;
+ res.absoluteBegin = m_absoluteLocationBase;
+ }
+ return res;
+ }
+
+ // Add a segment of absolute mapping after the previous ones.
+ void addAbsoluteSegment(SourceLoc begin, SourceLoc::RawValue absoluteBegin)
+ {
+ SLANG_ASSERT(m_range.contains(begin));
+ SLANG_ASSERT(getLastSegment().begin < begin);
+ AbsoluteSegment seg;
+ seg.begin = begin;
+ seg.absoluteBegin = absoluteBegin;
+ m_absSegments.add(seg);
+ }
+
+ // Maps a SourceLoc inside this SourceView to a unique absolute location ordered by includes.
+ SourceLoc::RawValue getAbsoluteLocation(SourceLoc loc) const;
+
/// Given a sourceLoc finds the entry associated with it. If returns -1 then no entry is
/// associated with this location, and therefore the location should be interpreted as an offset
/// into the underlying sourceFile.
@@ -495,6 +541,8 @@ protected:
SourceFile* m_sourceFile; ///< The source file. Can hold the line breaks
List<Entry> m_entries; ///< An array entries describing how we should interpret a range,
///< starting from the start location.
+ SourceLoc::RawValue m_absoluteLocationBase = 0; ///< Base of the absolute location mapping.
+ List<AbsoluteSegment> m_absSegments; ///< Segments of absolute location mapping.
};
struct SourceManager
@@ -564,6 +612,9 @@ struct SourceManager
void addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile);
void addSourceFileIfNotExist(const String& uniqueIdentity, SourceFile* sourceFile);
+ // Maps a SourceLoc to an absolute location
+ SourceLoc::RawValue getAbsoluteLocation(SourceLoc location) const;
+
/// Get the slice pool
StringSlicePool& getStringSlicePool() { return m_slicePool; }
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index dfea9fede..5dcc41bd8 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -429,6 +429,34 @@ DIAGNOSTIC(
Warning,
pragmaOnceIgnored,
"pragma once was ignored - this is typically because is not placed in an include")
+DIAGNOSTIC(15610, Error, pragmaWarningGenericError, "Error in #pragma warning processing: $0")
+DIAGNOSTIC(
+ 15611,
+ Warning,
+ pragmaWarningPopEmpty,
+ "Detected #pragma warning(pop) with no corresponding #pragma warning(push)")
+DIAGNOSTIC(
+ 15612,
+ Warning,
+ pragmaWarningPushNotPopped,
+ "Detected #pragma warning(push) with no corresponding #pragma warning(pop)")
+DIAGNOSTIC(15613, Warning, pragmaWarningUnknownSpecifier, "Unknown #pragma warning specifier '$0'")
+DIAGNOSTIC(
+ 15614,
+ Warning,
+ pragmaWarningSuppressCannotIdentifyNextLine,
+ "Cannot identify the next line to suppress in #pragma warning suppress")
+DIAGNOSTIC(
+ 15615,
+ Warning,
+ pragmaWarningCannotInsertHere,
+ "Cannot insert #pragma warning here for id '$0'")
+DIAGNOSTIC(
+ 15616,
+ Note,
+ pragmaWarningPointSuppress,
+ "#pragma warning for id '$0' was suppressed here")
+
// 159xx - user-defined error/warning
DIAGNOSTIC(15900, Error, userDefinedError, "#error: $0")
diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp
index 5a4e26f37..3f120d5eb 100644
--- a/source/slang/slang-preprocessor.cpp
+++ b/source/slang/slang-preprocessor.cpp
@@ -200,6 +200,8 @@ struct InputStream
MacroInvocation* getFirstBusyMacroInvocation() { return m_firstBusyMacroInvocation; }
+ virtual SourceLoc findNextLineEndImpl(SourceLoc from, UInt& lineCount) const = 0;
+
protected:
/// The preprocessor that this input stream is being used by
Preprocessor* m_preprocessor = nullptr;
@@ -235,6 +237,14 @@ struct PretokenizedInputStream : InputStream
virtual Token peekToken() SLANG_OVERRIDE { return m_tokenReader.peekToken(); }
+ virtual SourceLoc findNextLineEndImpl(SourceLoc from, UInt& lineCount) const SLANG_OVERRIDE
+ {
+ // Not implemented
+ SLANG_UNUSED(from)
+ SLANG_UNUSED(lineCount)
+ return {};
+ }
+
protected:
/// Initialize an input stream, and assocaite with a specific `preprocessor`
PretokenizedInputStream(Preprocessor* preprocessor)
@@ -467,6 +477,22 @@ struct InputStreamStack
}
}
+ SourceLoc findNextLineEnd(SourceLoc from, UInt lineCount = 1) const
+ {
+ auto top = m_top;
+ SourceLoc res = from;
+ res = top->findNextLineEndImpl(res, lineCount);
+ if (lineCount > 0)
+ {
+ // We did not consume all the lines, but arrived at the end of the current Stream
+ // For now, return none.
+ // We could pop the inputStream stack and continue looking,
+ // but it is an unlikely edge case.
+ res = {};
+ }
+ return res;
+ }
+
private:
/// The top of the stack of input streams
InputStream* m_top = nullptr;
@@ -509,6 +535,11 @@ struct LexerInputStream : InputStream
Token peekToken() SLANG_OVERRIDE { return m_lookaheadToken; }
+ virtual SourceLoc findNextLineEndImpl(SourceLoc from, UInt& lineCount) const SLANG_OVERRIDE
+ {
+ return m_lexer.findNextLineEnd(from, lineCount);
+ }
+
private:
/// Read a token from the lexer, bypassing lookahead
Token _readTokenImpl()
@@ -701,6 +732,14 @@ struct MacroInvocation : InputStream
MacroDefinition* getMacroDefinition() { return m_macro; }
+ virtual SourceLoc findNextLineEndImpl(SourceLoc from, UInt& lineCount) const SLANG_OVERRIDE
+ {
+ // There are no actual lines inside of a macro invocation
+ SLANG_UNUSED(from)
+ SLANG_UNUSED(lineCount)
+ return {};
+ }
+
private:
// Macro invocations are created as part of applying macro expansion
// to a stream, so the `ExpansionInputStream` type takes responsibility
@@ -838,6 +877,19 @@ struct ExpansionInputStream : InputStream
m_isInExpansion = true;
}
+ virtual SourceLoc findNextLineEndImpl(SourceLoc from, UInt& lineCount) const SLANG_OVERRIDE
+ {
+ // Should not be here / not implemented
+ SLANG_UNUSED(from)
+ SLANG_UNUSED(lineCount)
+ return {};
+ }
+
+ SourceLoc findNextLineEnd(SourceLoc from, UInt lineCount = 1) const
+ {
+ return m_inputStreams.findNextLineEnd(from, lineCount);
+ }
+
private:
/// The base stream that macro expansion is being applied to
InputStream* m_base = nullptr;
@@ -950,6 +1002,261 @@ private:
ExpansionInputStream* m_expansionStream;
};
+enum class PragmaWarningSpecifier
+{
+ Default,
+ Disable,
+ Error,
+ Once,
+ Suppress,
+};
+
+struct WarningTimeline
+{
+ struct Entry
+ {
+ PragmaWarningSpecifier specifier = {};
+ SourceLoc::RawValue location = {}; // Absolute location
+ // Used for the once specifier
+ // -1 points to this, but not consumed
+ // -2 points to this, but was consumed
+ // >= 0 points to a previous once entry (sharing the payload)
+ // Used for the suppress specifier to store the original SourceLoc needed to emit a warning
+ union
+ {
+ int payload = 0;
+ SourceLoc::RawValue debugLocation; // Store the raw value for trivial copy
+ };
+
+ bool operator<(Entry const& other) const { return location < other.location; }
+ };
+ // Sorted by location
+ List<Entry> entries = {};
+
+ const Entry* findEntry(SourceLoc::RawValue location) const
+ {
+ const Entry* res = nullptr;
+ if (entries.getCount() && location >= entries.getFirst().location)
+ {
+ auto nextEntryIndex = ::std::upper_bound(
+ entries.begin(),
+ entries.end(),
+ location,
+ [](SourceLoc::RawValue const& lhs, Entry const& rhs)
+ { return lhs < rhs.location; });
+ res = nextEntryIndex - 1;
+ }
+ return res;
+ }
+
+ PragmaWarningSpecifier consumeSpecifier(SourceLoc::RawValue location)
+ {
+ PragmaWarningSpecifier res = PragmaWarningSpecifier::Default;
+ Entry* entry = const_cast<Entry*>(findEntry(location));
+ if (entry)
+ {
+ PragmaWarningSpecifier& spec = entry->specifier;
+ res = spec;
+ if (res == PragmaWarningSpecifier::Once)
+ {
+ int* payload = &entry->payload;
+ if (*payload >= 0)
+ {
+ payload = &entries[*payload].payload;
+ }
+ if (*payload == -1)
+ {
+ res = PragmaWarningSpecifier::Default;
+ --(*payload);
+ }
+ else if (*payload < -1)
+ {
+ res = PragmaWarningSpecifier::Disable;
+ }
+ }
+ }
+ return res;
+ }
+
+ const Entry* getLatestEntry() const
+ {
+ const Entry* res = nullptr;
+ if (entries.getCount())
+ {
+ res = &entries.getLast();
+ }
+ return res;
+ }
+
+ PragmaWarningSpecifier getLatestSpecifier() const
+ {
+ PragmaWarningSpecifier res = PragmaWarningSpecifier::Default;
+ if (entries.getCount())
+ {
+ res = entries.getLast().specifier;
+ }
+ return res;
+ }
+
+ void addEntry(
+ SourceLoc::RawValue location,
+ PragmaWarningSpecifier specifier,
+ const Entry* poppingFrom,
+ DiagnosticSink* sink,
+ int id,
+ SourceLoc debugLoc)
+ {
+ SourceLoc::RawValue maxKnownLocation =
+ entries.getCount() ? entries.getLast().location : SourceLoc::RawValue(0);
+ // Add on top
+ if (location > maxKnownLocation)
+ {
+ // Add a new entry only if necessary
+ if (getLatestSpecifier() != specifier || specifier == PragmaWarningSpecifier::Once)
+ {
+ Entry e;
+ e.specifier = specifier;
+ e.location = location;
+ if (specifier == PragmaWarningSpecifier::Once)
+ {
+ if (poppingFrom)
+ {
+ SLANG_ASSERT(poppingFrom->specifier == PragmaWarningSpecifier::Once);
+ if (poppingFrom->payload >= 0)
+ {
+ e.payload = poppingFrom->payload;
+ }
+ else
+ {
+ e.payload = static_cast<int>(poppingFrom - entries.begin());
+ }
+ }
+ else
+ {
+ e.payload = -1;
+ }
+ }
+ else
+ {
+ e.debugLocation = debugLoc.getRaw();
+ }
+ entries.add(e);
+ }
+ }
+ else
+ {
+ if (sink)
+ {
+ sink->diagnose(debugLoc, Diagnostics::pragmaWarningCannotInsertHere, id);
+ const Entry* prevEntry = findEntry(location);
+ if (prevEntry && prevEntry->specifier == PragmaWarningSpecifier::Suppress)
+ {
+ sink->diagnose(
+ SourceLoc::fromRaw(prevEntry->debugLocation),
+ Diagnostics::pragmaWarningPointSuppress,
+ id);
+ }
+ }
+ }
+ }
+
+ void addEntryForPragmaPop(
+ SourceLoc::RawValue location,
+ SourceLoc::RawValue pushedLocation,
+ DiagnosticSink* sink,
+ int id,
+ SourceLoc debugLoc)
+ {
+ const Entry* poppingFrom = findEntry(pushedLocation);
+ addEntry(
+ location,
+ poppingFrom ? poppingFrom->specifier : PragmaWarningSpecifier::Default,
+ poppingFrom,
+ sink,
+ id,
+ debugLoc);
+ }
+};
+
+struct WarningStateTracker : SourceWarningStateTrackerBase
+{
+ SourceManager* sourceManager = nullptr;
+ Dictionary<int, WarningTimeline> mapDiagnosticIdToTimeline = {};
+ List<SourceLoc> stack = {};
+
+ WarningStateTracker(SourceManager* sourceManager = nullptr)
+ : sourceManager(sourceManager)
+ {
+ }
+
+ SourceLoc::RawValue getAbsoluteLocation(SourceLoc loc) const
+ {
+ return sourceManager ? sourceManager->getAbsoluteLocation(loc) : loc.getRaw();
+ }
+
+ virtual Severity consumeWarningSeverity(SourceLoc location, int id, Severity severity) override
+ {
+ Severity res = severity;
+ WarningTimeline* timeline = mapDiagnosticIdToTimeline.tryGetValue(id);
+ if (!timeline)
+ return res;
+ SourceLoc::RawValue absoluteLoc = getAbsoluteLocation(location);
+ PragmaWarningSpecifier spec = timeline->consumeSpecifier(absoluteLoc);
+ if (spec == PragmaWarningSpecifier::Disable || spec == PragmaWarningSpecifier::Suppress)
+ {
+ res = Severity::Disable;
+ }
+ else if (spec == PragmaWarningSpecifier::Error)
+ {
+ res = Severity::Error;
+ }
+ return res;
+ }
+
+ void addEntry(
+ SourceLoc location,
+ SourceLoc nextLineEnd,
+ int id,
+ PragmaWarningSpecifier specifier,
+ DiagnosticSink* sink = nullptr)
+ {
+ WarningTimeline& timeline = mapDiagnosticIdToTimeline[id];
+ auto absLoc = getAbsoluteLocation(location);
+ PragmaWarningSpecifier prev = timeline.getLatestSpecifier();
+ auto lastEntry = timeline.getLatestEntry();
+ timeline.addEntry(absLoc, specifier, nullptr, sink, id, location);
+ if (specifier == PragmaWarningSpecifier::Suppress)
+ {
+ auto nextAbsLoc = getAbsoluteLocation(nextLineEnd);
+ timeline.addEntry(nextAbsLoc, prev, lastEntry, sink, id, nextLineEnd);
+ }
+ }
+
+ void addPragmaPush(SourceLoc location) { stack.add(location); }
+
+ void addPragmaPop(SourceLoc location, DiagnosticSink* sink = nullptr)
+ {
+ if (stack.getCount())
+ {
+ const SourceLoc pushed = stack.getLast();
+ stack.removeLast();
+ if (mapDiagnosticIdToTimeline.getCount())
+ {
+ const SourceLoc::RawValue absLoc = getAbsoluteLocation(location);
+ const SourceLoc::RawValue absPushed = getAbsoluteLocation(pushed);
+ for (auto& [id, timeline] : mapDiagnosticIdToTimeline)
+ {
+ timeline.addEntryForPragmaPop(absLoc, absPushed, sink, id, location);
+ }
+ }
+ }
+ else if (sink)
+ {
+ sink->diagnose(location, Diagnostics::pragmaWarningPopEmpty);
+ }
+ }
+};
+
/// State of the preprocessor
struct Preprocessor
{
@@ -981,6 +1288,8 @@ struct Preprocessor
/// stop them from being included again.
HashSet<String> pragmaOnceUniqueIdentities;
+ WarningStateTracker* warningStateTracker = nullptr;
+
/// Name pool to use when creating `Name`s from strings
NamePool* namePool = nullptr;
@@ -1002,8 +1311,10 @@ struct Preprocessor
NamePool* getNamePool() { return namePool; }
SourceManager* getSourceManager() { return sourceManager; }
+ SourceLoc::RawValue absoluteSourceLocCounter = 0;
+
/// Push a new input file onto the input stack of the preprocessor
- void pushInputFile(InputFile* inputFile);
+ void pushInputFile(InputFile* inputFile, SourceLoc location);
/// Pop the inner-most input file from the stack of input files
void popInputFile();
@@ -2409,6 +2720,15 @@ static void SkipToEndOfLine(PreprocessorDirectiveContext* context)
}
}
+static SourceLoc FindNextEndOfLine(
+ PreprocessorDirectiveContext* context,
+ SourceLoc from,
+ UInt lineCount = 1)
+{
+ auto inputStream = getInputStream(context);
+ return inputStream->findNextLineEnd(from, lineCount);
+}
+
static bool ExpectRaw(
PreprocessorDirectiveContext* context,
TokenType tokenType,
@@ -3157,8 +3477,20 @@ static SlangResult readFile(
return SLANG_OK;
}
-void Preprocessor::pushInputFile(InputFile* inputFile)
+void Preprocessor::pushInputFile(InputFile* inputFile, SourceLoc loc)
{
+ if (m_currentInputFile)
+ {
+ SourceView* sourceView = m_currentInputFile->getLexer()->m_sourceView;
+ SourceLoc::RawValue offset = SourceRange(sourceView->getLastSegment().begin, loc).getSize();
+ absoluteSourceLocCounter += offset;
+ }
+
+ {
+ SourceView* sourceView = inputFile->getLexer()->m_sourceView;
+ sourceView->setAbsoluteLocationBase(absoluteSourceLocCounter);
+ }
+
inputFile->m_parent = m_currentInputFile;
m_currentInputFile = inputFile;
}
@@ -3281,7 +3613,7 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context)
InputFile* inputFile = new InputFile(context->m_preprocessor, sourceView);
- context->m_preprocessor->pushInputFile(inputFile);
+ context->m_preprocessor->pushInputFile(inputFile, directiveLoc);
}
static void _parseMacroOps(
@@ -3819,6 +4151,145 @@ SLANG_PRAGMA_DIRECTIVE_CALLBACK(handlePragmaOnceDirective)
context->m_preprocessor->pragmaOnceUniqueIdentities.add(issuedFromPathInfo.uniqueIdentity);
}
+SLANG_PRAGMA_DIRECTIVE_CALLBACK(handlePragmaWarningDirective)
+{
+ auto directiveLoc = GetDirectiveLoc(context);
+ SLANG_UNUSED(subDirectiveToken)
+ SLANG_UNUSED(directiveLoc);
+ Expect(context, TokenType::LParent, Diagnostics::syntaxError);
+ Token tk = PeekToken(context);
+ auto finish = [&]() -> void { SkipToEndOfLine(context); };
+ if (tk.type == TokenType::Identifier)
+ {
+ // #pragma warning (push)
+ if (tk.getContent() == "push")
+ {
+ AdvanceToken(context);
+ context->m_preprocessor->warningStateTracker->addPragmaPush(tk.loc);
+ }
+ // #pragma warning (pop)
+ else if (tk.getContent() == "pop")
+ {
+ AdvanceToken(context);
+ context->m_preprocessor->warningStateTracker->addPragmaPop(tk.loc, GetSink(context));
+ }
+ else
+ {
+ // #pragma warning (spec : id-list [; ...]), examples:
+ // (disable : 123) : disables 123
+ // (disable : 1 2 ; error 4 5 6) disables 1, then disables 2, then errors 4, ...
+ // (disable : 1 ; default : 1) disables 1, then defaults 1
+ // Parse a list of 'specifier : id-list', separated by ';'
+ while (true)
+ {
+ // Read the specifier
+ // We need the raw token location because if the token is a #definition,
+ // PeekToken().loc would be where the definition is, not the invocation
+ // PeekRawToken().loc is where the invocation of the macro is located:
+ // Example:
+ // #define SPEC suppress // (a)
+ // #pragma warning (SPEC : 12) (b)
+ // Here the raw token is 'SPEC' located at its invocation on line (b)
+ // and the token is 'suppress' located at the macro definition on line (a)
+ // The #pragma warning should take effect from line (b), not line (a),
+ // So we need the raw token location.
+ SourceLoc specifierLocation = PeekRawToken(context).loc;
+ Token id;
+ Expect(context, TokenType::Identifier, Diagnostics::syntaxError, &id);
+ PragmaWarningSpecifier specifier;
+ SourceLoc nextLineEnd = {}; // Needed for suppress
+ if (id.getContent() == "default")
+ {
+ specifier = PragmaWarningSpecifier::Default;
+ }
+ else if (id.getContent() == "disable")
+ {
+ specifier = PragmaWarningSpecifier::Disable;
+ }
+ else if (id.getContent() == "error")
+ {
+ specifier = PragmaWarningSpecifier::Error;
+ }
+ else if (id.getContent() == "once")
+ {
+ specifier = PragmaWarningSpecifier::Once;
+ }
+ else if (id.getContent() == "suppress")
+ {
+ specifier = PragmaWarningSpecifier::Suppress;
+ // We need to start from subDirectiveToken.loc because the next tokens
+ // might be macro invocations, and located before this #pragma warning line.
+ nextLineEnd = FindNextEndOfLine(context, specifierLocation, 2);
+ if (!nextLineEnd.isValid())
+ {
+ GetSink(context)->diagnose(
+ specifierLocation,
+ Diagnostics::pragmaWarningSuppressCannotIdentifyNextLine);
+ return finish();
+ }
+ }
+ else
+ {
+ GetSink(context)->diagnose(
+ specifierLocation,
+ Diagnostics::pragmaWarningUnknownSpecifier,
+ id.getContent());
+ return finish();
+ }
+ Expect(context, TokenType::Colon, Diagnostics::syntaxError);
+ // Read the id list
+ while (true)
+ {
+ // Same logic as for the specifierLocation
+ SourceLoc idLocation = PeekRawToken(context).loc;
+ Token warningNumberToken = PeekToken(context);
+ if (warningNumberToken.type == TokenType::IntegerLiteral)
+ {
+ AdvanceToken(context);
+ int warningNumber = stringToInt(warningNumberToken.getContent());
+ context->m_preprocessor->warningStateTracker->addEntry(
+ idLocation,
+ nextLineEnd,
+ warningNumber,
+ specifier,
+ GetSink(context));
+ }
+ else
+ {
+ break;
+ }
+ }
+ SourceLoc endLoc = PeekRawToken(context).loc;
+ Token end = PeekToken(context);
+ if (end.type == TokenType::Semicolon)
+ {
+ // We need to parse the next 'spec : id-list'
+ AdvanceToken(context);
+ continue;
+ }
+ else if (end.type == TokenType::RParent)
+ {
+ break;
+ }
+ else
+ {
+ GetSink(context)->diagnose(
+ endLoc,
+ Diagnostics::unexpectedToken,
+ end.getContent());
+ return finish();
+ }
+ }
+ }
+ }
+ else
+ {
+ GetSink(context)->diagnose(tk, Diagnostics::syntaxError);
+ return finish();
+ }
+ Expect(context, TokenType::RParent, Diagnostics::syntaxError);
+}
+
// Information about a specific `#pragma` directive
struct PragmaDirective
{
@@ -3833,6 +4304,8 @@ struct PragmaDirective
static const PragmaDirective kPragmaDirectives[] = {
{"once", &handlePragmaOnceDirective},
+ {"warning", &handlePragmaWarningDirective},
+
{NULL, NULL},
};
@@ -4071,6 +4544,13 @@ void Preprocessor::popInputFile()
conditional->ifToken.getContent());
}
+ {
+ SourceView* sourceView = inputFile->getLexer()->m_sourceView;
+ auto lastSegment = sourceView->getLastSegment();
+ absoluteSourceLocCounter +=
+ SourceRange(lastSegment.begin, sourceView->getRange().end).getSize();
+ }
+
// We will update the current file to the parent of whatever
// the `inputFile` was (usually the file that `#include`d it).
//
@@ -4086,6 +4566,13 @@ void Preprocessor::popInputFile()
{
endOfFileToken = eofToken;
}
+ else
+ {
+ SourceView* sourceView = parentFile->getLexer()->m_sourceView;
+ sourceView->addAbsoluteSegment(
+ parentFile->getExpansionStream()->peekLoc(),
+ absoluteSourceLocCounter);
+ }
delete inputFile;
}
@@ -4230,6 +4717,20 @@ static TokenList ReadAllTokens(Preprocessor* preprocessor)
}
}
+static void finalCheckPragmaWarnings(Preprocessor* preprocessor)
+{
+ auto tracker = preprocessor->warningStateTracker;
+ if (tracker)
+ {
+ auto sink = GetSink(preprocessor);
+ for (const auto& pushed : tracker->stack)
+ {
+ sink->diagnose(pushed, Diagnostics::pragmaWarningPushNotPopped);
+ }
+ tracker->stack.clearAndDeallocate();
+ }
+}
+
} // namespace preprocessor
/// Try to look up a macro with the given `macroName` and produce its value as a string
@@ -4300,6 +4801,11 @@ TokenList preprocessSource(
{
desc.contentAssistInfo = &linkage->contentAssistInfo.preprocessorInfo;
}
+
+ preprocessor::WarningStateTracker* wst =
+ new preprocessor::WarningStateTracker(desc.sourceManager);
+ desc.sink->setSourceWarningStateTracker(wst);
+
return preprocessSource(file, desc, outDetectedLanguage);
}
@@ -4321,6 +4827,9 @@ TokenList preprocessSource(
preprocessor.endOfFileToken.flags = TokenFlag::AtStartOfLine;
preprocessor.contentAssistInfo = desc.contentAssistInfo;
+ preprocessor.warningStateTracker =
+ dynamicCast<preprocessor::WarningStateTracker>(desc.sink->getSourceWarningStateTracker());
+
// Add builtin macros
{
auto namePool = desc.namePool;
@@ -4366,7 +4875,7 @@ TokenList preprocessSource(
// create an initial input stream based on the provided buffer
InputFile* primaryInputFile = new InputFile(&preprocessor, sourceView);
- preprocessor.pushInputFile(primaryInputFile);
+ preprocessor.pushInputFile(primaryInputFile, sourceView->getRange().begin);
}
TokenList tokens = ReadAllTokens(&preprocessor);
@@ -4376,6 +4885,8 @@ TokenList preprocessSource(
handler->handleEndOfTranslationUnit(&preprocessor);
}
+ finalCheckPragmaWarnings(&preprocessor);
+
// debugging: build the pre-processed source back together
#if 0
StringBuilder sb;