From 64d35b6a8697531b7af92ba4b677a7561aa3f9d9 Mon Sep 17 00:00:00 2001 From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:24:24 +0200 Subject: [PATCH] Allow comments within _Pragma(). Fixes #2231. --- src/cc65/pragma.c | 94 +++++++++++++++++++++++++--------------------- src/cc65/preproc.c | 37 ++++++++---------- src/cc65/preproc.h | 5 +++ test/val/bug2231.c | 10 +++++ 4 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 test/val/bug2231.c diff --git a/src/cc65/pragma.c b/src/cc65/pragma.c index 49754fbe8..51f7f3007 100644 --- a/src/cc65/pragma.c +++ b/src/cc65/pragma.c @@ -48,6 +48,7 @@ #include "error.h" #include "global.h" #include "litpool.h" +#include "preproc.h" #include "scanner.h" #include "scanstrbuf.h" #include "symtab.h" @@ -866,24 +867,18 @@ static void NoteMessagePragma (const char* Message) -static void ParsePragmaString (void) +static void ParsePragmaString (StrBuf* B) /* Parse the contents of _Pragma */ { pragma_t Pragma; StrBuf Ident = AUTO_STRBUF_INITIALIZER; - /* Create a string buffer from the string literal */ - StrBuf B = AUTO_STRBUF_INITIALIZER; - - - SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); - /* Skip the string token */ NextToken (); /* Get the pragma name from the string */ - SB_SkipWhite (&B); - if (!SB_GetSym (&B, &Ident, "-")) { + SB_SkipWhite (B); + if (!SB_GetSym (B, &Ident, "-")) { Error ("Invalid pragma"); goto ExitPoint; } @@ -903,119 +898,119 @@ static void ParsePragmaString (void) } /* Check for an open paren */ - SB_SkipWhite (&B); - if (SB_Get (&B) != '(') { + SB_SkipWhite (B); + if (SB_Get (B) != '(') { Error ("'(' expected"); goto ExitPoint; } /* Skip white space before the argument */ - SB_SkipWhite (&B); + SB_SkipWhite (B); /* Switch for the different pragmas */ switch (Pragma) { case PRAGMA_ALIGN: /* TODO: PES_EXPR (PES_DECL) */ - IntPragma (PES_STMT, Pragma, &B, &DataAlignment, 1, 4096); + IntPragma (PES_STMT, Pragma, B, &DataAlignment, 1, 4096); break; case PRAGMA_ALLOW_EAGER_INLINE: - FlagPragma (PES_STMT, Pragma, &B, &EagerlyInlineFuncs); + FlagPragma (PES_STMT, Pragma, B, &EagerlyInlineFuncs); break; case PRAGMA_BSS_NAME: /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ - SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, &B); + SegNamePragma (PES_FUNC, PRAGMA_BSS_NAME, B); break; case PRAGMA_CHARMAP: - CharMapPragma (PES_IMM, &B); + CharMapPragma (PES_IMM, B); break; case PRAGMA_CHECK_STACK: /* TODO: PES_SCOPE maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &CheckStack); + FlagPragma (PES_FUNC, Pragma, B, &CheckStack); break; case PRAGMA_CODE_NAME: /* PES_FUNC is the only sensible option so far */ - SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, &B); + SegNamePragma (PES_FUNC, PRAGMA_CODE_NAME, B); break; case PRAGMA_CODESIZE: /* PES_EXPR would be optimization nightmare */ - IntPragma (PES_STMT, Pragma, &B, &CodeSizeFactor, 10, 1000); + IntPragma (PES_STMT, Pragma, B, &CodeSizeFactor, 10, 1000); break; case PRAGMA_DATA_NAME: /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ - SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, &B); + SegNamePragma (PES_FUNC, PRAGMA_DATA_NAME, B); break; case PRAGMA_INLINE_STDFUNCS: /* TODO: PES_EXPR maybe? */ - FlagPragma (PES_STMT, Pragma, &B, &InlineStdFuncs); + FlagPragma (PES_STMT, Pragma, B, &InlineStdFuncs); break; case PRAGMA_LOCAL_STRINGS: /* TODO: PES_STMT or even PES_EXPR */ - FlagPragma (PES_FUNC, Pragma, &B, &LocalStrings); + FlagPragma (PES_FUNC, Pragma, B, &LocalStrings); break; case PRAGMA_MESSAGE: /* PES_IMM is the only sensible option */ - StringPragma (PES_IMM, &B, NoteMessagePragma); + StringPragma (PES_IMM, B, NoteMessagePragma); break; case PRAGMA_OPTIMIZE: /* TODO: PES_STMT or even PES_EXPR maybe? */ - FlagPragma (PES_STMT, Pragma, &B, &Optimize); + FlagPragma (PES_STMT, Pragma, B, &Optimize); break; case PRAGMA_REGVARADDR: /* TODO: PES_STMT or even PES_EXPR maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &AllowRegVarAddr); + FlagPragma (PES_FUNC, Pragma, B, &AllowRegVarAddr); break; case PRAGMA_REGISTER_VARS: /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &EnableRegVars); + FlagPragma (PES_FUNC, Pragma, B, &EnableRegVars); break; case PRAGMA_RODATA_NAME: /* TODO: PES_STMT or even PES_EXPR maybe? */ - SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, &B); + SegNamePragma (PES_FUNC, PRAGMA_RODATA_NAME, B); break; case PRAGMA_SIGNED_CHARS: /* TODO: PES_STMT or even PES_EXPR maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &SignedChars); + FlagPragma (PES_FUNC, Pragma, B, &SignedChars); break; case PRAGMA_STATIC_LOCALS: /* TODO: PES_STMT or even PES_EXPR (PES_DECL) maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &StaticLocals); + FlagPragma (PES_FUNC, Pragma, B, &StaticLocals); break; case PRAGMA_WRAPPED_CALL: /* PES_IMM is the only sensible option */ - WrappedCallPragma (PES_IMM, &B); + WrappedCallPragma (PES_IMM, B); break; case PRAGMA_WARN: /* PES_IMM is the only sensible option */ - WarnPragma (PES_IMM, &B); + WarnPragma (PES_IMM, B); break; case PRAGMA_WRITABLE_STRINGS: /* TODO: PES_STMT or even PES_EXPR maybe? */ - FlagPragma (PES_FUNC, Pragma, &B, &WritableStrings); + FlagPragma (PES_FUNC, Pragma, B, &WritableStrings); break; case PRAGMA_ZPSYM: /* PES_IMM is the only sensible option */ - StringPragma (PES_IMM, &B, MakeZPSym); + StringPragma (PES_IMM, B, MakeZPSym); break; default: @@ -1023,27 +1018,26 @@ static void ParsePragmaString (void) } /* Closing paren expected */ - SB_SkipWhite (&B); - if (SB_Get (&B) != ')') { + SB_SkipWhite (B); + if (SB_Get (B) != ')') { Error ("')' expected"); goto ExitPoint; } - SB_SkipWhite (&B); + SB_SkipWhite (B); /* Allow an optional semicolon to be compatible with the old syntax */ - if (SB_Peek (&B) == ';') { - SB_Skip (&B); - SB_SkipWhite (&B); + if (SB_Peek (B) == ';') { + SB_Skip (B); + SB_SkipWhite (B); } /* Make sure nothing follows */ - if (SB_Peek (&B) != '\0') { + if (SB_Peek (B) != '\0') { Error ("Unexpected input following pragma directive"); } ExitPoint: /* Release the string buffers */ - SB_Done (&B); SB_Done (&Ident); } @@ -1082,8 +1076,24 @@ void ConsumePragma (void) */ PragmaErrorSkip (); } else { + /* Pragmas that have the C99 _Pragma operator in the source code + ** (instead of #pragma that was converted to _Pragma by the + ** preprocessor) have the argument not preprocessed. We need to do + ** that here, since it may contain comments. Weird but legal C. Pragmas + ** that were converted from #pragma are already preprocessed but doing + ** it twice won't harm. + */ + StrBuf In = AUTO_STRBUF_INITIALIZER; + StrBuf Out = AUTO_STRBUF_INITIALIZER; + SB_Append (&In, GetLiteralStrBuf (CurTok.SVal)); + TranslationPhase3 (&In, &Out); + SB_Done (&In); + /* Parse the pragma */ - ParsePragmaString (); + ParsePragmaString (&Out); + + /* Free the string buffer */ + SB_Done (&Out); } --InPragmaParser; diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index bc21a69d1..bc3b532fa 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -157,11 +157,6 @@ struct HiddenMacro { -static void TranslationPhase3 (StrBuf* Source, StrBuf* Target); -/* Mimic Translation Phase 3. Handle old and new style comments. Collapse -** non-newline whitespace sequences. -*/ - static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFlags); /* Preprocess a single line. Handle specified tokens and operators, remove ** whitespace and comments, then do macro replacement. @@ -3338,7 +3333,22 @@ void HandleSpecialMacro (Macro* M, const char* Name) -static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) +static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFlags) +/* Preprocess a single line. Handle specified tokens and operators, remove +** whitespace and comments, then do macro replacement. +*/ +{ + MacroExp E; + + SkipWhitespace (0); + InitMacroExp (&E); + ReplaceMacros (Source, Target, &E, ModeFlags | MSM_IN_DIRECTIVE); + DoneMacroExp (&E); +} + + + +void TranslationPhase3 (StrBuf* Source, StrBuf* Target) /* Mimic Translation Phase 3. Handle old and new style comments. Collapse ** non-newline whitespace sequences. */ @@ -3384,21 +3394,6 @@ static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) -static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFlags) -/* Preprocess a single line. Handle specified tokens and operators, remove -** whitespace and comments, then do macro replacement. -*/ -{ - MacroExp E; - - SkipWhitespace (0); - InitMacroExp (&E); - ReplaceMacros (Source, Target, &E, ModeFlags | MSM_IN_DIRECTIVE); - DoneMacroExp (&E); -} - - - void Preprocess (void) /* Preprocess lines count of which is affected by directives */ { diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 44e35f30e..c8fc67ddc 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -68,6 +68,11 @@ struct IFile; void HandleSpecialMacro (Macro* M, const char* Name); /* Handle special "magic" macros that may change */ +void TranslationPhase3 (StrBuf* Source, StrBuf* Target); +/* Mimic Translation Phase 3. Handle old and new style comments. Collapse +** non-newline whitespace sequences. +*/ + void Preprocess (void); /* Preprocess a line */ diff --git a/test/val/bug2231.c b/test/val/bug2231.c new file mode 100644 index 000000000..96a856a8d --- /dev/null +++ b/test/val/bug2231.c @@ -0,0 +1,10 @@ +_Pragma("message/*Comment1*/ ( /*Comment2*/\"test message\" /*Comment3*/)") +/* We have no pragma without parenthesis but if there would be one, the +** following should also work: +*/ +/* _Pragma("once// Comment") */ + +int main(void) +{ + return 0; +}