gcc源码阅读--语法解析

语法解析的开始函数:

/* Initialize the integrated preprocessor after debug output has been
   initialized; loop over each input file.  */
void
c_common_parse_file (void)
{
  auto dumps = g->get_dumps ();
  for (unsigned int i = 0;;)
    {
      c_finish_options ();
      /* Open the dump file to use for the original dump output
         here, to be used during parsing for the current file.  */
      dumps->dump_start (TDI_original, &dump_flags);
      pch_init ();
      push_file_scope ();
      c_parse_file ();
      pop_file_scope ();
      /* And end the main input file, if the debug writer wants it  */
      if (debug_hooks->start_end_main_source_file)
    (*debug_hooks->end_source_file) (0);
      if (++i >= num_in_fnames)
    break;
      cpp_undef_all (parse_in);
      cpp_clear_file_cache (parse_in);
      this_input_filename
    = cpp_read_main_file (parse_in, in_fnames[i]);
      /* If an input file is missing, abandon further compilation.
     cpplib has issued a diagnostic.  */
      if (!this_input_filename)
    break;
      dumps->dump_finish (TDI_original);
    }

  c_parse_final_cleanups ();
  dumps->dump_finish (TDI_original);
}

/* Parse a single source file.  */

void
c_parse_file (void)
{
  /* Use local storage to begin.  If the first token is a pragma, parse it.
     If it is #pragma GCC pch_preprocess, then this will load a PCH file
     which will cause garbage collection.  */
  c_parser tparser;

  memset (&tparser, 0, sizeof tparser);
  tparser.translate_strings_p = true;
  tparser.tokens = &tparser.tokens_buf[0];
  the_parser = &tparser;

  if (c_parser_peek_token (&tparser)->pragma_kind == PRAGMA_GCC_PCH_PREPROCESS)
    c_parser_pragma_pch_preprocess (&tparser);
  else
    c_common_no_more_pch ();

  the_parser = ggc_alloc<c_parser> ();
  *the_parser = tparser;
  if (tparser.tokens == &tparser.tokens_buf[0])
    the_parser->tokens = &the_parser->tokens_buf[0];

  /* Initialize EH, if we've been told to do so.  */
  if (flag_exceptions)
    using_eh_for_cleanups ();

  c_parser_translation_unit (the_parser);
  the_parser = NULL;
}

/* Parse a translation unit (C90 6.7, C99 6.9, C11 6.9).

   translation-unit:
     external-declarations

   external-declarations:
     external-declaration
     external-declarations external-declaration

   GNU extensions:

   translation-unit:
     empty
*/

static void
c_parser_translation_unit (c_parser *parser)
{
  if (c_parser_next_token_is (parser, CPP_EOF))
    {
      pedwarn (c_parser_peek_token (parser)->location, OPT_Wpedantic,
           "ISO C forbids an empty translation unit");
    }
  else
    {
      void *obstack_position = obstack_alloc (&parser_obstack, 0);
      mark_valid_location_for_stdc_pragma (false);
      do
    {
      ggc_collect ();
      c_parser_external_declaration (parser);
      obstack_free (&parser_obstack, obstack_position);
    }
      while (c_parser_next_token_is_not (parser, CPP_EOF));

    }

  unsigned int i;
  tree decl;
  FOR_EACH_VEC_ELT (incomplete_record_decls, i, decl)
    if (DECL_SIZE (decl) == NULL_TREE && TREE_TYPE (decl) != error_mark_node)
      error ("storage size of %q+D isn%'t known", decl);

  if (vec_safe_length (current_omp_declare_target_attribute))
    {
      c_omp_declare_target_attr
    a = current_omp_declare_target_attribute->pop ();
      if (!errorcount)
    error ("%qs without corresponding %qs",
           a.device_type >= 0 ? "#pragma omp begin declare target"
                  : "#pragma omp declare target",
           "#pragma omp end declare target");
      vec_safe_truncate (current_omp_declare_target_attribute, 0);
    }
  if (vec_safe_length (current_omp_begin_assumes))
    {
      if (!errorcount)
    error ("%qs without corresponding %qs",
           "#pragma omp begin assumes", "#pragma omp end assumes");
      vec_safe_truncate (current_omp_begin_assumes, 0);
    }

#if ENABLE_ANALYZER
  if (flag_analyzer)
    {
      ana::c_translation_unit tu;
      ana::on_finish_translation_unit (tu);
    }
#endif
}

c_parser_external_declaration (c_parser *parser)
{
  int ext;
  switch (c_parser_peek_token (parser)->type)
    {
    case CPP_KEYWORD:
      switch (c_parser_peek_token (parser)->keyword)
    {
    case RID_EXTENSION:
      ext = disable_extension_diagnostics ();
      c_parser_consume_token (parser);
      c_parser_external_declaration (parser);
      restore_extension_diagnostics (ext);
      break;
    case RID_ASM:
      c_parser_asm_definition (parser);
      break;
    case RID_AT_INTERFACE:
    case RID_AT_IMPLEMENTATION:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_class_definition (parser, NULL_TREE);
      break;
    case RID_AT_CLASS:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_class_declaration (parser);
      break;
    case RID_AT_ALIAS:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_alias_declaration (parser);
      break;
    case RID_AT_PROTOCOL:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_protocol_definition (parser, NULL_TREE);
      break;
    case RID_AT_PROPERTY:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_at_property_declaration (parser);
      break;
    case RID_AT_SYNTHESIZE:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_at_synthesize_declaration (parser);
      break;
    case RID_AT_DYNAMIC:
      gcc_assert (c_dialect_objc ());
      c_parser_objc_at_dynamic_declaration (parser);
      break;
    case RID_AT_END:
      gcc_assert (c_dialect_objc ());
      c_parser_consume_token (parser);
      objc_finish_implementation ();
      break;
    default:
      goto decl_or_fndef;
    }
      break;
    case CPP_SEMICOLON:
      pedwarn (c_parser_peek_token (parser)->location, OPT_Wpedantic,
           "ISO C does not allow extra %<;%> outside of a function");
      c_parser_consume_token (parser);
      break;
    case CPP_PRAGMA:
      mark_valid_location_for_stdc_pragma (true);
      c_parser_pragma (parser, pragma_external, NULL, NULL_TREE);
      mark_valid_location_for_stdc_pragma (false);
      break;
    case CPP_PLUS:
    case CPP_MINUS:
      if (c_dialect_objc ())
    {
      c_parser_objc_method_definition (parser);
      break;
    }
      /* Else fall through, and yield a syntax error trying to parse
     as a declaration or function definition.  */
      /* FALLTHRU */
    default:
    decl_or_fndef:
      /* A declaration or a function definition (or, in Objective-C,
     an @interface or @protocol with prefix attributes).  We can
     only tell which after parsing the declaration specifiers, if
     any, and the first declarator.  */
      c_parser_declaration_or_fndef (parser, true, true, true, false, true,
                     false);

      break;
    }
}

/* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
   6.7, 6.9.1, C11 6.7, 6.9.1).  If FNDEF_OK is true, a function definition
   is accepted; otherwise (old-style parameter declarations) only other
   declarations are accepted.  If STATIC_ASSERT_OK is true, a static
   assertion is accepted; otherwise (old-style parameter declarations)
   it is not.  If NESTED is true, we are inside a function or parsing
   old-style parameter declarations; any functions encountered are
   nested functions and declaration specifiers are required; otherwise
   we are at top level and functions are normal functions and
   declaration specifiers may be optional.  If EMPTY_OK is true, empty
   declarations are OK (subject to all other constraints); otherwise
   (old-style parameter declarations) they are diagnosed.  If
   START_ATTR_OK is true, the declaration specifiers may start with
   attributes (GNU or standard); otherwise they may not.
   OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed
   declaration when parsing an Objective-C foreach statement.
   FALLTHRU_ATTR_P is used to signal whether this function parsed
   "__attribute__((fallthrough));".  ATTRS are any standard attributes
   parsed in the caller (in contexts where such attributes had to be
   parsed to determine whether what follows is a declaration or a
   statement); HAVE_ATTRS says whether there were any such attributes
   (even empty).  If SIMPLE_OK, the construct can be a simple-declaration;
   in that case, the ';' is not consumed (left to the caller so that it
   can figure out if there was a simple-declaration or not), there must
   be an initializer, and only one object may be declared.  When SIMPLE_OK
   is true we are called from c_parser_selection_header.

   Returns the resulting declaration, if there was any with an initializer.

   declaration:
     declaration-specifiers init-declarator-list[opt] ;
     static_assert-declaration

   function-definition:
     declaration-specifiers[opt] declarator declaration-list[opt]
       compound-statement

   declaration-list:
     declaration
     declaration-list declaration

   init-declarator-list:
     init-declarator
     init-declarator-list , init-declarator

   init-declarator:
     declarator simple-asm-expr[opt] gnu-attributes[opt]
     declarator simple-asm-expr[opt] gnu-attributes[opt] = initializer

   simple-declaration:
     attribute-specifier-sequence[opt] declaration-specifiers declarator
       = initializer

   GNU extensions:

   nested-function-definition:
     declaration-specifiers declarator declaration-list[opt]
       compound-statement

   attribute ;

   Objective-C:
     gnu-attributes objc-class-definition
     gnu-attributes objc-category-definition
     gnu-attributes objc-protocol-definition

   The simple-asm-expr and gnu-attributes are GNU extensions.

   This function does not handle __extension__; that is handled in its
   callers.  ??? Following the old parser, __extension__ may start
   external declarations, declarations in functions and declarations
   at the start of "for" loops, but not old-style parameter
   declarations.

   C99 requires declaration specifiers in a function definition; the
   absence is diagnosed through the diagnosis of implicit int.  In GNU
   C we also allow but diagnose declarations without declaration
   specifiers, but only at top level (elsewhere they conflict with
   other syntax).

   In Objective-C, declarations of the looping variable in a foreach
   statement are exceptionally terminated by 'in' (for example, 'for
   (NSObject *object in array) { ... }').

   OpenMP:

   declaration:
     threadprivate-directive

   GIMPLE:

   gimple-function-definition:
     declaration-specifiers[opt] __GIMPLE (gimple-or-rtl-pass-list) declarator
       declaration-list[opt] compound-statement

   rtl-function-definition:
     declaration-specifiers[opt] __RTL (gimple-or-rtl-pass-list) declarator
       declaration-list[opt] compound-statement  */

static tree
c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,

                   bool static_assert_ok, bool empty_ok,
                   bool nested, bool start_attr_ok,
                   bool simple_ok,
                   tree *objc_foreach_object_declaration
                   /* = NULL */,
                   vec<c_token> *omp_declare_simd_clauses
                   /* = NULL */,
                   bool have_attrs /* = false */,
                   tree attrs /* = NULL_TREE */,
                   struct oacc_routine_data *oacc_routine_data
                   /* = NULL */,
                   bool *fallthru_attr_p /* = NULL */)

