~sebsite/hare-c

C lexer/parser/type-checker for Hare
parse: fix a fucking awful gen bug holy shit lmfao
parse+test: also run tests with c2y
check tests: improve first struct test

refs

main
browse  log 

clone

read-only
https://git.sr.ht/~sebsite/hare-c
read/write
git@git.sr.ht:~sebsite/hare-c

You can also use your local clone with git send-email.

#hare-c

C parsing and checking library for Hare

A sprite in Scratch of a hare, named "Hare-c"

#Stuff in this repo

  • c - the c:: Hare module
  • cmd/cdecl - WIP-ish converter from C gibberish to readable English, and vice-versa. depends on madeline
  • cmd/crepl - C interpreter/REPL. depends on madeline
  • cmd/hacc - C compiler. depends on qbe
  • cmd/hareconv - converter between C headers and Hare modules
  • docs - man pages and implementation documentation

#hi pls contribute

Send patches to ~sebsite/hare-c@lists.sr.ht (archive)

If you need help or have questions ping me on fedi or IRC or wherever :)

#Checklist

This checklist isn't exhaustive; I'm updating it as I go.

Legend:
 🗸  implemented
T   properly tested
X   currently can't be tested (e.g. runtime behavior)

   basics
T🗸   comments
T🗸     block
T🗸     line
T🗸   backslashes at end of line
     declarations
T🗸     must declare at least one identifier
       specifiers
         type specifiers
T🗸         signed, unsigned
T🗸           parse
T🗸         void
T🗸           parse
T🗸           incomplete type; cannot be completed
T🗸         char
T🗸           parse
T🗸         short
T🗸           parse
T🗸         int
T🗸           parse
T🗸         long
T🗸           parse
T🗸         float
T🗸           parse
T🗸         double
T🗸           parse
T🗸         long double
T🗸           parse
           struct/union
T🗸           struct
T🗸             declarations
T🗸               named
T🗸                 parse
T🗸                 check
T🗸               anonymous
T🗸                 parse
T🗸                 check
T🗸               definitions
T🗸                 parse
T🗸                 check
T🗸               forward
T🗸                 parse
T🗸                 check
 🗸           union
 🗸             declarations
 🗸               named
T🗸                 parse
 🗸                 check
 🗸               anonymous
T🗸                 parse
 🗸                 check
 🗸               definitions
T🗸                 parse
 🗸                 check
 🗸               forward
T🗸                 parse
 🗸                 check
             bit-fields
T🗸             parse
T🗸               named
T🗸               anonymous
               check
                 can't take address
                 width requirements
                   integer constant expression
                   non-negative
                     may be zero
                   less than or equal to width of type
                 width must be integer constant expression
                 width must be non-negative
                 may or may not have declarator if width > 0
                 zero width
                   declarator must be absent
                   causes following bit-fields to not be packed into the previous bit-field's unit
                 adjacent bit-fields are packed into the same union
                   true for both structs and unions
                 int is signed
T🗸           incomplete types disallowed
T🗸           must contain at least one named member
T🗸           variably modified member types disallowed
T🗸           tag inserted into scope immediately after it's declared
             tag is always declared when not used as type specifier
T🗸           when used as type specifier, tag is only declared if no other declaration is visible
T🗸           duplicate fields disallowed
T🗸             fields outside of nested structs/unions
T🗸             including fields from nested structs/unions
T🗸           fields may not have function type
             tag is inserted into scope immediately after declared
               definition for same struct/union can't appear within struct/union definition body
           enum
T🗸           declarations
T🗸             named
T🗸             anonymous
T🗸             definitions
T🗸             forward disallowed
T🗸           members inserted into scope
T🗸             parse
T🗸             no linkage
T🗸               can't be redeclared
T🗸             not lvalue
T🗸               can't take address
T🗸             inserted into scope immediately after identifier
T🗸           tag inserted into scope immediately after definition ends
T🗸           compatible type
T🗸             implicit enumerated type
T🗸               type is unsigned int if all members are representable
T🗸               otherwise, type is int
T🗸             enumeration member type is int
T🗸           enum type is compatible with, but not equivalent to, enumerated type
 🗸           error if any value would overflow enumeration member type
 🗸             with initializer
 🗸             without initializer
T🗸           duplicate fields disallowed
T🗸             enum member redeclares existing non-enum declaration
T🗸             enum member redeclares existing different enum declaration
T🗸             enum member redeclares member of same enum
T🗸           enum type is incomplete until definition ends
T🗸         typedefs
T🗸           parse
T🗸           inserted into scope
T🗸           file scope
T🗸             disallowed for variably modified types
T🗸             allowed otherwise
T🗸           block scope
T🗸             variably modified types allowed
T🗸             all other types allowed
T🗸       storage-class specifiers
T🗸         auto
T🗸           parse
T🗸         static
T🗸           parse
T🗸         register
T🗸           parse
T🗸           forbids taking address
T🗸             explicitly
T🗸             implicitly (e.g. array -> pointer conversion)
T🗸             also applies to derivative lvalues
T🗸         extern
T🗸           parse
T🗸           defaults to external linkage
T🗸           uses linkage of identifier visible in scope when it's internal or external
T🗸         typedef
T🗸           parse
T🗸           inserts type into scope
T🗸           can't have initializer
T🗸         at most one storage-class specifier per declaration (sans thread_local)
T🗸         duplicates disallowed
T🗸       duplicate specifiers
T🗸         disallowed for type specifiers
T🗸         allowed for other specifiers
T🗸       disallow incompatible
T🗸         type specifiers
T🗸         other specifiers
T🗸     qualifiers
T🗸       allowed in any order
T🗸       duplicates allowed
       declarators
T🗸       pointer
         function
           parameters
T🗸           named
T🗸           anonymous
X🗸           arrays converted to (qualified) pointers
X🗸             still checked as arrays
X🗸           functions converted to function pointers
T🗸           may not declare more than one (non-tag) identifier
T🗸           may not be initialized
T🗸           without storage-class specifier
T🗸             defaults to implicit auto
T🗸           register storage-class specifier
T🗸             parse
T🗸             declares with register storage
T🗸             ignored for non-definitions
T🗸               may be different in compatible types
T🗸             applies even when other declarations' parameters have different/absent storage-class specifiers
T🗸           all other specifiers disallowed
T🗸           empty parameter list
T🗸             designates unspecified number of parameters
T🗸             isn't compatible with variadic function types
T🗸           treated as unqualified when taking composite type
             inserted into scope immediately after declared
               can be used in declarators of subsequent parameters
               can be used by previous parameters in k&r declarations
