From e38dfaa92c18ec984682dc0ebccde3e2abffd058 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 13 May 2026 05:55:12 +0100 Subject: [PATCH] preproc: Parse cpp linemarkers The line numbers in preproc's errors involving strings or INCBINs were incorrect before this change because all the lines from '#include'd files incremented the line number that would be reported. --- tools/preproc/c_file.cpp | 98 +++++++++++++++++++++++++++------ tools/preproc/c_file.h | 8 ++- tools/preproc/char_util.h | 5 ++ tools/preproc/string_parser.cpp | 3 +- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index 8de8ed7a92..1d763b9fe2 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -36,26 +36,26 @@ CFile::CFile(const char * filenameCStr, bool isStdin, const char * graphicsRootCStr) { if (isStdin) - m_filename = std::string{"/"}.append(filenameCStr); + m_location.filename = std::string{"/"}.append(filenameCStr); else - m_filename = std::string(filenameCStr); + m_location.filename = std::string(filenameCStr); + m_location.lineNum = 1; + m_location.acceptLineMarker = isStdin; m_buffer = ReadFileToBuffer(filenameCStr, isStdin, &m_size); m_pos = 0; - m_lineNum = 1; m_isStdin = isStdin; m_graphicsRoot = graphicsRootCStr; if (m_graphicsRoot.empty()) m_graphicsRoot = "./"; if (m_graphicsRoot[m_graphicsRoot.length() - 1] != '/') m_graphicsRoot.push_back('/'); } -CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename)) +CFile::CFile(CFile&& other) : m_location(std::move(other.m_location)) { m_buffer = other.m_buffer; m_pos = other.m_pos; m_size = other.m_size; - m_lineNum = other.m_lineNum; m_isStdin = other.m_isStdin; other.m_buffer = NULL; @@ -72,6 +72,66 @@ void CFile::Preproc() while (m_pos < m_size) { + if (m_location.acceptLineMarker) + { + if (m_buffer[m_pos] == '#') + { + long hashPos = m_pos; + + long startPos; + long lineNum; + std::string filename; + + m_pos++; + + if (m_buffer[m_pos] != ' ') + goto linemarker_error; + m_pos++; + + startPos = m_pos; + if (!IsAsciiDigit(m_buffer[m_pos])) + goto linemarker_error; + do + m_pos++; + while (IsAsciiDigit(m_buffer[m_pos])); + lineNum = atol(&m_buffer[startPos]); + + if (m_buffer[m_pos] != ' ') + goto linemarker_error; + m_pos++; + + if (m_buffer[m_pos] != '"') + goto linemarker_error; + m_pos++; + + startPos = m_pos; + while (m_pos < m_size && m_buffer[m_pos] != '"') + m_pos++; + filename = std::string(&m_buffer[startPos], m_pos - startPos); + + if (m_buffer[m_pos] != '"') + goto linemarker_error; + m_pos++; + + while (m_pos < m_size && m_buffer[m_pos] != '\n') + m_pos++; + if (m_buffer[m_pos] != '\n') + goto linemarker_error; + m_pos++; + + m_location.lineNum = lineNum - 1; + m_location.filename = std::move(filename); +linemarker_error: + m_location.acceptLineMarker = false; + // Re-parse this line so that it's available to cc1. + m_pos = hashPos; + } + else if (!IsAsciiWhitespace(m_buffer[m_pos])) + { + m_location.acceptLineMarker = false; + } + } + if (stringChar) { if (m_buffer[m_pos] == stringChar) @@ -89,7 +149,7 @@ void CFile::Preproc() else { if (m_buffer[m_pos] == '\n') - m_lineNum++; + Newline(); std::putchar(m_buffer[m_pos]); m_pos++; } @@ -108,7 +168,7 @@ void CFile::Preproc() std::putchar(c); if (c == '\n') - m_lineNum++; + Newline(); else if (c == '"') stringChar = '"'; else if (c == '\'') @@ -133,7 +193,7 @@ bool CFile::ConsumeNewline() if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n') { m_pos += 2; - m_lineNum++; + Newline(); std::putchar('\n'); return true; } @@ -141,7 +201,7 @@ bool CFile::ConsumeNewline() if (m_buffer[m_pos] == '\n') { m_pos++; - m_lineNum++; + Newline(); std::putchar('\n'); return true; } @@ -149,6 +209,12 @@ bool CFile::ConsumeNewline() return false; } +void CFile::Newline() +{ + m_location.lineNum++; + m_location.acceptLineMarker = m_isStdin; +} + void CFile::SkipWhitespace() { while (ConsumeHorizontalWhitespace() || ConsumeNewline()) @@ -158,7 +224,7 @@ void CFile::SkipWhitespace() void CFile::TryConvertString() { long oldPos = m_pos; - long oldLineNum = m_lineNum; + auto oldLocation = m_location; bool noTerminator = false; if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1]))) @@ -177,7 +243,7 @@ void CFile::TryConvertString() if (m_buffer[m_pos] != '(') { m_pos = oldPos; - m_lineNum = oldLineNum; + m_location = oldLocation; return; } @@ -335,7 +401,7 @@ void CFile::TryConvertIncbin() int size = 1 << incbinType; long oldPos = m_pos; - long oldLineNum = m_lineNum; + auto oldLocation = m_location; m_pos += idents[incbinType].length(); @@ -344,7 +410,7 @@ void CFile::TryConvertIncbin() if (m_buffer[m_pos] != '(') { m_pos = oldPos; - m_lineNum = oldLineNum; + m_location = oldLocation; return; } @@ -412,7 +478,7 @@ void CFile::TryConvertIncgfx() int size = 1 << incgfxType; long oldPos = m_pos; - long oldLineNum = m_lineNum; + auto oldLocation = m_location; m_pos += idents[incgfxType].length(); @@ -420,7 +486,7 @@ void CFile::TryConvertIncgfx() if (m_buffer[m_pos] != '(') { m_pos = oldPos; - m_lineNum = oldLineNum; + m_location = oldLocation; return; } m_pos++; @@ -497,7 +563,7 @@ void CFile::ReportDiagnostic(const char* type, const char* format, std::va_list const int bufferSize = 1024; char buffer[bufferSize]; std::vsnprintf(buffer, bufferSize, format, args); - std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer); + std::fprintf(stderr, "%s:%ld: %s: %s\n", m_location.filename.c_str(), m_location.lineNum, type, buffer); } #define DO_REPORT(type) \ diff --git a/tools/preproc/c_file.h b/tools/preproc/c_file.h index 0dbc4aef1b..56b46bd012 100644 --- a/tools/preproc/c_file.h +++ b/tools/preproc/c_file.h @@ -40,13 +40,17 @@ private: char* m_buffer; long m_pos; long m_size; - long m_lineNum; - std::string m_filename; + struct LogicalLocation { + std::string filename; + long lineNum; + bool acceptLineMarker; + } m_location; bool m_isStdin; std::string m_graphicsRoot; bool ConsumeHorizontalWhitespace(); bool ConsumeNewline(); + void Newline(); void SkipWhitespace(); void TryConvertString(); std::unique_ptr ReadWholeFile(const std::string& path, int& size); diff --git a/tools/preproc/char_util.h b/tools/preproc/char_util.h index 02a6e1ceb2..05754cb497 100644 --- a/tools/preproc/char_util.h +++ b/tools/preproc/char_util.h @@ -56,6 +56,11 @@ inline bool IsAsciiPrintable(unsigned char c) return (c >= ' ' && c <= '~'); } +inline bool IsAsciiWhitespace(unsigned char c) +{ + return c == ' ' || c == '\t' || c == '\f' || c == '\n' || c == '\r' || c == '\v'; +} + // Returns whether the character can start a C identifier or the identifier of a "{FOO}" constant in strings. inline bool IsIdentifierStartingChar(unsigned char c) { diff --git a/tools/preproc/string_parser.cpp b/tools/preproc/string_parser.cpp index dd5196a444..56348d5951 100644 --- a/tools/preproc/string_parser.cpp +++ b/tools/preproc/string_parser.cpp @@ -204,8 +204,9 @@ void StringParser::RaiseError(const char* format, ...) std::va_list args; va_start(args, format); - std::vsnprintf(buffer, bufferSize, format, args); + int n = std::vsnprintf(buffer, bufferSize, format, args); va_end(args); + std::snprintf(buffer + n, bufferSize - n, " before '%.10s'", &m_buffer[m_pos]); throw std::runtime_error(buffer); }