{
  struct c_declspecs *specs;
  tree prefix_attrs;
  tree all_prefix_attrs;
  bool diagnosed_no_specs = false;
  location_t here = c_parser_peek_token (parser)->location;
  tree result = NULL_TREE;

  add_debug_begin_stmt (c_parser_peek_token (parser)->location);

  if (static_assert_ok
      && c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT))
    {
      c_parser_static_assert_declaration (parser);
      return result;
    }
  specs = build_null_declspecs ();

  /* Handle any standard attributes parsed in the caller.  */
  if (have_attrs)
    {
      declspecs_add_attrs (here, specs, attrs);
      specs->non_std_attrs_seen_p = false;
    }

  /* Try to detect an unknown type name when we have "A B" or "A *B".  */
  if (c_parser_peek_token (parser)->type == CPP_NAME
      && c_parser_peek_token (parser)->id_kind == C_ID_ID
      && (c_parser_peek_2nd_token (parser)->type == CPP_NAME
          || c_parser_peek_2nd_token (parser)->type == CPP_MULT)
      && (!nested || !lookup_name (c_parser_peek_token (parser)->value)))
    {
      tree name = c_parser_peek_token (parser)->value;

      /* Issue a warning about NAME being an unknown type name, perhaps
     with some kind of hint.
     If the user forgot a "struct" etc, suggest inserting
     it.  Otherwise, attempt to look for misspellings.  */
      gcc_rich_location richloc (here);
      if (tag_exists_p (RECORD_TYPE, name))
    {
      /* This is not C++ with its implicit typedef.  */
      richloc.add_fixit_insert_before ("struct ");
      error_at (&richloc,
            "unknown type name %qE;"
            " use %<struct%> keyword to refer to the type",
            name);
    }
      else if (tag_exists_p (UNION_TYPE, name))
    {
      richloc.add_fixit_insert_before ("union ");
      error_at (&richloc,
            "unknown type name %qE;"
            " use %<union%> keyword to refer to the type",
            name);
    }
      else if (tag_exists_p (ENUMERAL_TYPE, name))
    {
      richloc.add_fixit_insert_before ("enum ");
      error_at (&richloc,
            "unknown type name %qE;"
            " use %<enum%> keyword to refer to the type",
            name);
    }
      else
    {
      auto_diagnostic_group d;
      name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME,
                          here);
      if (const char *suggestion = hint.suggestion ())
        {
          richloc.add_fixit_replace (suggestion);
          error_at (&richloc,
            "unknown type name %qE; did you mean %qs?",
            name, suggestion);
        }
      else
        error_at (here, "unknown type name %qE", name);
    }

      /* Parse declspecs normally to get a correct pointer type, but avoid
         a further "fails to be a type name" error.  Refuse nested functions
         since it is not how the user likely wants us to recover.  */
      c_parser_peek_token (parser)->type = CPP_KEYWORD;
      c_parser_peek_token (parser)->keyword = RID_VOID;
      c_parser_peek_token (parser)->value = error_mark_node;
      fndef_ok = !nested;
    }

  /* When there are standard attributes at the start of the
     declaration (to apply to the entity being declared), an
     init-declarator-list or function definition must be present.  */
  if (c_parser_nth_token_starts_std_attributes (parser, 1))
    have_attrs = true;

  c_parser_declspecs (parser, specs, true, true, start_attr_ok,
              true, true, start_attr_ok, true, cla_nonabstract_decl);
  if (parser->error)
    {
      c_parser_skip_to_end_of_block_or_statement (parser);
      return result;
    }
  if (nested && !specs->declspecs_seen_p)
    {
      c_parser_error (parser, "expected declaration specifiers");
      c_parser_skip_to_end_of_block_or_statement (parser);
      return result;
    }

  finish_declspecs (specs);
  bool gnu_auto_type_p = specs->typespec_word == cts_auto_type;
  bool std_auto_type_p = specs->c23_auto_p;
  bool any_auto_type_p = gnu_auto_type_p || std_auto_type_p;
  gcc_assert (!(gnu_auto_type_p && std_auto_type_p));
  const char *auto_type_keyword = gnu_auto_type_p ? "__auto_type" : "auto";
  if (specs->constexpr_p)
    {
      /* An underspecified declaration may not declare tags or members
     or structures or unions; it is undefined behavior to declare
     the members of an enumeration.  Where the structure, union or
     enumeration type is declared within an initializer, this is
     diagnosed elsewhere.  Diagnose here the case of declaring
     such a type in the type specifiers of a constexpr
     declaration.  */
      switch (specs->typespec_kind)
    {
    case ctsk_tagfirstref:
    case ctsk_tagfirstref_attrs:
      error_at (here, "%qT declared in underspecified object declaration",
            specs->type);
      break;

    case ctsk_tagdef:
      error_at (here, "%qT defined in underspecified object declaration",
            specs->type);
      break;

    default:
      break;
    }
    }

  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
    {
      bool handled_assume = false;
      if (specs->attrs
      && !nested
      && specs->typespec_kind == ctsk_none
      && c_parser_handle_statement_omp_attributes (parser, specs->attrs,
                               NULL))
    {
      if (specs->attrs)
        c_warn_unused_attributes (specs->attrs);
      while (parser->in_omp_attribute_pragma)
        {
          gcc_assert (c_parser_next_token_is (parser, CPP_PRAGMA));
          c_parser_pragma (parser, pragma_external, NULL, NULL_TREE);
        }
      c_parser_consume_token (parser);
      return result;
    }
      if (specs->typespec_kind == ctsk_none
      && lookup_attribute ("gnu", "assume", specs->attrs))
    {
      handled_assume = true;
      specs->attrs
        = handle_assume_attribute (here, specs->attrs, nested);
    }
      if (any_auto_type_p)
    error_at (here, "%qs in empty declaration", auto_type_keyword);
      else if (specs->typespec_kind == ctsk_none
           && attribute_fallthrough_p (specs->attrs))
    {
      if (fallthru_attr_p != NULL)
        *fallthru_attr_p = true;
      if (nested)
        {
          tree fn = build_call_expr_internal_loc (here, IFN_FALLTHROUGH,
                              void_type_node, 0);
          add_stmt (fn);
        }
      else
        pedwarn (here, OPT_Wattributes,
             "%<fallthrough%> attribute at top level");
    }
      else if (empty_ok
           && !(have_attrs && specs->non_std_attrs_seen_p)
           && !handled_assume)
    shadow_tag (specs);
      else
    {
      shadow_tag_warned (specs, 1);
      if (!handled_assume)
        pedwarn (here, 0, "empty declaration");
    }
      /* We still have to evaluate size expressions.  */
      if (specs->expr)
    add_stmt (fold_convert (void_type_node, specs->expr));
      c_parser_consume_token (parser);
      if (oacc_routine_data)
    c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false);
      return result;
    }
  else if (specs->typespec_kind == ctsk_none
       && nested
       /* Only parse __attribute__((musttail)) when called from
          c_parser_compound_statement_nostart.  This certainly isn't
          a declaration in that case, but we don't do tentative parsing
          of GNU attributes right now.  */
       && fallthru_attr_p
       && c_parser_next_token_is_keyword (parser, RID_RETURN))
    {
      attr_state astate = {};
      specs->attrs = c_parser_handle_musttail (parser, specs->attrs, astate);
      if (astate.musttail_p)
    {
      if (specs->attrs)
        {
          auto_urlify_attributes sentinel;
          warning_at (c_parser_peek_token (parser)->location,
              OPT_Wattributes,
              "attribute %<musttail%> mixed with other attributes "
              "on %<return%> statement");
        }
      c_parser_statement_after_labels (parser, NULL, NULL_TREE, NULL,
                       astate);
      return result;
    }
    }

  /* Provide better error recovery.  Note that a type name here is usually
     better diagnosed as a redeclaration.  */
  if (empty_ok
      && specs->typespec_kind == ctsk_tagdef
      && c_parser_next_token_starts_declspecs (parser)
      && !c_parser_next_token_is (parser, CPP_NAME))
    {
      c_parser_error (parser, "expected %<;%>, identifier or %<(%>");
      parser->error = false;
      shadow_tag_warned (specs, 1);
      return result;
    }
  else if (c_dialect_objc () && !any_auto_type_p)
    {
      /* Prefix attributes are an error on method decls.  */
      switch (c_parser_peek_token (parser)->type)
    {
      case CPP_PLUS:
      case CPP_MINUS:
        if (c_parser_objc_diagnose_bad_element_prefix (parser, specs))
          return result;
        if (specs->attrs)
          {
        warning_at (c_parser_peek_token (parser)->location,
                OPT_Wattributes,
                       "prefix attributes are ignored for methods");
        specs->attrs = NULL_TREE;
          }
        if (fndef_ok)
          c_parser_objc_method_definition (parser);
        else
          c_parser_objc_methodproto (parser);
        return result;
        break;
      default:
        break;
    }
      /* This is where we parse 'attributes @interface ...',
     'attributes @implementation ...', 'attributes @protocol ...'
     (where attributes could be, for example, __attribute__
     ((deprecated)).
      */
      switch (c_parser_peek_token (parser)->keyword)
    {
    case RID_AT_INTERFACE:
      {
        if (c_parser_objc_diagnose_bad_element_prefix (parser, specs))
          return result;
        c_parser_objc_class_definition (parser, specs->attrs);
        return result;
      }
      break;
    case RID_AT_IMPLEMENTATION:
      {
        if (c_parser_objc_diagnose_bad_element_prefix (parser, specs))
          return result;
        if (specs->attrs)
          {
        warning_at (c_parser_peek_token (parser)->location,
            OPT_Wattributes,
            "prefix attributes are ignored for implementations");
        specs->attrs = NULL_TREE;
          }
        c_parser_objc_class_definition (parser, NULL_TREE);
        return result;
      }
      break;
    case RID_AT_PROTOCOL:
      {
        if (c_parser_objc_diagnose_bad_element_prefix (parser, specs))
          return result;
        c_parser_objc_protocol_definition (parser, specs->attrs);
        return result;
      }
      break;
    case RID_AT_ALIAS:
    case RID_AT_CLASS:
    case RID_AT_END:
    case RID_AT_PROPERTY:
      if (specs->attrs)
        {
          c_parser_error (parser, "unexpected attribute");
          specs->attrs = NULL;
        }
      break;
    default:
      break;
    }
    }
  else if (attribute_fallthrough_p (specs->attrs))
    warning_at (here, OPT_Wattributes,
        "%<fallthrough%> attribute not followed by %<;%>");
  else if (lookup_attribute ("gnu", "assume", specs->attrs))
    warning_at (here, OPT_Wattributes,
        "%<assume%> attribute not followed by %<;%>");

  auto_vec<c_token> omp_declare_simd_attr_clauses;
  c_parser_handle_directive_omp_attributes (specs->attrs,
                        omp_declare_simd_clauses,
                        &omp_declare_simd_attr_clauses);
  pending_xref_error ();
  prefix_attrs = specs->attrs;
  all_prefix_attrs = prefix_attrs;
  specs->attrs = NULL_TREE;
  bool more_than_one_decl = false;
  while (true)
    {
      struct c_declarator *declarator;
      bool dummy = false;
      timevar_id_t tv;
      tree fnbody = NULL_TREE;
      tree underspec_name = NULL_TREE;
      auto_vec<c_token> omp_dsimd_idattr_clauses;
      /* Declaring either one or more declarators (in which case we
     should diagnose if there were no declaration specifiers) or a
     function definition (in which case the diagnostic for
     implicit int suffices).  */
      declarator = c_parser_declarator (parser,
                    specs->typespec_kind != ctsk_none,
                    C_DTR_NORMAL, &dummy);
      if (declarator == NULL)
    {
      if (omp_declare_simd_clauses)
        c_finish_omp_declare_simd (parser, NULL_TREE, NULL_TREE,
                       omp_declare_simd_clauses);
      if (oacc_routine_data)
        c_finish_oacc_routine (oacc_routine_data, NULL_TREE, false);
      /* This check is here purely to improve the diagnostic.  */
      if (!simple_ok)
        c_parser_skip_to_end_of_block_or_statement (parser);
      return result;
    }
      if (flag_openmp || flag_openmp_simd)
    {
      struct c_declarator *d = declarator;
      while (d->kind != cdk_id)
        d = d->declarator;
      vec<c_token> *dummy = NULL;
      c_parser_handle_directive_omp_attributes (d->u.id.attrs, dummy,
                            &omp_dsimd_idattr_clauses);
    }
      if (gnu_auto_type_p && declarator->kind != cdk_id)
    {
      error_at (here,
            "%<__auto_type%> requires a plain identifier"
            " as declarator");
      c_parser_skip_to_end_of_block_or_statement (parser);
      return result;
    }
      if (std_auto_type_p)
    {
      struct c_declarator *d = declarator;
      while (d->kind == cdk_attrs)
        d = d->declarator;
      if (d->kind != cdk_id)
        {
          error_at (here,
            "%<auto%> requires a plain identifier, possibly with"
            " attributes, as declarator");
          c_parser_skip_to_end_of_block_or_statement (parser);
          return result;
        }
      underspec_name = d->u.id.id;
    }
      else if (specs->constexpr_p)
    {
      struct c_declarator *d = declarator;
      while (d->kind != cdk_id)
        d = d->declarator;
      underspec_name = d->u.id.id;
    }
      if (c_parser_next_token_is (parser, CPP_EQ)
      || c_parser_next_token_is (parser, CPP_COMMA)
      || c_parser_next_token_is (parser, CPP_SEMICOLON)
      || c_parser_next_token_is_keyword (parser, RID_ASM)
      || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)
      || c_parser_next_token_is_keyword (parser, RID_IN))
    {
      tree asm_name = NULL_TREE;
      tree postfix_attrs = NULL_TREE;
      if (!diagnosed_no_specs && !specs->declspecs_seen_p)
        {
          diagnosed_no_specs = true;
          pedwarn (here, 0, "data definition has no type or storage class");
        }
      /* Having seen a data definition, there cannot now be a
         function definition.  */
      fndef_ok = false;
      if (c_parser_next_token_is_keyword (parser, RID_ASM))
        asm_name = c_parser_simple_asm_expr (parser);
      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
        {
          postfix_attrs = c_parser_gnu_attributes (parser);
          if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
        {
          /* This means there is an attribute specifier after
             the declarator in a function definition.  Provide
             some more information for the user.  */
          error_at (here, "attributes should be specified before the "
                "declarator in a function definition");
          c_parser_skip_to_end_of_block_or_statement (parser);
          return result;
        }
        }
      if (c_parser_next_token_is (parser, CPP_EQ))
        {
          tree d;
          struct c_expr init;
          location_t init_loc;
          c_parser_consume_token (parser);
          if (any_auto_type_p)
        {
          init_loc = c_parser_peek_token (parser)->location;
          rich_location richloc (line_table, init_loc);
          unsigned int underspec_state = 0;
          if (std_auto_type_p)
            underspec_state =
              start_underspecified_init (init_loc, underspec_name);
          start_init (NULL_TREE, asm_name,
                  (global_bindings_p ()
                   || specs->storage_class == csc_static
                   || specs->constexpr_p),
                  specs->constexpr_p, &richloc);
          /* A parameter is initialized, which is invalid.  Don't
             attempt to instrument the initializer.  */
          int flag_sanitize_save = flag_sanitize;
          if (nested && !empty_ok)
            flag_sanitize = 0;
          init = c_parser_expr_no_commas (parser, NULL);
          if (std_auto_type_p)
            finish_underspecified_init (underspec_name,
                        underspec_state);
          flag_sanitize = flag_sanitize_save;
          if (gnu_auto_type_p
              && TREE_CODE (init.value) == COMPONENT_REF
              && DECL_C_BIT_FIELD (TREE_OPERAND (init.value, 1)))
            error_at (here,
                  "%<__auto_type%> used with a bit-field"
                  " initializer");
          init = convert_lvalue_to_rvalue (init_loc, init, true, true,
                           true);
          tree init_type = TREE_TYPE (init.value);
          bool vm_type = c_type_variably_modified_p (init_type);
          if (vm_type)
            init.value = save_expr (init.value);
          finish_init ();
          specs->typespec_kind = ctsk_typeof;
          specs->locations[cdw_typedef] = init_loc;
          specs->typedef_p = true;
          specs->type = init_type;
          if (specs->postfix_attrs)
            {
              /* Postfix [[]] attributes are valid with C23
             auto, although not with __auto_type, and
             modify the type given by the initializer.  */
              specs->postfix_attrs
            = c_warn_type_attributes (specs->type,
                          specs->postfix_attrs);
              decl_attributes (&specs->type, specs->postfix_attrs, 0);
              specs->postfix_attrs = NULL_TREE;
            }
          if (vm_type)
            {
              bool maybe_const = true;
              tree type_expr = c_fully_fold (init.value, false,
                             &maybe_const);
              specs->expr_const_operands &= maybe_const;
              if (specs->expr)
            specs->expr = build2 (COMPOUND_EXPR,
                          TREE_TYPE (type_expr),
                          specs->expr, type_expr);
              else
            specs->expr = type_expr;
            }
          d = start_decl (declarator, specs, true,
                  chainon (postfix_attrs, all_prefix_attrs));
          if (!d)
            d = error_mark_node;
          if (omp_declare_simd_clauses)
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           omp_declare_simd_clauses);
          if (!omp_dsimd_idattr_clauses.is_empty ())
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           &omp_dsimd_idattr_clauses);
        }
          else
        {
          /* The declaration of the variable is in effect while
             its initializer is parsed, except for a constexpr
             variable.  */
          init_loc = c_parser_peek_token (parser)->location;
          rich_location richloc (line_table, init_loc);
          unsigned int underspec_state = 0;
          if (specs->constexpr_p)
            underspec_state =
              start_underspecified_init (init_loc, underspec_name);
          d = start_decl (declarator, specs, true,
                  chainon (postfix_attrs,
                       all_prefix_attrs),
                  !specs->constexpr_p);
          if (!d)
            d = error_mark_node;
          if (!specs->constexpr_p && omp_declare_simd_clauses)
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           omp_declare_simd_clauses);
          if (!specs->constexpr_p
              && !omp_dsimd_idattr_clauses.is_empty ())
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           &omp_dsimd_idattr_clauses);
          start_init (d, asm_name,
                  TREE_STATIC (d) || specs->constexpr_p,
                  specs->constexpr_p, &richloc);
          /* A parameter is initialized, which is invalid.  Don't
             attempt to instrument the initializer.  */
          int flag_sanitize_save = flag_sanitize;
          if (TREE_CODE (d) == PARM_DECL)
            flag_sanitize = 0;
          init = c_parser_initializer (parser, d);
          flag_sanitize = flag_sanitize_save;
          if (specs->constexpr_p)
            {
              finish_underspecified_init (underspec_name,
                          underspec_state);
              d = pushdecl (d);
              if (omp_declare_simd_clauses)
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           omp_declare_simd_clauses);
          if (!specs->constexpr_p
              && !omp_dsimd_idattr_clauses.is_empty ())
            c_finish_omp_declare_simd (parser, d, NULL_TREE,
                           &omp_dsimd_idattr_clauses);
            }
          finish_init ();
        }
          if (oacc_routine_data)
        c_finish_oacc_routine (oacc_routine_data, d, false);
          if (d != error_mark_node)
        {
          maybe_warn_string_init (init_loc, TREE_TYPE (d), init);
          finish_decl (d, init_loc, init.value,
                   init.original_type, asm_name);
          result = d;
        }
        }
      else
        {
          if (any_auto_type_p || specs->constexpr_p)
        {
          error_at (here,
                "%qs requires an initialized data declaration",
                any_auto_type_p ? auto_type_keyword : "constexpr");
          c_parser_skip_to_end_of_block_or_statement (parser);
          return result;
        }

          location_t lastloc = UNKNOWN_LOCATION;
          tree attrs = chainon (postfix_attrs, all_prefix_attrs);
          tree d = start_decl (declarator, specs, false, attrs, true,
                   &lastloc);
          if (d && TREE_CODE (d) == FUNCTION_DECL)
        {
          /* Find the innermost declarator that is neither cdk_id
             nor cdk_attrs.  */
          const struct c_declarator *decl = declarator;
          const struct c_declarator *last_non_id_attrs = NULL;

          while (decl)
            switch (decl->kind)
              {
              case cdk_array:
              case cdk_function:
              case cdk_pointer:
            last_non_id_attrs = decl;
            decl = decl->declarator;
            break;

              case cdk_attrs:
            decl = decl->declarator;
            break;

              case cdk_id:
            decl = 0;
            break;

              default:
            gcc_unreachable ();
              }

          /* If it exists and is cdk_function declaration whose
             arguments have not been set yet, use its arguments.  */
          if (last_non_id_attrs
              && last_non_id_attrs->kind == cdk_function)
            {
              tree parms = last_non_id_attrs->u.arg_info->parms;
              if (DECL_ARGUMENTS (d) == NULL_TREE
              && DECL_INITIAL (d) == NULL_TREE)
            DECL_ARGUMENTS (d) = parms;

              warn_parm_array_mismatch (lastloc, d, parms);
            }
        }
          if (omp_declare_simd_clauses
          || !omp_dsimd_idattr_clauses.is_empty ())
        {
          tree parms = NULL_TREE;
          if (d && TREE_CODE (d) == FUNCTION_DECL)
            {
              struct c_declarator *ce = declarator;
              while (ce != NULL)
            if (ce->kind == cdk_function)
              {
                parms = ce->u.arg_info->parms;
                break;
              }
            else
              ce = ce->declarator;
            }
          if (parms)
            temp_store_parm_decls (d, parms);
          if (omp_declare_simd_clauses)
            c_finish_omp_declare_simd (parser, d, parms,
                           omp_declare_simd_clauses);
          if (!specs->constexpr_p
              && !omp_dsimd_idattr_clauses.is_empty ())
            c_finish_omp_declare_simd (parser, d, parms,
                           &omp_dsimd_idattr_clauses);
          if (parms)
            temp_pop_parm_decls ();
        }
          if (oacc_routine_data)
        c_finish_oacc_routine (oacc_routine_data, d, false);
          if (d)
        finish_decl (d, UNKNOWN_LOCATION, NULL_TREE,
                 NULL_TREE, asm_name);

          if (c_parser_next_token_is_keyword (parser, RID_IN))
        {
          if (d)
            *objc_foreach_object_declaration = d;
          else
            *objc_foreach_object_declaration = error_mark_node;
        }
        }
      if (c_parser_next_token_is (parser, CPP_COMMA))
        {
          more_than_one_decl = true;
          if (any_auto_type_p || specs->constexpr_p)
        {
          error_at (here,
                "%qs may only be used with a single declarator",
                any_auto_type_p ? auto_type_keyword : "constexpr");
          c_parser_skip_to_end_of_block_or_statement (parser);
          return result;
        }
          c_parser_consume_token (parser);
          if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
        all_prefix_attrs = chainon (c_parser_gnu_attributes (parser),
                        prefix_attrs);
          else
        all_prefix_attrs = prefix_attrs;
          continue;
        }
      else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
        {
          if (!simple_ok)
        c_parser_consume_token (parser);
          return result;
        }
      else if (c_parser_next_token_is_keyword (parser, RID_IN))
        {
          /* This can only happen in Objective-C: we found the
         'in' that terminates the declaration inside an
         Objective-C foreach statement.  Do not consume the
         token, so that the caller can use it to determine
         that this indeed is a foreach context.  */
          return result;
        }
      else
        {
          if (!simple_ok)
        {
          c_parser_error (parser, "expected %<,%> or %<;%>");
          c_parser_skip_to_end_of_block_or_statement (parser);
        }
          /* It's not valid to use if (int i = 2, j = 3).  */
          else if (more_than_one_decl)
        error_at (here, "declaration in condition can only declare "
              "a single object");
          return result;
        }
    }
      else if (any_auto_type_p || specs->constexpr_p)
    {
      error_at (here,
            "%qs requires an initialized data declaration",
            any_auto_type_p ? auto_type_keyword : "constexpr");
      c_parser_skip_to_end_of_block_or_statement (parser);
      return result;
    }
      else if (!fndef_ok)
    {
      if (simple_ok && c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
        /* Let c_parser_selection_header emit the error.  */;
      else
        {
          c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, "
                  "%<asm%> or %<__attribute__%>");
          c_parser_skip_to_end_of_block_or_statement (parser);
        }
      return result;
    }
      /* Function definition (nested or otherwise).  */
      if (nested)
    {
      pedwarn (here, OPT_Wpedantic, "ISO C forbids nested functions");
      c_push_function_context ();
    }
      if (!start_function (specs, declarator, all_prefix_attrs))
    {
      /* At this point we've consumed:
           declaration-specifiers declarator
         and the next token isn't CPP_EQ, CPP_COMMA, CPP_SEMICOLON,
         RID_ASM, RID_ATTRIBUTE, or RID_IN,
         but the
           declaration-specifiers declarator
         aren't grokkable as a function definition, so we have
         an error.  */
      gcc_assert (!c_parser_next_token_is (parser, CPP_SEMICOLON));
      if (c_parser_next_token_starts_declspecs (parser))
        {
          /* If we have
           declaration-specifiers declarator decl-specs
         then assume we have a missing semicolon, which would
         give us:
           declaration-specifiers declarator  decl-specs
                            ^
                            ;
           <~~~~~~~~~ declaration ~~~~~~~~~~>
         Use c_parser_require to get an error with a fix-it hint.  */
          c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>");
          parser->error = false;
        }
      else
        {
          /* This can appear in many cases looking nothing like a
         function definition, so we don't give a more specific
         error suggesting there was one.  */
          c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, %<asm%> "
                  "or %<__attribute__%>");
        }
      if (nested)
        c_pop_function_context ();
      break;
    }

      if (DECL_DECLARED_INLINE_P (current_function_decl))
        tv = TV_PARSE_INLINE;
      else
        tv = TV_PARSE_FUNC;
      auto_timevar at (g_timer, tv);

      /* Parse old-style parameter declarations.  ??? Attributes are
     not allowed to start declaration specifiers here because of a
     syntax conflict between a function declaration with attribute
     suffix and a function definition with an attribute prefix on
     first old-style parameter declaration.  Following the old
     parser, they are not accepted on subsequent old-style
     parameter declarations either.  However, there is no
     ambiguity after the first declaration, nor indeed on the
     first as long as we don't allow postfix attributes after a
     declarator with a nonempty identifier list in a definition;
     and postfix attributes have never been accepted here in
     function definitions either.  */
      int save_debug_nonbind_markers_p = debug_nonbind_markers_p;
      debug_nonbind_markers_p = 0;
      c_parser_maybe_reclassify_token (parser);
      while (c_parser_next_token_is_not (parser, CPP_EOF)
         && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE))
    c_parser_declaration_or_fndef (parser, false, false, false,
                       true, false, false);
      debug_nonbind_markers_p = save_debug_nonbind_markers_p;
      store_parm_decls ();
      if (omp_declare_simd_clauses)
    c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
                   omp_declare_simd_clauses);
      if (!omp_dsimd_idattr_clauses.is_empty ())
    c_finish_omp_declare_simd (parser, current_function_decl, NULL_TREE,
                   &omp_dsimd_idattr_clauses);
      if (oacc_routine_data)
    c_finish_oacc_routine (oacc_routine_data, current_function_decl, true);
      location_t startloc = c_parser_peek_token (parser)->location;
      DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus
    = startloc;
      location_t endloc = startloc;

      /* If the definition was marked with __RTL, use the RTL parser now,
     consuming the function body.  */
      if (specs->declspec_il == cdil_rtl)
    {
      endloc = c_parser_parse_rtl_body (parser, specs->gimple_or_rtl_pass);

      /* Normally, store_parm_decls sets next_is_function_body,
         anticipating a function body.  We need a push_scope/pop_scope
         pair to flush out this state, or subsequent function parsing
         will go wrong.  */
      push_scope ();
      pop_scope ();

      finish_function (endloc);
      return result;
    }
      /* If the definition was marked with __GIMPLE then parse the
         function body as GIMPLE.  */
      else if (specs->declspec_il != cdil_none)
    {
      bool saved = in_late_binary_op;
      in_late_binary_op = true;
      c_parser_parse_gimple_body (parser, specs->gimple_or_rtl_pass,
                      specs->declspec_il,
                      specs->entry_bb_count);
      in_late_binary_op = saved;
    }
      else
    fnbody = c_parser_compound_statement (parser, &endloc);
      tree fndecl = current_function_decl;
      if (nested)
    {
      tree decl = current_function_decl;
      /* Mark nested functions as needing static-chain initially.
         lower_nested_functions will recompute it but the
         DECL_STATIC_CHAIN flag is also used before that happens,
         by initializer_constant_valid_p.  See gcc.dg/nested-fn-2.c.  */
      DECL_STATIC_CHAIN (decl) = 1;
      add_stmt (fnbody);
      finish_function (endloc);
      c_pop_function_context ();
      add_stmt (build_stmt (DECL_SOURCE_LOCATION (decl), DECL_EXPR, decl));
    }
      else
    {
      if (fnbody)
        add_stmt (fnbody);
      finish_function (endloc);
    }
      /* Get rid of the empty stmt list for GIMPLE/RTL.  */
      if (specs->declspec_il != cdil_none)
    DECL_SAVED_TREE (fndecl) = NULL_TREE;

      break;
    }

  return result;
}

