前面部分分析了初始化流程,紧接着分析的是 Parse 部分。

所有 Action 的基类是 FrontendAction,在 FrontendActionExecute 中发现:

bool FrontendAction::Execute() {
  CompilerInstance &CI = getCompilerInstance();

  if (CI.hasFrontendTimer()) {
    llvm::TimeRegion Timer(CI.getFrontendTimer());
    ExecuteAction();
  }
  else ExecuteAction();

  // ....

  return true;
}

流程转入了 ExecuteAction 中,目前的 ActEmitLLVMAction

class EmitLLVMAction : public CodeGenAction {
  virtual void anchor();
public:
  EmitLLVMAction(llvm::LLVMContext *_VMContext = nullptr);
};

实际上 EmitLLVMAction 只是 CodeGenAction 的一个子类。所以应该在 CodeGenAction 中找 Execute 的逻辑。

void CodeGenAction::ExecuteAction() {
  // If this is an IR file, we have to treat it specially.
  if (getCurrentFileKind() == IK_LLVM_IR) {
    // other codes.
  }

  // Otherwise follow the normal AST path.
  this->ASTFrontendAction::ExecuteAction();
}

所以在 CodeGenAction::ExecuteAction 中,直接使用了 ASTFrontendAction::ExecuteAction 的实现。找到 ASTFrontendAction 对应的 ExecuteAction

void ASTFrontendAction::ExecuteAction() {
  ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
           CI.getFrontendOpts().SkipFunctionBodies);
}

这里截取了重要的部分代码。跟踪 ParseAST,截取代码如下:

void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
  ASTConsumer *Consumer = &S.getASTConsumer();

  std::unique_ptr<Parser> ParseOP(
      new Parser(S.getPreprocessor(), S, SkipFunctionBodies));
  Parser &P = *ParseOP.get();

  S.getPreprocessor().EnterMainSourceFile();
  P.Initialize();

  if (P.ParseTopLevelDecl(ADecl)) {
    if (!External && !S.getLangOpts().CPlusPlus)
      P.Diag(diag::ext_empty_translation_unit);
  } else {
    do {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
        return;
    } while (!P.ParseTopLevelDecl(ADecl));
  }

  // Process any TopLevelDecls generated by #pragma weak.
  for (Decl *D : S.WeakTopLevelDecls())
    Consumer->HandleTopLevelDecl(DeclGroupRef(D));
  
  Consumer->HandleTranslationUnit(S.getASTContext());
}

Parse 通过 ParseTopLevelDecl 得到 Decl ,然后通过 ASTConsumerHandleTopLevelDecl 处理。忽略其他现在并不关心的部分,在 ParseTopLevelDecl 内部调用 ParseExternalDeclaration 开始。而 ParseExternalDeclaration 内部,我们只关心下面一行代码:

return ParseDeclarationOrFunctionDefinition(attrs, DS);

这里处理声明或者函数定义,内部有一个 Internal 包含。这其中分为两部分,一部分是 ParseDeclarationSpecifiers;另一部分是最后的 ParseDeclGroup。 第一部分用于获取类型说明符,第二部分则是具体声明部分,这里重点看第二部分:

Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
                                              unsigned Context,
                                              SourceLocation *DeclEnd,
                                              ForRangeInit *FRI) {
  // Parse the first declarator.
  ParsingDeclarator D(*this, DS, static_cast<Declarator::TheContext>(Context));
  ParseDeclarator(D);

  // Check to see if we have a function *definition* which must have a body.
  if (D.isFunctionDeclarator() && !isDeclarationAfterDeclarator()) {
      Decl *TheDecl =
          ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs);
        return Actions.ConvertDeclToDeclGroup(TheDecl);
  }

  SmallVector<Decl *, 8> DeclsInGroup;
  Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(
      D, ParsedTemplateInfo(), FRI);

  // If we don't have a comma, it is either the end of the list (a ';') or an
  // error, bail out.
  SourceLocation CommaLoc;
  while (TryConsumeToken(tok::comma, CommaLoc)) {
    ParseDeclarator(D);
    if (!D.isInvalidType()) {
      Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
    }
  }
}

ParseDeclGroup 的结构大致如上,首先使用 ParseDeclarator 获取一个声明,比如 int i; 这里得到的就是 i,然后判断是否紧接 (),如果是,则调用 ParseFunctionDefinition 分析函数定义,否则循环调用 ParseDeclarator 获取所有声明的变量。

对于声明而言,需要调用 ParseDeclarationAfterDeclarator 将类型与 declarator 结合,形成一个完整的声明。ParseDeclarationAfterDeclarator 中调用了 ParseDeclarationAfterDeclaratorAndAttributes, 而 ParseDeclarationAfterDeclaratorAndAttributes 中实际调用了 Actions.ActOnDeclaratorActOnDeclarator 实际调用了 HandleDeclarator

HandleDeclarator 中,实际的工作有三个,首先调用 GetTypeForDeclarator; 得到类型信息,因为各个类型实际上只有一个实例,所以这里需要映射过程。其次调用以下几个中的某一个:

ActOnTypedefDeclarator
ActOnFunctionDeclarator
ActOnVariableDeclarator

最后,调用 PushOnScopeChains 将声明存起来。保存起来的 declarator 的信息可以用于处理下一次遇到 declarator 判断是否符合语法。