T🗸           warn when parameter is void
T🗸         return type requirements
T🗸           may not return array
T🗸           may not return function
T🗸         warn when function returning complete type doesn't have return statement
T🗸           except for main function in hosted environment
T🗸           all other functions
T🗸         storage-class specifier must be static or extern (explicit or otherwise)
T🗸         error out when explicitly given qualifiers (via typedef)
T🗸         implicit const qualifier
T🗸         qualifiers discarded for return type
T🗸       array
T🗸         without qualifiers
T🗸         qualifiers apply to element type
T🗸           don't apply to array itself before c23
T🗸         ...except as inner-most declarator of function parameter
T🗸           different from qualifying an array typedef or typeof
T🗸         qualifiers may only be in square brackets in inner-most declarator of function parameter
T🗸         if present, known size must be greater than zero
T🗸         element type must be complete object type
T🗸       identifier only
T🗸       parenthesized
T🗸       type names
T🗸         with declarator
T🗸         without declarator
T🗸     scopes
T🗸       file
T🗸         parse
T🗸         auto/register specifiers disallowed
T🗸         default storage
T🗸           extern for functions
T🗸           external linkage for objects
T🗸       block
T🗸         parse
T🗸         default storage
T🗸           static for functions (TODO: double check this)
T🗸           auto for objects
T🗸       duplicates
T🗸         file scope
T🗸           allowed for compatible declarations
T🗸             composite type
T🗸             compatible specifiers
T🗸           disallowed for incompatible declarations
T🗸           at most one definition allowed
T🗸         block scope
T🗸           disallowed within same scope
T🗸           shadowing in nested scopes
T🗸             shadowing block scoped declarations
T🗸             shadowing file scoped declarations
T🗸         struct/union/enum tags must always refer to same type
T🗸       unique namespaces
T🗸         identifiers
T🗸         struct/union/enum
T🗸         goto labels
T🗸           function scope
T🗸     internally-linked declarations must be complete by end of translation unit
T🗸     translation unit must not be empty (static assertions and empty declarations are ok)
       internally-linked declaration must be initialized if used
         except sizeof
         except alignof
         all other expressions
T🗸     can't declare an object with type void
       warnings
         internally-linked declaration is never used
         internally-linked function is never defined
     expressions
 🗸     constants and literals
T🗸       numbers
T🗸         preprocessing numbers
T🗸         integer constants
T🗸           bases
T🗸             decimal
T🗸             hex
T🗸             octal
T🗸           suffixes
T🗸             u
T🗸             l
T🗸           parse value
T🗸         floating constants
T🗸           decimal
T🗸           exponent
T🗸           suffixes
T🗸             f
T🗸             l
T🗸           parse value
T🗸       character constants
T🗸         plain
T🗸         L prefix
 🗸       string literals
T🗸         plain
T🗸         L prefix
T🗸         string concatenation
T🗸           works for same-prefix strings
T🗸           disallowed for strings with different prefixes
T🗸         trigraph-like character sequences are escaped when unparsed (prior to c23)
 🗸         may be invalid utf-8
       sizeof
T🗸       parse
T🗸         type
T🗸         expression
T🗸           unambiguous expressions
T🗸           unparenthesized compound literal
X🗸       operand isn't evaluated if not variably modified
X🗸       operand is evaluated if variably modified
         operand must not be bit-field
T🗸       operand must be complete object
T🗸     array indexing
T🗸       parse
T🗸     struct/union field accessing
T🗸       forms (parse)
T🗸         a.b
T🗸         a->b
T🗸       check
T🗸     unary
T🗸       postfix
T🗸         parse
T🗸       prefix
T🗸         parse
T🗸         &
T🗸           compile-time
T🗸           runtime
T🗸           operand is lvalue
T🗸           operand is function designator
T🗸           operand is unary *
T🗸             allowed when explicit
T🗸             allowed when implicit from array indexing
X🗸             neither & nor * is evaluated
T🗸             constraints still apply
T🗸           everything else disallowed
T🗸       ++ and --
X🗸         equivalent to += 1
T🗸         ...except operand can't have a complex type
T🗸     binary
T🗸       parse
T🗸     assignment
T🗸       parse
T🗸       simple assignment
T🗸       compound assignment
X🗸         operands are only evaluated once
X🗸         computed value is computed equivalently to binary expression
T🗸         result of binary expression must be assignable to first operand's type
T🗸           second operand can't be a pointer for addition or subtraction
T🗸           everything else is fine
T🗸       first operand must be a mutable lvalue
T🗸       first operand must not be an array
T🗸     parenthesized
T🗸     ternary
T🗸     operator precedence
       conversion
T🗸       explicit (cast expressions)
T🗸         parse
T🗸         extends implicit type conversion rules
T🗸         integer -> pointer
T🗸         pointer -> integer
T🗸         pointer -> pointer
T🗸           object -> object
T🗸           function -> function
T🗸           no limitations for qualifiers
T🗸         any -> void
T🗸         everything else disallowed (except object <-> function; see extensions)
         implicit
           integer promotion
T🗸           non-bit-fields
             bit-fields
               width taken into account
               bit-precise integers don't change, regardless of width
T🗸         lvalue conversion
           types
T🗸           integer -> float
T🗸           float -> float
T🗸           integer -> integer
T🗸           float -> integer
T🗸           pointer -> bool
T🗸           array -> pointer
T🗸           function -> pointer
T🗸           pointer -> pointer
T🗸             pointer -> compatible pointer
T🗸             object pointer -> void pointer
T🗸             void pointer -> object pointer
T🗸             can't implicitly convert to any other pointer
T🗸             qualifiers may be added
T🗸             qualifiers may not be removed
             struct/union -> same struct/union
T🗸             tagged
               untagged
T🗸           integer -> pointer
T🗸             allowed when converting from integer constant 0
T🗸             disallowed otherwise
T🗸           nullptr -> pointer
T🗸           nullptr -> bool
T🗸           nullptr -> nullptr
T🗸           null pointer constant -> pointer
T🗸           null pointer constant -> nullptr
T🗸           everything else disallowed
T🗸     function call
T🗸       parse
T🗸       check
T🗸       arguments must have complete type
     constant expressions
T🗸     disallowed (sub-)expressions
T🗸       assignment
T🗸       increment/decrement
T🗸       array indexing
T🗸       arrow expression (->)
T🗸       unary *
T🗸         including as operand of unary &
T🗸       function call
T🗸       comma expression
T🗸       __builtin_va_arg
X🗸       __builtin_va_copy
X🗸       __builtin_va_end
X🗸       __builtin_va_start
       must evaluate to value which is representable in its type
       struct/union field access
 🗸       allowed if constexpr
         disallowed if not constexpr
         result is also constexpr
         result is named constant if struct/union object is named constant
 🗸     enum constant