当完成变量或者函数的解析后,会在符号表注册

/* Finish up a function declaration and compile that function
   all the way to assembler language output.  Then free the storage
   for the function definition.

   This is called after parsing the body of the function definition.  */

void
finish_function (location_t end_loc)

{
  tree fndecl = current_function_decl;

  if (c_dialect_objc ())
    objc_finish_function ();

  if (TREE_CODE (fndecl) == FUNCTION_DECL
      && targetm.calls.promote_prototypes (TREE_TYPE (fndecl)))
    {
      tree args = DECL_ARGUMENTS (fndecl);
      for (; args; args = DECL_CHAIN (args))
    {
      tree type = TREE_TYPE (args);
      if (INTEGRAL_TYPE_P (type)
          && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))
        DECL_ARG_TYPE (args) = c_type_promotes_to (type);
    }
    }

  if (DECL_INITIAL (fndecl) && DECL_INITIAL (fndecl) != error_mark_node)
    BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl;

  /* Must mark the RESULT_DECL as being in this function.  */

  if (DECL_RESULT (fndecl) && DECL_RESULT (fndecl) != error_mark_node)
    DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;

  if (MAIN_NAME_P (DECL_NAME (fndecl)) && !TREE_THIS_VOLATILE (fndecl)
      && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl)))
      == integer_type_node && flag_isoc99)
    {
      /* Hack.  We don't want the middle-end to warn that this return
     is unreachable, so we mark its location as special.  Using
     UNKNOWN_LOCATION has the problem that it gets clobbered in
     annotate_one_with_locus.  A cleaner solution might be to
     ensure ! should_carry_locus_p (stmt), but that needs a flag.
      */
      c_finish_return (BUILTINS_LOCATION, integer_zero_node, NULL_TREE);
    }

  /* Tie off the statement tree for this function.  */
  DECL_SAVED_TREE (fndecl) = pop_stmt_list (DECL_SAVED_TREE (fndecl));

  finish_fname_decls ();

  /* Complain if there's no return statement only if option specified on
     command line.  */
  if (warn_return_type > 0
      && TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE
      && !current_function_returns_value && !current_function_returns_null
      /* Don't complain if we are no-return.  */
      && !current_function_returns_abnormally
      /* Don't complain if we are declared noreturn.  */
      && !TREE_THIS_VOLATILE (fndecl)
      /* Don't warn for main().  */
      && !MAIN_NAME_P (DECL_NAME (fndecl))
      /* Or if they didn't actually specify a return type.  */
      && !C_FUNCTION_IMPLICIT_INT (fndecl)
      /* Normally, with -Wreturn-type, flow will complain, but we might
         optimize out static functions.  */
      && !TREE_PUBLIC (fndecl)
      && targetm.warn_func_return (fndecl)
      && warning (OPT_Wreturn_type,
          "no return statement in function returning non-void"))
    suppress_warning (fndecl, OPT_Wreturn_type);

  /* Complain about parameters that are only set, but never otherwise used.  */
  if (warn_unused_but_set_parameter)
    {
      tree decl;

      for (decl = DECL_ARGUMENTS (fndecl);
       decl;
       decl = DECL_CHAIN (decl))
    if (TREE_USED (decl)
        && TREE_CODE (decl) == PARM_DECL
        && !DECL_READ_P (decl)
        && DECL_NAME (decl)
        && !DECL_ARTIFICIAL (decl)
        && !warning_suppressed_p (decl, OPT_Wunused_but_set_parameter))
      warning_at (DECL_SOURCE_LOCATION (decl),
              OPT_Wunused_but_set_parameter,
              "parameter %qD set but not used", decl);
    }

  /* Complain about locally defined typedefs that are not used in this
     function.  */
  maybe_warn_unused_local_typedefs ();

  /* Possibly warn about unused parameters.  */
  if (warn_unused_parameter)
    do_warn_unused_parameter (fndecl);

  /* Store the end of the function, so that we get good line number
     info for the epilogue.  */
  cfun->function_end_locus = end_loc;

  /* Finalize the ELF visibility for the function.  */
  c_determine_visibility (fndecl);

  /* For GNU C extern inline functions disregard inline limits.  */
  if (DECL_EXTERNAL (fndecl)
      && DECL_DECLARED_INLINE_P (fndecl)
      && (flag_gnu89_inline
      || lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (fndecl))))
    DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;

  /* Genericize before inlining.  Delay genericizing nested functions
     until their parent function is genericized.  Since finalizing
     requires GENERIC, delay that as well.  */

  if (DECL_INITIAL (fndecl) && DECL_INITIAL (fndecl) != error_mark_node
      && !undef_nested_function)
    {
      if (!decl_function_context (fndecl))
    {
      invoke_plugin_callbacks (PLUGIN_PRE_GENERICIZE, fndecl);
      c_genericize (fndecl);

      /* ??? Objc emits functions after finalizing the compilation unit.
         This should be cleaned up later and this conditional removed.  */
      if (symtab->global_info_ready)
        {
          cgraph_node::add_new_function (fndecl, false);
          return;
        }
      cgraph_node::finalize_function (fndecl, false);
    }
      else
    {
      /* Register this function with cgraph just far enough to get it
        added to our parent's nested function list.  Handy, since the
        C front end doesn't have such a list.  */
      (void) cgraph_node::get_create (fndecl);

    }
    }

  if (!decl_function_context (fndecl))
    undef_nested_function = false;

  if (cfun->language != NULL)
    {
      ggc_free (cfun->language);
      cfun->language = NULL;
    }

  /* We're leaving the context of this function, so zap cfun.
     It's still in DECL_STRUCT_FUNCTION, and we'll restore it in
     tree_rest_of_compilation.  */
  set_cfun (NULL);
  invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, current_function_decl);
  current_function_decl = NULL;
}

符号或者函数的实现需要注册到符号表中去

/* Add node into symbol table.  This function is not used directly, but via
   cgraph/varpool node creation routines.  */

void
symtab_node::register_symbol (void)
{
  symtab->register_symbol (this);

  if (!decl->decl_with_vis.symtab_node)
    decl->decl_with_vis.symtab_node = this;

  ref_list.clear ();

  /* Be sure to do this last; C++ FE might create new nodes via
     DECL_ASSEMBLER_NAME langhook!  */
  symtab->insert_to_assembler_name_hash (this, false);
}
 

总体来说,语法解析的代码非常大,细节的实现难以看明白,需要的话需要很深入的去了解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoldKey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值