现在回头看 ParseFunctionDefinition 部分。节选代码如下:

Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
                                      const ParsedTemplateInfo &TemplateInfo,
                                      LateParsedAttrList *LateParsedAttrs) {
  // Enter a scope for the function body.
  ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope);

  // Tell the actions module that we have entered a function definition with the
  // specified Declarator for the function.
  Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), D,
                                              TemplateInfo.TemplateParams
                                                  ? *TemplateInfo.TemplateParams
                                                  : MultiTemplateParamsArg(),
                                              &SkipBody);

  return ParseFunctionStatementBody(Res, BodyScope);
}

首先进入函数作用域,执行相应的 Action,最后调用 ParseFunctionStatementBody 开始解析函数部分。

Decl *Parser::ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope) {
  // Do not enter a scope for the brace, as the arguments are in the same scope
  // (the function body) as the body itself.  Instead, just read the statement
  // list and put it into a CompoundStmt for safe keeping.
  StmtResult FnBody(ParseCompoundStatementBody());

  BodyScope.Exit();
  return Actions.ActOnFinishFunctionBody(Decl, FnBody.get());
}

ParseFunctionStatementBody 中最重要的一句是:

StmtResult FnBody(ParseCompoundStatementBody());

然后就是退出作用域,执行相应的 Action。继续跟进 ParseCompoundStatementBody

ParseCompoundStatementBody 中,暂时忽略 kw__extension__ 的情况,于是,实际的调用为 ParseStatementOrDeclaration,其中又调用了 ParseStatementOrDeclarationAfterAttributes,这个函数就是正式进行分析的代码部分:

StmtResult
Parser::ParseStatementOrDeclarationAfterAttributes(StmtVector &Stmts,
          AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc,
          ParsedAttributesWithRange &Attrs) {
  switch (Kind) {
  case tok::identifier: {
    Token Next = NextToken();
    if (Next.is(tok::colon)) { // C99 6.8.1: labeled-statement
      // identifier ':' statement
      return ParseLabeledStatement(Attrs);
    }

    // Look up the identifier, and typo-correct it to a keyword if it's not
    // found.
    if (Next.isNot(tok::coloncolon)) {
      // Try to limit which sets of keywords should be included in typo
      // correction based on what the next token is.
      if (TryAnnotateName(/*IsAddressOfOperand*/ false,
                          llvm::make_unique<StatementFilterCCC>(Next)) ==
          ANK_Error) {
        // Handle errors here by skipping up to the next semicolon or '}', and
        // eat the semicolon if that's what stopped us.
        SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
        if (Tok.is(tok::semi))
          ConsumeToken();
        return StmtError();
      }

      // If the identifier was typo-corrected, try again.
      if (Tok.isNot(tok::identifier))
        goto Retry;
    }

    // Fall through
  }

  default: {
    if ((getLangOpts().CPlusPlus || Allowed == ACK_Any) &&
        isDeclarationStatement()) {
      SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
      DeclGroupPtrTy Decl = ParseDeclaration(Declarator::BlockContext,
                                             DeclEnd, Attrs);
      return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd);
    }

    if (Tok.is(tok::r_brace)) {
      Diag(Tok, diag::err_expected_statement);
      return StmtError();
    }

    return ParseExprStatement();
  }

  case tok::kw_case:                // C99 6.8.1: labeled-statement
    return ParseCaseStatement();
  case tok::kw_default:             // C99 6.8.1: labeled-statement
    return ParseDefaultStatement();

  case tok::l_brace:                // C99 6.8.2: compound-statement
    return ParseCompoundStatement();
  case tok::semi: {                 // C99 6.8.3p3: expression[opt] ';'
    bool HasLeadingEmptyMacro = Tok.hasLeadingEmptyMacro();
    return Actions.ActOnNullStmt(ConsumeToken(), HasLeadingEmptyMacro);
  }

  case tok::kw_if:                  // C99 6.8.4.1: if-statement
    return ParseIfStatement(TrailingElseLoc);
  case tok::kw_switch:              // C99 6.8.4.2: switch-statement
    return ParseSwitchStatement(TrailingElseLoc);

  case tok::kw_while:               // C99 6.8.5.1: while-statement
    return ParseWhileStatement(TrailingElseLoc);
  case tok::kw_do:                  // C99 6.8.5.2: do-statement
    Res = ParseDoStatement();
    SemiError = "do/while";
    break;
  case tok::kw_for:                 // C99 6.8.5.3: for-statement
    return ParseForStatement(TrailingElseLoc);

  case tok::kw_goto:                // C99 6.8.6.1: goto-statement
    Res = ParseGotoStatement();
    SemiError = "goto";
    break;
  case tok::kw_continue:            // C99 6.8.6.2: continue-statement
    Res = ParseContinueStatement();
    SemiError = "continue";
    break;
  case tok::kw_break:               // C99 6.8.6.3: break-statement
    Res = ParseBreakStatement();
    SemiError = "break";
    break;
  case tok::kw_return:              // C99 6.8.6.4: return-statement
    Res = ParseReturnStatement();
    SemiError = "return";
    break;
  }

  return Res;
}

关键代码如上,可以看到针对对应的关键字,调用对应的 Parse。这里就不继续跟踪了,有兴趣可以深入了解。

可以发现的是,Action 穿插在代码中,Parse 到指定位置,则调用相应的语义动作进行检查,这样就不需要在 Parse 完成后,遍历一次语法树,且代码简介易于编写。