T🗸     pre-defined constant
T🗸       true
T🗸       false
T🗸       nullptr
 🗸     constexpr object
       integer constant expressions
 🗸       top-most expression may be anything not generally disallowed, but see limitations
 🗸       result must be integer type
         limitations for operands
T🗸         integer constant
 🗸         named constant with integer type
           constexpr compound literal with integer type
             non-constexpr compound literal disallowed
T🗸         character constant
T🗸         translation-time sizeof
T🗸         alignof
T🗸         __builtin_offsetof
           cast
             additional expressions allowed as immediate operand
 🗸             floating-point constant
 🗸             named constant with arithmetic type
               constexpr compound literal with arithmetic type
                 non-constexpr compound literal disallowed
           everything else disallowed, unless not evaluated (sizeof/alignof)
             sizeof on variably modified type disallowed (since it's evaluated)
       arithmetic constant expressions
         TODO
T🗸     constant expression in initializer
T🗸     null pointer constant
T🗸       integer constant expression which evaluates to zero
T🗸       ...casted to void *
T🗸         unqualified
T🗸         qualified
T🗸         void can't be qualified
T🗸       doesn't otherwise affect type conversion rules
 🗸     integer promotion
 🗸       unsigned -> unsigned
 🗸       unsigned -> signed
 🗸       signed -> unsigned
 🗸       signed -> signed
 🗸       char's signedness is taken into account
T🗸     additional errors
T🗸       don't error when not evaluated
T🗸         including because of short-circuiting
T🗸         expression which isn't evaluated need not be constant
T🗸       division by zero
T🗸         integer division
T🗸         integer modulus division
T🗸         floating-point is ok
T🗸       bit-shift right operand exceeds integer width
T🗸       signed integer overflow
T🗸         addition
T🗸         subtraction
T🗸         multiplication
T🗸         division
T🗸         modulo
T🗸         left shift
T🗸         negation
 🗸     TODO: write more shit here i guess
     statements
T🗸     goto
T🗸       parse
T🗸       jump to any label within function
T🗸       disallow undefined labels
T🗸       may not jump from outside scope of VMT to inside scope
T🗸     compound
T🗸       parse
       labelled
T🗸       goto label
T🗸         parse
T🗸         unique per-function
         case
T🗸         parse
T🗸         must evaluate to integer constant
           case type is converted to type of controlling expression
           warn if constant value is truncated
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         each case in switch body is unique
T🗸         ...but nested switch statements may contain duplicates
T🗸       default
T🗸         parse
T🗸         must only appear in switch body
T🗸           direct descendant
T🗸           within another block
T🗸           disallowed everywhere else
T🗸           only visible to inner-most switch body
T🗸         at most one per switch body
T🗸         ...but nested switch statements may contain their own
T🗸       labelled statements may themselves be labelled
T🗸     empty
T🗸       parse
T🗸     if
T🗸       parse
T🗸       condition must be scalar
T🗸     while
T🗸       parse
T🗸       condition must be scalar
T🗸     do-while
T🗸       parse
T🗸       condition must be scalar
T🗸     for
T🗸       parse
T🗸       condition must be scalar (if present)
T🗸       initializer, condition, and afterthought may all be omitted
       switch
T🗸       parse
T🗸       value must be integer
X🗸       value is promoted
T🗸       no VMTs not in scope of switch statement may be in scope of any cases
         warnings
T🗸         no cases in body
           contains unreachable code
T🗸     break
T🗸       parse
T🗸       allowed in loop
T🗸       allowed in switch statement
T🗸       disallowed everywhere else
X🗸       causes jump to outside whatever it applies to
T🗸         within check
X🗸         for-loop afterthought isn't evaluated
T🗸     continue
T🗸       parse
T🗸       allowed in loop
T🗸       disallowed everywhere else
T🗸       causes jump to end of loop it applies to
T🗸         within check
X🗸         so for-loop afterthought is evaluated, then condition
T🗸     return
T🗸       parse
T🗸       type of return value must be convertible to function return type
T🗸       function with void result type can't return an expression
T🗸         non-void expression
T🗸         void expression
T🗸       returning without expression from function with non-void result type
T🗸         warn before c99
T🗸         error after c99
T🗸       warn when used in noreturn function
T🗸     expression statements
T🗸       parse
T🗸       implicitly convert to void
T🗸     bindings
T🗸       TODO: write more shit here (stuff handled in fromabinding)
T🗸       enters scope before initializer is checked
 🗸   initializers
T🗸     parse
T🗸     non-compound
T🗸       without braces
T🗸       may be surrounded by braces
T🗸         when single scalar object
T🗸         when using string literal to initialize array
 🗸     compound initializers
T🗸       parse
T🗸       check
T🗸         array literals
T🗸           complete array type
T🗸           incomplete array type
T🗸             set length to length of literal
T🗸         struct literals
T🗸         union literals
T🗸       can be nested
 🗸       must always consist of constant expressions prior to c99
 🗸         braced scalar initializer need not be constant
T🗸     must be constant expression for objects with non-automatic storage duration
T🗸     may be any expression otherwise (except for compound initializer prior to c99)
T🗸     string literal may initialize array
T🗸       allowed when array length >= sizeof(string) - 1
T🗸       disallowed when array length < sizeof(string) - 1
T🗸       for incomplete array type, length defaults to sizeof(string)
X🗸       excess array elements are initialized to zero (nul)
T🗸   variadic functions
T🗸   function definitions
T🗸     declaration inserted into scope
T🗸       before body is parsed
T🗸       before body is checked
T🗸     declarator must be function
T🗸       non-pointer accepted
T🗸       pointer rejected
T🗸       typedef rejected
T🗸     without arguments (void)
T🗸     with named arguments
T🗸     argument types must be complete (sans (void) case)
T🗸     return type must be either complete type or void (in addition to other requirements)
T🗸   identifiers
T🗸   no such thing as an invalid token
T🗸   type size must be representable as size_t
T🗸     array
T🗸     struct
T🗸     union
     main function
 🗸     no requirements or restrictions imposed in freestanding environment
       hosted environment
 🗸       warn; don't error
         if __asm__("main") is given, no restrictions on the function's identifier are imposed
         TODO: main but with non-main __asm__?
T🗸       must be function
T🗸       must have strictly conforming prototype
T🗸         (void)
T🗸         (int, char **)
T🗸         ...or equivalent, expanding typedefs and performing usual parameter conversions
 🗸         all other forms rejected
 🗸       must have external linkage
T🗸       checked even if no definition is present
 🗸   environ declaration
 🗸     no restrictions imposed if not POSIX
 🗸     POSIX restrictions
T🗸       warn; don't error
 🗸       only apply restrictions when
 🗸         declaration has external linkage
T🗸         identifier is environ and __asm__ is absent
 🗸         ...or __asm__ is "environ"
 🗸         don't apply restrictions otherwise
 🗸       unqualified type must be compatible with char **
T🗸         no qualifiers present
 🗸         qualifiers present
 🗸         typedefs permitted
 🗸       by the end of translation, declaration should be extern
 🗸         can't have definition
   preprocessor
T🗸   macro definitions
T🗸     #define
T🗸       variable-like
T🗸         creates macro definition
T🗸       function-like
T🗸         creates macro definition
T🗸         duplicate parameters disallowed
T🗸       shadowing keywords
T🗸     undef
T🗸       shadowing keywords
T🗸       other identifiers
     macro substitution
T🗸     pre-defined macros
T🗸        standard
T🗸          constant
T🗸            __STDC__
T🗸            __STDC_HOSTED__
T🗸          special
T🗸            __FILE__
T🗸            __LINE__
X🗸            __DATE__
X🗸            __TIME__
T🗸        non-standard
T🗸          constant
T🗸            target-independent
T🗸              __HARE_C__
T🗸              __ORDER_LITTLE_ENDIAN__
T🗸              __ORDER_BIG_ENDIAN__
T🗸              __ORDER_PDP_ENDIAN__
T🗸              __STRICT_ANSI__
T🗸            target-dependent
T🗸              __BYTE_ORDER__
T🗸          special
T🗸            __BASE_FILE__
T🗸            __COUNTER__
T🗸            __INCLUDE_LEVEL__
T🗸            __FILE_NAME__
X🗸            __TIMESTAMP__
X🗸            __VERSION__
       operators
         # (%:)
 🗸         without whitespace
           whitespace is preserved, but collapsed into a single space
 🗸       ## (%:%:)
T🗸         with macro arguments
T🗸           only token closest to ## is concatenated
T🗸           when argument expands to no tokens, replace with placemarker
T🗸         with other tokens
 🗸         constructs new token
T🗸           non-preprocessing tokens
 🗸           constructed token won't be used for preprocessing (i.e. no # or ##)
 🗸     # has higher precedence than ##
T🗸     object-like
       function-like
T🗸       works when followed by left paren
T🗸       doesn't work when not followed by left paren
         parameters
T🗸         expand non-recursively
T🗸           expands macros
T🗸           everything else
T🗸         don't expand recursively
T🗸           from parameters
T🗸           from macros
           warn in c90 when argument expands to no tokens
T🗸     recursive
T🗸       macros being expanded can't be expanded again
T🗸         macro can't expand itself
T🗸         macro can't be expanded from within nested expansion
T🗸       everything else expands
     #include
T🗸     system headers
T🗸     non-system headers
T🗸       header exists
T🗸       header doesn't exist; fallback to system header
T🗸         header name doesn't have angle brackets
T🗸         header name has angle brackets
T🗸     macro expansion
T🗸       expands into system header
T🗸       expands into non-system header
T🗸       multiple macros may expand into one resource
T🗸     <this> is lexed as a system header string literal
T🗸       true in #include
T🗸       false everywhere else (prior to c23)
       warn if header name contains any of ', \, ", //, or /*
 🗸   conditional
T🗸     #if, #endif
T🗸     #else
T🗸     #elif
T🗸     defined
T🗸       non-parenthesized identifier
T🗸       paranthesized identifier
T🗸       error out when neither of the above forms is matched
T🗸     #ifdef
T🗸     #ifndef
 🗸     evaluation
T🗸       integer constant expressions
 🗸       other expressions disallowed
 🗸       keywords are treated as regular identifiers
T🗸       all arithmetic has intmax_t range
T🗸   #error
T🗸     errors out
X🗸     error message uses all tokens
X🗸     macros aren't expanded
     #warning
       actually warns
 🗸     warning message uses all tokens
 🗸     macros aren't expanded
 🗸     backported to all versions
T🗸   #line
T🗸     only change line number
T🗸     also change filename
T🗸     macros are expanded
T🗸     line number may not exceed 2147483647
   legacy (c90)
T🗸   trigraphs
     k&r-style functions
 🗸     empty parameter list
       non-empty parameter list without types
         TODO: allowed in c90 when k&r declarations aren't present?
       k&r-style parameter declarations
         parse
         only allowed in definitions
         every binding must correspond to a parameter
         allowed when parameter list doesn't have types
           empty parameter list
           non-empty parameter list without types
         disallowed when parameter list has types
           void
           more than zero parameters
         TODO: are duplicates allowed?
         declarations can reference parameters which come later (TODO: do they need to be declared first?)
         function type is unchanged (TODO: allowed when another declaration has types in parameter list?)
         parameter bindings are given the appropriate type within the function's scope
       warns
     implicit int
 🗸     implicit function declarations
 🗸       extern int ()
 🗸       happens when first operand of a call expression is an undeclared identifier
T🗸         unparenthesized
 🗸         can't be parenthesized
T🗸       warns
T🗸       inserted into current scope
       k&r parameter declarations may be omitted
         defaults to int
         warns
       type defaults to int when no type specifiers are present
         locations
 🗸         file scope
 🗸           declarations
 🗸             allowed with other specifiers or type qualifiers
T🗸             disallowed without other specifiers or type qualifiers
T🗸               including for non-definition function declarations
 🗸           function definitions
 🗸             allowed with other specifiers or type qualifiers
T🗸             allowed without other specifiers or type qualifiers
 🗸         block scope
 🗸           allowed with other specifiers or type qualifiers
 🗸           disallowed without other specifiers or type qualifiers
           k&r parameter
             allowed with other specifiers or type qualifiers
             disallowed without other specifiers or type qualifiers
 🗸         function parameter
 🗸           allowed with other specifiers or type qualifiers
 🗸           disallowed without other specifiers or type qualifiers
 🗸         struct/union field
 🗸           allowed with type qualifiers
 🗸           disallowed without type qualifiers
 🗸           without bit-field
 🗸           with bit-field
 🗸         type name
 🗸           allowed with type qualifiers
 🗸           disallowed without type qualifiers
T🗸       when ambiguous, identifier which is a typedef is treated as a type specifier
         warns
 🗸   implicit conversion
 🗸     pointer->integer
 🗸     integer->pointer
     pragma
T🗸     #pragma
T🗸     _Pragma
       STDC
T🗸       accept standardized
T🗸         FP_CONTRACT
T🗸         FENV_ACCESS
T🗸         CX_LIMITED_RANGE
X🗸       ignored (except for c23 additions)
T🗸       reject nonstandard
         valid locations
           file-scope, outside of declaration
 🗸           allowed
             error if contained within declaration, unless in block scope
             in effect until pragma state is changed or EOF
           beginning of compound statement, preceding all declarations and statements
 🗸           allowed
             multiple pragmas allowed; last one is used
             disallowed anywhere else in compound statement
             in effect for compound statement
             when compound statement ends, pragma state is restored to what it was prior to entering compound statement
           everywhere else disallowed
T🗸     implementation-defined
T🗸       accepted; ignored
 🗸     backported to c90
 🗸       standard pragmas still don't work until c99
     __has_include
       backported to all versions
T🗸 c95
T🗸   digraphs
T🗸   __STDC_VERSION__
   c99
     VLAs
T🗸     parse
T🗸     initialization
T🗸       can't be initialized by non-empty initializer
T🗸       can be initialized by empty initializer
       TODO: [*]
     hex floating constants
T🗸     prefix / base
       parse value
T🗸     exponent
       warn if value can't be losslessly represented in its type
     universal character names
T🗸     in character constants and string literals
T🗸       parse
T🗸     in identifiers
T🗸       parse
T🗸       c99: conform to annex D (different from c11 annex D)
T🗸       c11, c17: conform to annex D (different from c99 annex D)
T🗸       c23: follow same rules as regular unicode codepoints in identifiers
T🗸     disallowed: (<0xa0 && !='$' && !='@' && !='`') || (>=0xd800 && <0xe000)
       preserved when stringizing
T🗸   initializer designators
T🗸     array
T🗸     struct
     declaration as for-loop initializer
T🗸     parse
T🗸     must be auto or register
T🗸       default is auto
       must not declare new tag
         tag may still be declared within expression
 🗸     enter scope immediately after declared
T🗸   specifiers
T🗸     type specifiers
T🗸       _Complex
T🗸       _Imaginary
T🗸         errors out
T🗸       long long
       inline
T🗸       parse
T🗸       may appear more than once
T🗸       can't be used outside of function declaration
T🗸       applies to function declaration
T🗸       if inline is used on any declaration, there must be a definition
 🗸       internally-linked
 🗸         can be used on any declaration; no change in behavior
         externally-linked; inline definitions
 🗸         function becomes inline definition if all file scope declarations use inline
 🗸         ...and none explicitly use extern
 🗸         otherwise no change in behavior; function isn't an inline definition
 🗸         inline definition doesn't provide external definition
           constraints
             may not define a modifiable object with static or thread duration
             may not use identifier with internal linkage
T🗸     static array qualifier
T🗸       allowed in function parameter
T🗸       disallowed everywhere else
T🗸       length must be provided
T🗸   restrict
T🗸     parse
T🗸     restrictions on use (pun intended i think actually no it wasn't i lied)
T🗸       allowed on pointer to object type
T🗸       allowed on array of allowed type
T🗸       disallowed on non-array non-pointer types
T🗸       disallowed on pointer to function type
T🗸       disallowed on array of disallowed type
T🗸   variadic macros
T🗸     use of ... in definition
T🗸     __VA_ARGS__
T🗸       allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     argument list must be preceded by comma
T🗸   intermixing declarations and statements in compound statements
T🗸   ll integer constant suffix
T🗸   __func__
T🗸     parse
T🗸     resolves to name of current function
T🗸     can't be redeclared at top-level
T🗸     has type 'const char []'
 🗸   implicit return 0 from main
T🗸     for hosted targets
 🗸     not for freestanding targets
     incomplete array struct member
T🗸     allowed when it's the final member and other members are present
T🗸     disallowed otherwise
T🗸     not allowed in union
T🗸     struct type is still complete; size is as though incomplete array wasn't present
T🗸     but struct type can't be use in other structs or arrays
T🗸       including if it's (recursively) a member of a union
       initialization in struct literal
 🗸       can be initialized when object has static storage duration
         can't be initialized otherwise
T🗸   pre-defined macros
T🗸     __STDC_ISO_10646__
     compound initializers/literals
T🗸     compound literals
T🗸       parse
T🗸       check
T🗸       type can't be VLA
T🗸     designators
T🗸       parse
T🗸         none
T🗸         array
T🗸         struct
T🗸       check
T🗸         none
T🗸         array
T🗸         struct
T🗸     multiple designators per object element may be given
T🗸     designators may be combined
       duplicate designators
 🗸       final object overwrites all previous objects
         warns
       warn when multiple designators for the same union are given
         excluding embedded structs
   c11
     specifiers
T🗸     _Thread_local
T🗸       parse
T🗸       may not appear alongside auto or register
T🗸       may not appear in block scope when no storage-class specifiers are present
T🗸       may not be used on function declaration
T🗸       must appear on all declarations of an object
       _Noreturn
T🗸       parse
T🗸       may appear more than once
         applies to function declaration
T🗸       can't be used outside of function declaration
         information persists in check
         warnings
T🗸         warn when return type isn't void
           warn when called in an expression which isn't a statement
       _Alignas
 🗸       expression
 🗸       type
         duplicates permitted; most strict alignment used
         invalid uses
           alongside typedef/register specifier
           on function
           on bit-field
         must not specify less strict alignment than default
         redeclarations
           first declaration has alignment specifier
             redeclarations may omit alignment specifier
             redeclarations with alignment specifier must specify same alignment
             error when redeclarations explicitly specify different alignment
               even if effective alignment would be the same
           first declaration doesn't have alignment specifier
             redeclarations must also not have alignment specifier
       _Atomic
         as specifier
           parse
           type in parens must not be qualified
           type in parens must not be array or function
           type in parens must not be atomic
T🗸       as qualifier
T🗸         parse
T🗸       errors out in check
T🗸   _Static_assert
T🗸     parse
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸     eval
T🗸       top-level
T🗸       within function
T🗸       within struct/union
T🗸       errors out when condition is false
T🗸   _Alignof
T🗸     parse
X🗸     operand isn't evaluated, even if variably modified
T🗸     operand must be complete object
 🗸   _Generic
T🗸     parse
T🗸     controlling expression conversions
T🗸       lvalue conversion
T🗸       array -> pointer
T🗸       function -> function pointer
T🗸     must match exactly one case
T🗸       at most one compatible case
T🗸       at most one default case allowed
T🗸       if no compatible case, default case must be present
T🗸     each non-default case must specify a non-variably-modified object type
 🗸     warnings
 🗸       no warnings when operand is type
T🗸       operand is expression
T🗸         case is qualified
T🗸         case is array
T🗸   prefixes
T🗸     character constants
T🗸       u8-
T🗸       u-
T🗸       U-
T🗸     string literals
T🗸       u-
T🗸       U-
T🗸   nested anonymous structs/unions
T🗸   pre-defined macros
T🗸     __STDC_IEC_559__
T🗸     __STDC_UTF_16__
T🗸     __STDC_UTF_32__
   c23
T🗸   unicode
T🗸     identifiers (without universal character names)
T🗸     universal character names must be less than U+110000
     specifiers
 🗸     _BitInt
T🗸       signed
T🗸         parse
T🗸       unsigned
T🗸         parse
T🗸       wb integer constant suffix
T🗸       operand must be integer constant expression
T🗸       signed width must be >= 2
T🗸       unsigned width must be >= 1
T🗸       doesn't undergo integer promotion
 🗸       has rank below equivalently sized basic integer type
 🗸       otherwise ranked based on width of both types
       typeof
T🗸       qualified (typeof)
T🗸       unqualified (typeof_unqual)
T🗸       with expression argument
T🗸       with type name argument
X🗸       operand isn't evaluated if not variably modified
         operand is evaluated if variably modified
         operand must not be bit-field
T🗸     decimal types
T🗸       _Decimal32
T🗸         type specifier
T🗸         df suffix
T🗸       _Decimal64
T🗸         type specifier
T🗸         dd suffix
T🗸       _Decimal128
T🗸         type specifier
T🗸         dl suffix
       constexpr
 🗸       parse
         check
 🗸         type
T🗸           implicitly qualified as const
T🗸           must be object (not function)
T🗸           must not be variably modified
 🗸           must not be volatile or restrict qualified
T🗸             constraint applies to type itself
 🗸             constraint applies recursively to subtypes
           initializer
T🗸           must be provided
T🗸           must be constant expression
             constraints
               value must be exactly representable in type
T🗸               integer
                 floating-point type
                   binary
                     real
                     complex
                   decimal
T🗸             pointer initializer must be null pointer
T🗸             integer initializer must be integer constant expression
 🗸             arithmetic initializer must be arithmetic constant expression
T🗸             real float initializer must have integer or real float type
T🗸             decimal float initializer must have decimal float type
               constraints apply recursively to subobjects
     preprocessor
       #embed
         system embeds
         non-system embeds
           embed exists
           embed doesn't exist; fallback to system header
             embed name doesn't have angle brackets
             embed name has angle brackets
         parameters
           standard
             if_empty
             limit
             prefix
             suffix
           vendor-specific
             gnu::offset
             warn for other non-standard parameters
           duplicates not permitted
           leading+trailing underscores permitted
 🗸       macro expansion
 🗸         expands into system embed
 🗸         expands into non-system embed
 🗸         multiple macros may expand into one resource
 🗸         only expand if the unexpanded tokens have a syntax error
 🗸       <this> is lexed as a system header string literal
         `defined` token prohibited
         warn if header name contains any of ', \, ", //, or /*
       __VA_OPT__
         allowed in variadic macro definition
T🗸       disallowed everywhere else
T🗸     comma before variadic macro arguments can be omitted when there's no argument list
T🗸     #elifdef, #elifndef
       standard pragmas
         FENV_ROUND
 🗸         value must be direction
           eval
           information persists in check
         FENV_DEC_ROUND
 🗸         value must be dec-direction
           eval
           information persists in check
T🗸   static_assert without reason
T🗸   type inference with auto
T🗸     parse
T🗸     infers type
T🗸     initializer must be provided
T🗸     compound initializer disallowed
T🗸     array/function initializer is converted to pointer
T🗸     declaration inserted into scope immediately after declared, but can't be used
T🗸     can't be used on function definition
T🗸     must have exactly one declarator
T🗸     must be direct declarator
T🗸     can't be used alongside other storage-class specifiers
X🗸     can't be used alongside __auto_type
T🗸     can be used alongside qualifiers
T🗸     can be followed by attributes
T🗸     only usable on bindings
T🗸       not function parameters
T🗸       not struct/union fields
T🗸       not type names
T🗸   nullptr
T🗸   labels
T🗸     labelled declarations
T🗸     at end of compound statement
T🗸   binary integer constants
T🗸     parse value
T🗸   function declarations
T🗸     variadic function without parameters
T🗸     parameters in function definition need not be named
     attributes
       locations
T🗸       declaration
T🗸         top-level
T🗸           bindings
T🗸           function definitions
T🗸           as a declaration consisting of only attributes and nothing else
T🗸         within compound body
T🗸         for-loop initializer
T🗸         function parameter
T🗸         struct/union fields
T🗸         attribute declaration
T🗸           attribute list followed by semicolon, as though null statement
T🗸           only allowed where declarations are allowed
T🗸       binding
T🗸       base type
T🗸       declarator
T🗸         array
T🗸         function
T🗸         pointer
T🗸         with identifier
T🗸         without identifier
         struct/union/enum declaration
T🗸         parse
           allowed when defining struct/union/enum
           allowed in tag declaration where struct/union doesn't act as type specifier
             doesn't apply to enums since they can't be forward declared
           disallowed otherwise
T🗸       enum field
T🗸       statement
T🗸         non-null statements
T🗸         can't be on null statement (that makes it an attribute declaration)
       standard
         attributes
T🗸         [[noreturn]], [[_Noreturn]]
T🗸           disallow argument
T🗸           applicable to
T🗸             function declaration
T🗸             nothing else
T🗸           behaves equivalently to _Noreturn
           [[deprecated]]
             may have optional argument
             applicable to
               struct/union declaration
               typedef declaration
               object declaration
               struct/union member
               function declaration
               enum
               enum member
               nothing else
             warn when name is used
           [[fallthrough]]
T🗸           disallow argument
             applicable to
T🗸             attribute declaration
               nothing else
T🗸           must only appear in switch body
 🗸           next encountered statement must have case or default label
 🗸           next encountered statement must appear within same switch body
 🗸           if within iteration statement, next statement must also be within said iteration statement
           [[maybe_unused]]
             disallow argument
             applicable to
               struct/union declaration
               typedef declaration
               object declaration
               struct/union member
               function declaration
               enum
               enum member
               label
               nothing else
           [[nodiscard]]
             may have optional argument
             applicable to
               function declaration
               struct/union definition
               enum definition
               nothing else
             warn when value discarded
           [[reproducible]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
           [[unsequenced]]
             disallow argument
             applicable to
               function declarator
               type specifier with function type
             warn when returns void
T🗸       __has_c_attribute => 202311L
       non-standard
         warn
T🗸       __has_c_attribute => 0L
T🗸     leading+trailing underscores permitted
T🗸     keywords are treated as identifiers
T🗸     with prefix
T🗸     without prefix
T🗸     without arguments
T🗸     with arguments
T🗸       including balanced tokens
T🗸     multiple attribute lists are combined
T🗸     multiple attributes in [[brackets]]
T🗸   u8- string literals
T🗸   empty initializers
T🗸     parse
T🗸     scalar types
T🗸     array types
T🗸       allowed for complete array types
T🗸       disallowed for incomplete array types
T🗸         as initializer
T🗸         in compound literal
T🗸     struct types
T🗸     union types
T🗸   ' as separator
T🗸     integer constants
T🗸     floating constants
T🗸   empty declarations
     pre-defined macros
       keyword aliases
 🗸       alignas
T🗸       alignof
T🗸       bool
T🗸       true
T🗸         defined
T🗸         expands to _Bool value
T🗸       false
T🗸         defined
T🗸         expands to _Bool value
T🗸       static_assert
T🗸       thread_local
         when equivalent keyword is re-defined, alias macro doesn't expand said definition (TODO: maybe just don't bother with this?)
T🗸     __has_c_attribute
       __has_embed
T🗸     constants
T🗸       __STDC_EMBED_NOT_FOUND__
T🗸       __STDC_EMBED_FOUND__
T🗸       __STDC_EMBED_EMPTY__
     enum
       explicit enumerated type
T🗸       parse
         check
T🗸         explicitly specified type is used as enumerated type
T🗸         type constraints
T🗸           must be integer type
T🗸           can't be bit-precise
T🗸           can't be enum
T🗸         qualifiers are discarded
T🗸         enum type is completed immediately after explicitly specified type is parsed
T🗸         forward declarations
T🗸           allowed when not used as type specifier (i.e. nothing else is declared)
T🗸           disallowed when used as type specifier
           multiple declarations of the same enum tag
             explicit enumerated type must be provided by either all declarations or none
               explicit enumerated type need not be (and can't be) provided when enum without definition is used as type specifier
T🗸           the enumerated types must all be compatible
T🗸         error if value would be implicitly truncated
T🗸           with initializer
T🗸           without initializer
T🗸     other type additions/changes
T🗸       implicit enumerated type
T🗸         order is (un)signed int, (un)signed long, (un)signed long long
T🗸       enumeration member type
T🗸         with implicit enumerated type
T🗸           within enum definition (each individual member)
T🗸             type is int if representable
T🗸             otherwise, if there's an initializer, type is that of the initializer
T🗸             otherwise, type is basic integer type with the same signedness as the previous member, whose rank is the minimum which isn't smaller than that of the previous member but which can represent the new implicit value
T🗸               error if no suitable type exists
T🗸           after enum is defined (all members collectively)
T🗸             type is int if all representable
T🗸             otherwise, type is the same as the enumerated type
T🗸         with explicit enumerated type
T🗸           type is the same as the enumerated type
T🗸             within enum definition
T🗸             after enum is defined
T🗸   storage-class specifiers in compound literals
T🗸     allowed
T🗸     disallowed for cast expressions
T🗸   treat empty parameter list as identical to (void)
 🗸 c2y
 🗸   unary increment/decrement operand can have complex type
 🗸   _Generic with type operand
T🗸     parse
 🗸     doesn't undergo lvalue or pointer conversion
 🗸     cases can be function types
 🗸     cases can be incomplete
 🗸     cases still can't be variably modified
 🗸   alignof can take alignment of incomplete array type
 🗸   pre-defined macros
 🗸     __STDC_IEC_60559_COMPLEX__
T🗸   _Imaginary is no longer a keyword
   extensions
     preprocessor
       redefinition of macros
T🗸       user-defined
T🗸       pre-defined
         warns when new definition isn't identical to old
           doesn't warn when new definition is identical
X🗸     __DATE__ and __TIME__ use SOURCE_DATE_EPOCH if set
T🗸     __has_attribute
       linemarkers
     builtin identifiers
T🗸     can't be redeclared at top-level
       __builtin_offsetof
T🗸       parse
         check
           can't be used on bit-fields
T🗸         can be used on non-bit-fields
       __builtin_pp_embed
T🗸       parse
         check
         warnings and errors
           error when type isn't a character type
       __builtin_unreachable
T🗸       parse
T🗸       persists in check
         warnings and errors
           warn when used as an expression
T🗸     __builtin_va_arg
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         when type name can't be made a pointer by postfixing it with *
T🗸           warn before c23
T🗸           no diagnostic since c23
T🗸         error when type name is an array type or function type
T🗸         error when type name is an incomplete type
T🗸         error when first argument isn't a va_list
T🗸     __builtin_va_copy
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         error when arguments aren't both va_lists
T🗸     __builtin_va_end
T🗸       parse
X🗸       check
T🗸       warnings and errors
T🗸         error when argument isn't a va_list
T🗸     __builtin_va_list
T🗸       parse
X🗸       check
       __builtin_va_start
 🗸       parse
 🗸         second argument must be identifier before c23
 🗸         second argument must be identifier or integer constant 0 after c23
X🗸       check
         warnings and errors
T🗸         error when function isn't variadic
T🗸         last named parameter has register storage-class specifier
T🗸           warn before c23
T🗸           no diagnostic since c23
           TODO: last named parameter is declared as array type or function type?
T🗸         last named parameter's type is incompatible with type after default argument promotion
T🗸           disallowed types
T🗸             integer type ranked lower than int
T🗸             float
T🗸           warn before c23
T🗸           no diagnostic since c23
           optional argument isn't the last named function parameter
             error before c23
             warn since c23
     __asm__
       declarations
T🗸       parse
T🗸         disallowed on function definition
T🗸         disallowed for struct/union fields
T🗸         no other restrictions
T🗸         empty string unparses correctly
T🗸         trigraph-like character sequences are escaped when unparsed (prior to c23)
         check
T🗸         allowed declarations
T🗸           internal or external linkage
T🗸           register storage-class
T🗸           disallowed otherwise
T🗸         empty symbol disallowed
T🗸         no other restrictions imposed on symbol
T🗸         duplicate declarations must agree on symbol (if present)
           TODO: what's the deal with main?
       statements
         basic
           outside function
 🗸           parse
             check
           inside function
 🗸           parse
             check
 🗸         string is interpreted literally, without % escapes
 🗸           don't check as format string
         extended
 🗸         parse
           check
 🗸         only allowed inside function
 🗸         double colons are separated into two single colons
 🗸         qualifiers
 🗸           goto
 🗸           inline (or __inline__)
 🗸           volatile
           % escapes in format string are checked
             %n (n = number)
               operands are numbered starting at zero
                 operands with symbolic names are also numbered
               error when no operand corresponds to the given number
             %[symbolic_name]
             %=
               replaced with number unique to __asm__ statement
             multiple assembler dialects
               {foo|bar|baz}
               may supply only one dialect
               can't be nested
               escapes within braces are recognized
             escape special characters
               %%
               %{
               %|
               %}
 🗸         output operands
 🗸           parse
 🗸           expression must be a mutable lvalue
 🗸         input operands
 🗸           parse
 🗸         clobbers
 🗸           parse
 🗸         goto labels
 🗸           parse
 🗸           all identifiers must be valid goto labels in current function scope
 🗸           at least one label must be provided
 🗸           labels can't be provided if goto qualifier isn't used
 🗸           labels must be provided if goto qualifier is used
 🗸         no two operands can share a symbolic name
           check constraint syntax
T🗸   casting between object pointer and function pointer
T🗸     errors
T🗸      when POSIX_C isn't used
T🗸      no diagnostic otherwise
X🗸     does the Right Thing
     __attribute__
 🗸     locations
 🗸       within specifier/qualifier list
 🗸         declaration
T🗸           parse
T🗸             binding
T🗸               file scope
T🗸               block scope
T🗸             function parameter
T🗸             struct/union field
T🗸             disallowed for static_assert
T🗸               file scope
T🗸               block scope
T🗸             allowed for [[attribute]] declaration
T🗸               file scope
T🗸               block scope
 🗸           always applies only here when ambiguous
T🗸           disallowed without other specifiers
T🗸             even prior to c99
T🗸         type name
T🗸           parse
T🗸           without other specifiers
T🗸             prior to c99
T🗸               allowed outside of function parameters
T🗸               disallowed for function parameters
T🗸             disallowed since c99
 🗸       binding
 🗸         before declarator
T🗸           disallowed on first declarator
 🗸           allowed for subsequent declarators
T🗸         after declarator
T🗸           parse
T🗸           must go after __asm__ if present
T🗸           disallowed in function definition
T🗸       struct/union/enum
T🗸         parse
T🗸           after struct/union/enum token
T🗸           after closing brace
T🗸             always applies only here when ambiguous
T🗸       enum field
T🗸       null statement
T🗸         parse
T🗸         only if not labelled
T🗸         must precede any standard attributes
T🗸         standard attributes only allowed where declaration would be allowed
T🗸       label
T🗸         parse
T🗸         disallowed with case and default
T🗸         always applies only here when ambiguous
T🗸       declarator
T🗸         locations within declarator
T🗸           after *
T🗸           parenthesized, before nested declarator
T🗸             parse
T🗸             `(__attribute__` always begins function declarator, not nested declarator
T🗸               even if it would parse if interpreted as nested declarator, still error out
T🗸           alongside qualifiers within array brackets
T🗸         application
T🗸           to declarator
T🗸           to base type
T🗸         parse
       warn for unrecognized attributes
       supported attributes
T🗸       noreturn, _Noreturn
T🗸         behaves equivalently to [[noreturn]]
         alias
         aligned
         always_inline
           error when argument used
           applicable to
             function declaration
             nothing else
           must be used alongside inline specifier
           information persists in check
         const
           behaves equivalently to [[unsequenced]]
           ...but also applicable to function declaration
         deprecated
           behaves equivalently to [[deprecated]]
 🗸       fallthrough
 🗸         behaves equivalently to [[fallthrough]]
 🗸           applicable to null statement
         format
           arguments
             must be exactly three
             archetype
               must be one of printf, scanf, strftime, or strfmon
             string-index
               must be integer constant (TODO?)
               must refer to `const char *` parameter (1-indexed) (TODO: unqualified `char *`?)
             first-to-check
               must be integer constant (TODO?)
               allowed values
                 index of valid (possibly variadic) parameter (1-indexed)
                 0
                   arguments aren't available to check
                 other values disallowed
           warnings
             invalid format string
               printf
               scanf
               strftime
               strfmon
             invalid arguments
         gnu_inline
           error when argument used
           applicable to
             function declaration
             nothing else
           must be used alongside inline specifier
           must appear in all declarations of same function
           causes behavior for no storage-class specifier / extern to be flipped
             INLINE if extern everywhere, otherwise STATIC_EXTERNAL_DEFN
         noinline
           error when argument used
           applicable to
             function declaration
             nothing else
           error when used with inline specifier
             in same declaration
             in other redeclaration
           information persists in check
         noipa
           error when argument used
           applicable to
             function declaration
             nothing else
           error when used with inline specifier
             in same declaration
             in other redeclaration
           information persists in check
         nonnull
         packed
           error when argument used
           applicable to
T🗸           struct
             struct field
             union
             union field
             nothing else
T🗸         check
T🗸           TODO: write shit here
         pure
           behaves equivalently to [[reproducible]]
           ...but also applicable to function declaration
         returns_nonnull
           error when argument used
           applicable to
             function declaration
             nothing else
           function must return pointer type
           warn if return statement in function returns null pointer constant or constant expression with value null
         section
         unavailable
           may have optional argument
           applicable to
             declaration
             nothing else
           error whenever the declaration is used
             don't error when redeclared
           don't emit(?)
         unused
           behaves equivalently to [[maybe_unused]]
         used
           error when argument used
           applicable to
             declaration
             nothing else
           persists in check
           silences unused declaration warning
         visibility
         weak
T🗸     attributes may also be used as c23 attributes with gnu:: prefix
T🗸     leading+trailing underscores permitted
T🗸   __inline__
T🗸     equivalent to inline, except allowed in all versions
T🗸   __typeof__
T🗸     equivalent to typeof, except allowed in all versions
T🗸   __typeof_unqual__
T🗸     equivalent to typeof_unqual, except allowed in all versions
T🗸   imaginary constants
T🗸     have complex type
T🗸     suffixes
T🗸       i
T🗸       j
T🗸     supported after c99
T🗸     not supported before c99
T🗸   __auto_type
T🗸     parse
T🗸     same semantics as c23 type inference in check, except allowed in all versions
T🗸     duplicate __auto_type specifiers disallowed
   additional warnings
     SOURCE_DATE_EPOCH is invalid
     unused internally-linked objects and functions
     statement follows statement which manipulates control flow or won't return
       statement being followed
T🗸       break
T🗸       continue
T🗸       goto
T🗸       return
         noreturn function call
T🗸       __builtin_unreachable()
T🗸       potentially labelled
T🗸       compound whose last non-unlabelled-null statement is any of these
T🗸       nothing else
       following statement
T🗸       warn if
T🗸         statement which isn't included in "don't warn if" below
T🗸         bindings or tag declaration
           fallthrough attribute declaration
T🗸           [[fallthrough]]
             __attribute__((fallthrough))
         don't warn if
T🗸         null statement
T🗸           warn for following statements which fit the requirements
T🗸         labelled statement
T🗸           don't warn for following statements
T🗸         __builtin_unreachable() expression
T🗸           continue to warn for following statements which fit the requirements
T🗸         static assertion
T🗸           warn for following statements which fit the requirements
           nonstandard (non-fallthrough) attribute declaration
T🗸           standard attributes
             gnu-style attributes
T🗸           don't warn for following statements