//===--- ParseCXXInlineMethods.cpp - C++ class inline methods parsing------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements parsing for C++ class inline methods. // //===----------------------------------------------------------------------===// #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/DeclSpec.h" #include "clang/Parse/Scope.h" using namespace clang; /// ParseCXXInlineMethodDef - We parsed and verified that the specified /// Declarator is a well formed C++ inline method definition. Now lex its body /// and store its tokens for parsing after the C++ class is complete. Parser::DeclPtrTy Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) { assert(D.getTypeObject(0).Kind == DeclaratorChunk::Function && "This isn't a function declarator!"); assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) && "Current token not a '{', ':' or 'try'!"); DeclPtrTy FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0); // Consume the tokens and store them for later parsing. getCurrentClass().MethodDefs.push_back(LexedMethod(FnD)); CachedTokens &Toks = getCurrentClass().MethodDefs.back().Toks; tok::TokenKind kind = Tok.getKind(); // We may have a constructor initializer or function-try-block here. if (kind == tok::colon || kind == tok::kw_try) { // Consume everything up to (and including) the left brace. if (!ConsumeAndStoreUntil(tok::l_brace, tok::unknown, Toks, tok::semi)) { // We didn't find the left-brace we expected after the // constructor initializer. if (Tok.is(tok::semi)) { // We found a semicolon; complain, consume the semicolon, and // don't try to parse this method later. Diag(Tok.getLocation(), diag::err_expected_lbrace); ConsumeAnyToken(); getCurrentClass().MethodDefs.pop_back(); return FnD; } } } else { // Begin by storing the '{' token. Toks.push_back(Tok); ConsumeBrace(); } // Consume everything up to (and including) the matching right brace. ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks); // If we're in a function-try-block, we need to store all the catch blocks. if (kind == tok::kw_try) { while (Tok.is(tok::kw_catch)) { ConsumeAndStoreUntil(tok::l_brace, tok::unknown, Toks); ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks); } } return FnD; } /// ParseLexedMethodDeclarations - We finished parsing the member /// specification of a top (non-nested) C++ class. Now go over the /// stack of method declarations with some parts for which parsing was /// delayed (such as default arguments) and parse them. void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) { bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; ParseScope TemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); if (HasTemplateScope) Actions.ActOnReenterTemplateScope(CurScope, Class.TagOrTemplate); bool HasClassScope = !Class.TopLevelClass; ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope, HasClassScope); for (; !Class.MethodDecls.empty(); Class.MethodDecls.pop_front()) { LateParsedMethodDeclaration &LM = Class.MethodDecls.front(); // FIXME: For member function templates, we'll need to introduce a // scope for the template parameters. // Start the delayed C++ method declaration Actions.ActOnStartDelayedCXXMethodDeclaration(CurScope, LM.Method); // Introduce the parameters into scope and parse their default // arguments. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope); for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) { // Introduce the parameter into scope. Actions.ActOnDelayedCXXMethodParameter(CurScope, LM.DefaultArgs[I].Param); if (CachedTokens *Toks = LM.DefaultArgs[I].Toks) { // Parse the default argument from its saved token stream. Toks->push_back(Tok); // So that the current token doesn't get lost PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false); // Consume the previously-pushed token. ConsumeAnyToken(); // Consume the '='. assert(Tok.is(tok::equal) && "Default argument not starting with '='"); SourceLocation EqualLoc = ConsumeToken(); OwningExprResult DefArgResult(ParseAssignmentExpression()); if (DefArgResult.isInvalid()) Actions.ActOnParamDefaultArgumentError(LM.DefaultArgs[I].Param); else Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc, move(DefArgResult)); delete Toks; LM.DefaultArgs[I].Toks = 0; } } PrototypeScope.Exit(); // Finish the delayed C++ method declaration. Actions.ActOnFinishDelayedCXXMethodDeclaration(CurScope, LM.Method); } for (unsigned I = 0, N = Class.NestedClasses.size(); I != N; ++I) ParseLexedMethodDeclarations(*Class.NestedClasses[I]); } /// ParseLexedMethodDefs - We finished parsing the member specification of a top /// (non-nested) C++ class. Now go over the stack of lexed methods that were /// collected during its parsing and parse them all. void Parser::ParseLexedMethodDefs(ParsingClass &Class) { bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; ParseScope TemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); if (HasTemplateScope) Actions.ActOnReenterTemplateScope(CurScope, Class.TagOrTemplate); bool HasClassScope = !Class.TopLevelClass; ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope, HasClassScope); for (; !Class.MethodDefs.empty(); Class.MethodDefs.pop_front()) { LexedMethod &LM = Class.MethodDefs.front(); assert(!LM.Toks.empty() && "Empty body!"); // Append the current token at the end of the new token stream so that it // doesn't get lost. LM.Toks.push_back(Tok); PP.EnterTokenStream(&LM.Toks.front(), LM.Toks.size(), true, false); // Consume the previously pushed token. ConsumeAnyToken(); assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) && "Inline method not starting with '{', ':' or 'try'"); // Parse the method body. Function body parsing code is similar enough // to be re-used for method bodies as well. ParseScope FnScope(this, Scope::FnScope|Scope::DeclScope); Actions.ActOnStartOfFunctionDef(CurScope, LM.D); if (Tok.is(tok::kw_try)) { ParseFunctionTryBlock(LM.D); continue; } if (Tok.is(tok::colon)) ParseConstructorInitializer(LM.D); // FIXME: What if ParseConstructorInitializer doesn't leave us with a '{'?? ParseFunctionStatementBody(LM.D); } for (unsigned I = 0, N = Class.NestedClasses.size(); I != N; ++I) ParseLexedMethodDefs(*Class.NestedClasses[I]); } /// ConsumeAndStoreUntil - Consume and store the token at the passed token /// container until the token 'T' is reached (which gets /// consumed/stored too, if ConsumeFinalToken). /// If EarlyAbortIf is specified, then we will stop early if we find that /// token at the top level. /// Returns true if token 'T1' or 'T2' was found. /// NOTE: This is a specialized version of Parser::SkipUntil. bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2, CachedTokens &Toks, tok::TokenKind EarlyAbortIf, bool ConsumeFinalToken) { // We always want this function to consume at least one token if the first // token isn't T and if not at EOF. bool isFirstTokenConsumed = true; while (1) { // If we found one of the tokens, stop and return true. if (Tok.is(T1) || Tok.is(T2)) { if (ConsumeFinalToken) { Toks.push_back(Tok); ConsumeAnyToken(); } return true; } // If we found the early-abort token, return. if (Tok.is(EarlyAbortIf)) return false; switch (Tok.getKind()) { case tok::eof: // Ran out of tokens. return false; case tok::l_paren: // Recursively consume properly-nested parens. Toks.push_back(Tok); ConsumeParen(); ConsumeAndStoreUntil(tok::r_paren, tok::unknown, Toks); break; case tok::l_square: // Recursively consume properly-nested square brackets. Toks.push_back(Tok); ConsumeBracket(); ConsumeAndStoreUntil(tok::r_square, tok::unknown, Toks); break; case tok::l_brace: // Recursively consume properly-nested braces. Toks.push_back(Tok); ConsumeBrace(); ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks); break; // Okay, we found a ']' or '}' or ')', which we think should be balanced. // Since the user wasn't looking for this token (if they were, it would // already be handled), this isn't balanced. If there is a LHS token at a // higher level, we will assume that this matches the unbalanced token // and return it. Otherwise, this is a spurious RHS token, which we skip. case tok::r_paren: if (ParenCount && !isFirstTokenConsumed) return false; // Matches something. Toks.push_back(Tok); ConsumeParen(); break; case tok::r_square: if (BracketCount && !isFirstTokenConsumed) return false; // Matches something. Toks.push_back(Tok); ConsumeBracket(); break; case tok::r_brace: if (BraceCount && !isFirstTokenConsumed) return false; // Matches something. Toks.push_back(Tok); ConsumeBrace(); break; case tok::string_literal: case tok::wide_string_literal: Toks.push_back(Tok); ConsumeStringToken(); break; default: // consume this token. Toks.push_back(Tok); ConsumeToken(); break; } isFirstTokenConsumed = false; } }