116116 "_Alignas" ,
117117 "alignas" ,
118118)
119- _CXX_DECLARATION_KEYWORDS = {"using" , "namespace" , "template" , "class" }
120- _CXX_ACCESS_SPECIFIERS = {"public" , "private" , "protected" }
121119_RAW_CONDITIONAL_DIRECTIVE_RE = re .compile (
122120 r"^\s*#\s*(?P<directive>if|ifdef|ifndef|elif|else|endif)\b"
123121)
@@ -230,6 +228,12 @@ class _InvalidSpecifierSequence(ValueError):
230228 pass
231229
232230
231+ class _InvalidCGrammarSyntax (ValueError ):
232+ """Raised internally when a nested C grammar region is malformed."""
233+
234+ pass
235+
236+
233237def _looks_like_existing_source_path (value : object ) -> bool :
234238 """Return whether `value` can safely be treated as an existing source path."""
235239 if isinstance (value , Path ):
@@ -274,21 +278,6 @@ def _is_source_key(key: str) -> bool:
274278 return PurePosixPath (key ).suffix .lower () == ".c"
275279
276280
277- def _looks_like_cxx_declaration (text : str ) -> bool :
278- """Detect obvious C++ declarations so they become explicit diagnostics."""
279- stripped = text .lstrip ()
280- identifier = _IDENTIFIER_RE .match (stripped )
281- if identifier is None :
282- return False
283-
284- word = identifier .group (0 )
285- if word in _CXX_DECLARATION_KEYWORDS :
286- return True
287- if word in _CXX_ACCESS_SPECIFIERS :
288- return stripped [identifier .end () :].lstrip ().startswith (":" )
289- return False
290-
291-
292281class CParser :
293282 """Parser orchestration object for the partial typed C model.
294283
@@ -584,7 +573,7 @@ def _could_start_c_external_declaration(text: str) -> bool:
584573 @staticmethod
585574 def _raise_for_invalid_top_level_syntax (segment : CTopLevelSegment ) -> None :
586575 text = segment .text .strip ()
587- if not text or _looks_like_cxx_declaration ( text ) :
576+ if not text :
588577 return
589578 tokens = lex_c_source (text )
590579 has_scope_operator = any (
@@ -606,6 +595,25 @@ def _raise_for_invalid_top_level_syntax(segment: CTopLevelSegment) -> None:
606595 code = "CPARSE_INVALID_SYNTAX" ,
607596 )
608597
598+ def _invalid_syntax_error (
599+ self ,
600+ segment : CTopLevelSegment ,
601+ text : str ,
602+ * ,
603+ context : str ,
604+ offset : int = 0 ,
605+ ) -> CParseError :
606+ """Build the fatal diagnostic used when a C grammar region is invalid."""
607+ location = self ._source_location_at (segment , offset )
608+ return CParseError (
609+ f"Invalid C syntax in { context } : { text .strip ()} " ,
610+ filename = location .filename ,
611+ line_number = location .line ,
612+ column = location .column ,
613+ source_line = location .source_line ,
614+ code = "CPARSE_INVALID_SYNTAX" ,
615+ )
616+
609617 def _macro_dependencies (
610618 self ,
611619 source : str ,
@@ -1183,7 +1191,7 @@ def _invalid_specifier_error(
11831191 line_number = location .line ,
11841192 column = location .column ,
11851193 source_line = location .source_line ,
1186- code = "CPARSE003 " ,
1194+ code = "CPARSE_INVALID_SPECIFIER_SEQUENCE " ,
11871195 )
11881196
11891197 def _atomic_type_specifier_parts (self , spec_text : str ) -> tuple [str , str ] | None :
@@ -1669,7 +1677,7 @@ def _parse_parameter(self, text: str) -> CParameter | None:
16691677 return None
16701678 spec_text , declarator = self ._split_declaration_specifiers (stripped )
16711679 if not spec_text :
1672- return None
1680+ raise _InvalidCGrammarSyntax ( f"Invalid parameter declaration: { stripped } " )
16731681 name , type_ , _storage , _function_specifiers , _direct_function = self ._build_declared_type (
16741682 spec_text ,
16751683 declarator ,
@@ -1724,13 +1732,17 @@ def _parse_parameters(self, parameters_text: str) -> tuple[list[CParameter], boo
17241732
17251733 parameters : list [CParameter ] = []
17261734 variadic = False
1727- for item in top_level_split (stripped , "," ):
1735+ items = top_level_split (stripped , "," )
1736+ for index , item in enumerate (items ):
17281737 if item == "..." :
1738+ if variadic or index != len (items ) - 1 :
1739+ raise _InvalidCGrammarSyntax ("The variadic marker must be the final function parameter." )
17291740 variadic = True
17301741 continue
17311742 parameter = self ._parse_parameter (item )
1732- if parameter is not None :
1733- parameters .append (parameter )
1743+ if parameter is None :
1744+ raise _InvalidCGrammarSyntax (f"Invalid parameter declaration: { item } " )
1745+ parameters .append (parameter )
17341746 return parameters , variadic
17351747
17361748 def _is_knr_definition (self , segment : CTopLevelSegment , parameters_text : str ) -> bool :
@@ -1803,7 +1815,7 @@ def _raise_for_unsupported_old_style_definitions(
18031815 line_number = mapping .line if mapping is not None else index + 1 ,
18041816 column = max (line .find (name_match .group (0 )) + 1 , 1 ),
18051817 source_line = source_line ,
1806- code = "CPARSE002 " ,
1818+ code = "CPARSE_UNSUPPORTED_KNR_DEFINITION " ,
18071819 )
18081820 if stripped .endswith (";" ):
18091821 saw_old_style_declaration = True
@@ -1821,7 +1833,7 @@ def _raise_for_unsupported_old_style_definitions(
18211833 line_number = mapping .line if mapping is not None else index + 1 ,
18221834 column = max (line .find (name_match .group (0 )) + 1 , 1 ),
18231835 source_line = source_line ,
1824- code = "CPARSE002 " ,
1836+ code = "CPARSE_UNSUPPORTED_KNR_DEFINITION " ,
18251837 )
18261838
18271839 def _prototype_style (self , parameters_text : str ) -> str :
@@ -1857,7 +1869,7 @@ def _parse_function(self, segment: CTopLevelSegment) -> CFunction | None:
18571869 line_number = segment .original_start_line ,
18581870 column = segment .original_start_column ,
18591871 source_line = segment .original_source_line ,
1860- code = "CPARSE002 " ,
1872+ code = "CPARSE_UNSUPPORTED_KNR_DEFINITION " ,
18611873 )
18621874 return self ._function_from_type (
18631875 name ,
@@ -2196,6 +2208,13 @@ def _parse_fields(
21962208 """Parse struct/union member declarations through the shared backend."""
21972209 members : list [CVariable ] = []
21982210 diagnostics : list [CDiagnostic ] = []
2211+ if body .strip () and not body .rstrip ().endswith (";" ):
2212+ raise self ._invalid_syntax_error (
2213+ segment ,
2214+ body ,
2215+ context = f"{ owner_kind } field declaration" ,
2216+ offset = body_offset ,
2217+ )
21992218 for text , field_offset in top_level_split_with_offsets (body , ";" ):
22002219 member_offset = body_offset + field_offset
22012220 member_location = self ._source_location_at (segment , member_offset )
@@ -2239,17 +2258,21 @@ def _parse_fields(
22392258 )
22402259 )
22412260 continue
2261+ if "::" in text :
2262+ raise self ._invalid_syntax_error (
2263+ segment ,
2264+ text ,
2265+ context = f"{ owner_kind } field declaration" ,
2266+ offset = member_offset ,
2267+ )
22422268 spec_text , declarator_list = self ._split_declaration_specifiers (text )
22432269 if not spec_text or not declarator_list :
2244- diagnostics .append (
2245- self ._field_diagnostic (
2246- segment ,
2247- owner_kind ,
2248- "Unsupported field declaration." ,
2249- offset = member_offset ,
2250- )
2270+ raise self ._invalid_syntax_error (
2271+ segment ,
2272+ text ,
2273+ context = f"{ owner_kind } field declaration" ,
2274+ offset = member_offset ,
22512275 )
2252- continue
22532276 for declarator in top_level_split (declarator_list , "," ):
22542277 declaration , _initializer = top_level_partition (declarator , "=" )
22552278 declaration , bit_width = top_level_partition (declaration , ":" )
@@ -2293,10 +2316,10 @@ def _parse_enumerators(self, body: str, segment: CTopLevelSegment) -> list[CEnum
22932316 name_text , value = top_level_partition (item , "=" )
22942317 identifier = self ._read_identifier (name_text .strip (), 0 )
22952318 if identifier is None :
2296- continue
2319+ raise self . _invalid_syntax_error ( segment , item , context = "enum member" )
22972320 name , end = identifier
22982321 if name_text [end :].strip ():
2299- continue
2322+ raise self . _invalid_syntax_error ( segment , item , context = "enum member" )
23002323 constants .append (
23012324 CEnumerator (
23022325 name = name ,
@@ -2402,7 +2425,6 @@ def _parse_declaration(
24022425 not text
24032426 or text .startswith ("_Static_assert" )
24042427 or self ._has_unsupported_declaration_marker (text )
2405- or _looks_like_cxx_declaration (text )
24062428 ):
24072429 return [], [], [], []
24082430
@@ -2418,13 +2440,10 @@ def _unsupported_declaration_diagnostic(self, segment: CTopLevelSegment) -> CDia
24182440 if not text :
24192441 return None
24202442
2421- kind = "unsupported_declaration "
2422- message = "Unsupported C declaration form. "
2443+ kind = ""
2444+ message = ""
24232445
2424- if _looks_like_cxx_declaration (text ):
2425- kind = "cxx_declaration"
2426- message = "C++ declaration syntax is not supported by the C parser."
2427- elif text .startswith ("struct " ):
2446+ if text .startswith ("struct " ):
24282447 kind = "struct_definition"
24292448 message = "Struct definitions are not supported yet."
24302449 elif text .startswith ("union " ):
@@ -2445,6 +2464,8 @@ def _unsupported_declaration_diagnostic(self, segment: CTopLevelSegment) -> CDia
24452464 elif "{" in text or "}" in text :
24462465 kind = "brace_declaration"
24472466 message = "Unsupported declaration containing braces."
2467+ else :
2468+ return None
24482469
24492470 return CDiagnostic (
24502471 code = "C_UNSUPPORTED_DECLARATION" ,
@@ -2521,12 +2542,10 @@ def _parse_translation_unit(
25212542 )
25222543 )
25232544 continue
2524- if _looks_like_cxx_declaration (segment .text ):
2525- unsupported = self ._unsupported_declaration_diagnostic (segment )
2526- if unsupported is not None :
2527- diagnostics .append (unsupported )
2528- continue
2529- tag_definition = self ._parse_tag_definition (segment )
2545+ try :
2546+ tag_definition = self ._parse_tag_definition (segment )
2547+ except _InvalidCGrammarSyntax as error :
2548+ raise self ._invalid_syntax_error (segment , str (error ), context = "nested declaration" ) from None
25302549 if tag_definition is not None :
25312550 aggregate , parsed_functions , parsed_typedefs , parsed_variables , parsed_diagnostics = tag_definition
25322551 if isinstance (aggregate , CStruct ):
@@ -2551,6 +2570,8 @@ def _parse_translation_unit(
25512570 except _UnsupportedDeclaratorSyntax as error :
25522571 diagnostics .append (self ._declarator_diagnostic (segment , str (error )))
25532572 continue
2573+ except _InvalidCGrammarSyntax as error :
2574+ raise self ._invalid_syntax_error (segment , str (error ), context = "function declaration" ) from None
25542575 if function is not None :
25552576 function .condition_set = condition_sets .get (
25562577 segment .original_start_line ,
@@ -2562,17 +2583,21 @@ def _parse_translation_unit(
25622583 unsupported = self ._unsupported_declaration_diagnostic (segment )
25632584 if unsupported is not None :
25642585 diagnostics .append (unsupported )
2565- continue
2586+ continue
2587+ raise self ._invalid_syntax_error (segment , segment .text , context = "top level" )
25662588 forward_tag = self ._forward_tag (segment )
25672589 if isinstance (forward_tag , CStruct ):
25682590 structs .append (forward_tag )
25692591 continue
25702592 if isinstance (forward_tag , CUnion ):
25712593 unions .append (forward_tag )
25722594 continue
2573- parsed_functions , parsed_typedefs , parsed_variables , declarator_diagnostics = self ._parse_declaration (
2574- segment
2575- )
2595+ try :
2596+ parsed_functions , parsed_typedefs , parsed_variables , declarator_diagnostics = self ._parse_declaration (
2597+ segment
2598+ )
2599+ except _InvalidCGrammarSyntax as error :
2600+ raise self ._invalid_syntax_error (segment , str (error ), context = "declaration" ) from None
25762601 functions .extend (parsed_functions )
25772602 for function in parsed_functions :
25782603 function .condition_set = condition_sets .get (
@@ -2593,6 +2618,8 @@ def _parse_translation_unit(
25932618 unsupported = self ._unsupported_declaration_diagnostic (segment )
25942619 if unsupported is not None :
25952620 diagnostics .append (unsupported )
2621+ else :
2622+ raise self ._invalid_syntax_error (segment , segment .text , context = "top level" )
25962623
25972624 return functions , structs , unions , enums , typedefs , variables , diagnostics
25982625
0 commit comments