assert(!isActive && "Forgot to call Commit or Revert!");
}
};
+ class UnannotatedTentativeParsingAction;
/// ObjCDeclContextSwitch - An object used to switch context from
/// an objective-c decl context to its enclosing decl context and
void DeallocateParsedClasses(ParsingClass *Class);
void PopParsingClass(Sema::ParsingClassState);
+ enum CachedInitKind {
+ CIK_DefaultArgument,
+ CIK_DefaultInitializer
+ };
+
NamedDecl *ParseCXXInlineMethodDef(AccessSpecifier AS,
AttributeList *AccessAttrs,
ParsingDeclarator &D,
void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI);
void ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod);
bool ConsumeAndStoreFunctionPrologue(CachedTokens &Toks);
+ bool ConsumeAndStoreInitializer(CachedTokens &Toks, CachedInitKind CIK);
+ bool ConsumeAndStoreConditional(CachedTokens &Toks);
bool ConsumeAndStoreUntil(tok::TokenKind T1,
CachedTokens &Toks,
bool StopAtSemi = true,
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(),
bool *HasMissingTypename = 0);
+ /// Given that isCXXDeclarationSpecifier returns \c TPResult::True or
+ /// \c TPResult::Ambiguous, determine whether the decl-specifier would be
+ /// a type-specifier other than a cv-qualifier.
+ bool isCXXDeclarationSpecifierAType();
+
/// \brief Determine whether an identifier has been tentatively declared as a
/// non-type. Such tentative declarations should not be found to name a type
/// during a tentative parse, but also should not be annotated as a non-type.
// that more tentative parsing is necessary for disambiguation.
// They all consume tokens, so backtracking should be used after calling them.
- TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0);
TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl);
TPResult TryParseTypeofSpecifier();
TPResult TryParseProtocolQualifiers();
+ TPResult TryParsePtrOperatorSeq();
+ TPResult TryParseOperatorId();
TPResult TryParseInitDeclaratorList();
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true);
- TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
+ TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0,
+ bool VersusTemplateArg = false);
TPResult TryParseFunctionDeclarator();
TPResult TryParseBracketDeclarator();
+ TPResult TryConsumeDeclarationSpecifier();
public:
TypeResult ParseTypeName(SourceRange *Range = 0,
/// \brief RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
- /// deduction.`
+ /// deduction.
class SFINAETrap {
Sema &SemaRef;
unsigned PrevSFINAEErrors;
}
};
+ /// \brief RAII class used to indicate that we are performing provisional
+ /// semantic analysis to determine the validity of a construct, so
+ /// typo-correction and diagnostics in the immediate context (not within
+ /// implicitly-instantiated templates) should be suppressed.
+ class TentativeAnalysisScope {
+ Sema &SemaRef;
+ // FIXME: Using a SFINAETrap for this is a hack.
+ SFINAETrap Trap;
+ bool PrevDisableTypoCorrection;
+ public:
+ explicit TentativeAnalysisScope(Sema &SemaRef)
+ : SemaRef(SemaRef), Trap(SemaRef, true),
+ PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) {
+ SemaRef.DisableTypoCorrection = true;
+ }
+ ~TentativeAnalysisScope() {
+ SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection;
+ }
+ };
+
/// \brief The current instantiation scope used to store local
/// variables.
LocalInstantiationScope *CurrentInstantiationScope;
+ /// \brief Tracks whether we are in a context where typo correction is
+ /// disabled.
+ bool DisableTypoCorrection;
+
/// \brief The number of typos corrected by CorrectTypo.
unsigned TyposCorrected;
ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true);
} else {
// Consume everything up to (but excluding) the comma or semicolon.
- ConsumeAndStoreUntil(tok::comma, Toks, /*StopAtSemi=*/true,
- /*ConsumeFinalToken=*/false);
+ ConsumeAndStoreInitializer(Toks, CIK_DefaultInitializer);
}
// Store an artificial EOF token to ensure that we don't run off the end of
else {
if (Tok.is(tok::cxx_defaultarg_end))
ConsumeToken();
- else
- Diag(Tok.getLocation(), diag::err_default_arg_unparsed);
+ else {
+ // The last two tokens are the terminator and the saved value of
+ // Tok; the last token in the default argument is the one before
+ // those.
+ assert(Toks->size() >= 3 && "expected a token in default arg");
+ Diag(Tok.getLocation(), diag::err_default_arg_unparsed)
+ << SourceRange(Tok.getLocation(),
+ (*Toks)[Toks->size() - 3].getLocation());
+ }
Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc,
DefArgResult.take());
}
}
}
}
+
+/// \brief Consume and store tokens from the '?' to the ':' in a conditional
+/// expression.
+bool Parser::ConsumeAndStoreConditional(CachedTokens &Toks) {
+ // Consume '?'.
+ assert(Tok.is(tok::question));
+ Toks.push_back(Tok);
+ ConsumeToken();
+
+ while (Tok.isNot(tok::colon)) {
+ if (!ConsumeAndStoreUntil(tok::question, tok::colon, Toks, /*StopAtSemi*/true,
+ /*ConsumeFinalToken*/false))
+ return false;
+
+ // If we found a nested conditional, consume it.
+ if (Tok.is(tok::question) && !ConsumeAndStoreConditional(Toks))
+ return false;
+ }
+
+ // Consume ':'.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ return true;
+}
+
+/// \brief A tentative parsing action that can also revert token annotations.
+class Parser::UnannotatedTentativeParsingAction : public TentativeParsingAction {
+public:
+ explicit UnannotatedTentativeParsingAction(Parser &Self,
+ tok::TokenKind EndKind)
+ : TentativeParsingAction(Self), Self(Self), EndKind(EndKind) {
+ // Stash away the old token stream, so we can restore it once the
+ // tentative parse is complete.
+ TentativeParsingAction Inner(Self);
+ Self.ConsumeAndStoreUntil(EndKind, Toks, true, /*ConsumeFinalToken*/false);
+ Inner.Revert();
+ }
+
+ void RevertAnnotations() {
+ Revert();
+
+ // Put back the original tokens.
+ Self.SkipUntil(EndKind, true, /*DontConsume*/true);
+ if (Toks.size()) {
+ Token *Buffer = new Token[Toks.size()];
+ std::copy(Toks.begin() + 1, Toks.end(), Buffer);
+ Buffer[Toks.size() - 1] = Self.Tok;
+ Self.PP.EnterTokenStream(Buffer, Toks.size(), true, /*Owned*/true);
+
+ Self.Tok = Toks.front();
+ }
+ }
+
+private:
+ Parser &Self;
+ CachedTokens Toks;
+ tok::TokenKind EndKind;
+};
+
+/// ConsumeAndStoreInitializer - Consume and store the token at the passed token
+/// container until the end of the current initializer expression (either a
+/// default argument or an in-class initializer for a non-static data member).
+/// The final token is not consumed.
+bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
+ CachedInitKind CIK) {
+ // We always want this function to consume at least one token if not at EOF.
+ bool IsFirstTokenConsumed = true;
+
+ // Number of possible unclosed <s we've seen so far. These might be templates,
+ // and might not, but if there were none of them (or we know for sure that
+ // we're within a template), we can avoid a tentative parse.
+ unsigned AngleCount = 0;
+ unsigned KnownTemplateCount = 0;
+
+ while (1) {
+ switch (Tok.getKind()) {
+ case tok::comma:
+ // If we might be in a template, perform a tentative parse to check.
+ if (!AngleCount)
+ // Not a template argument: this is the end of the initializer.
+ return true;
+ if (KnownTemplateCount)
+ goto consume_token;
+
+ // We hit a comma inside angle brackets. This is the hard case. The
+ // rule we follow is:
+ // * For a default argument, if the tokens after the comma form a
+ // syntactically-valid parameter-declaration-clause, in which each
+ // parameter has an initializer, then this comma ends the default
+ // argument.
+ // * For a default initializer, if the tokens after the comma form a
+ // syntactically-valid init-declarator-list, then this comma ends
+ // the default initializer.
+ {
+ UnannotatedTentativeParsingAction PA(*this,
+ CIK == CIK_DefaultInitializer
+ ? tok::semi : tok::r_paren);
+ Sema::TentativeAnalysisScope Scope(Actions);
+
+ TPResult Result = TPResult::Error();
+ ConsumeToken();
+ switch (CIK) {
+ case CIK_DefaultInitializer:
+ Result = TryParseInitDeclaratorList();
+ // If we parsed a complete, ambiguous init-declarator-list, this
+ // is only syntactically-valid if it's followed by a semicolon.
+ if (Result == TPResult::Ambiguous() && Tok.isNot(tok::semi))
+ Result = TPResult::False();
+ break;
+
+ case CIK_DefaultArgument:
+ bool InvalidAsDeclaration = false;
+ Result = TryParseParameterDeclarationClause(
+ &InvalidAsDeclaration, /*VersusTemplateArgument*/true);
+ // If this is an expression or a declaration with a missing
+ // 'typename', assume it's not a declaration.
+ if (Result == TPResult::Ambiguous() && InvalidAsDeclaration)
+ Result = TPResult::False();
+ break;
+ }
+
+ // If what follows could be a declaration, it is a declaration.
+ if (Result != TPResult::False() && Result != TPResult::Error()) {
+ PA.Revert();
+ return true;
+ }
+
+ // In the uncommon case that we decide the following tokens are part
+ // of a template argument, revert any annotations we've performed in
+ // those tokens. We're not going to look them up until we've parsed
+ // the rest of the class, and that might add more declarations.
+ PA.RevertAnnotations();
+ }
+
+ // Keep going. We know we're inside a template argument list now.
+ ++KnownTemplateCount;
+ goto consume_token;
+
+ case tok::eof:
+ // Ran out of tokens.
+ return false;
+
+ case tok::less:
+ // FIXME: A '<' can only start a template-id if it's preceded by an
+ // identifier, an operator-function-id, or a literal-operator-id.
+ ++AngleCount;
+ goto consume_token;
+
+ case tok::question:
+ // In 'a ? b : c', 'b' can contain an unparenthesized comma. If it does,
+ // that is *never* the end of the initializer. Skip to the ':'.
+ if (!ConsumeAndStoreConditional(Toks))
+ return false;
+ break;
+
+ case tok::greatergreatergreater:
+ if (!getLangOpts().CPlusPlus11)
+ goto consume_token;
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ // Fall through.
+ case tok::greatergreater:
+ if (!getLangOpts().CPlusPlus11)
+ goto consume_token;
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ // Fall through.
+ case tok::greater:
+ if (AngleCount) --AngleCount;
+ if (KnownTemplateCount) --KnownTemplateCount;
+ goto consume_token;
+
+ case tok::kw_template:
+ // 'template' identifier '<' is known to start a template argument list,
+ // and can be used to disambiguate the parse.
+ // FIXME: Support all forms of 'template' unqualified-id '<'.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ if (Tok.is(tok::identifier)) {
+ Toks.push_back(Tok);
+ ConsumeToken();
+ if (Tok.is(tok::less)) {
+ ++KnownTemplateCount;
+ Toks.push_back(Tok);
+ ConsumeToken();
+ }
+ }
+ break;
+
+ case tok::kw_operator:
+ // If 'operator' precedes other punctuation, that punctuation loses
+ // its special behavior.
+ Toks.push_back(Tok);
+ ConsumeToken();
+ switch (Tok.getKind()) {
+ case tok::comma:
+ case tok::greatergreatergreater:
+ case tok::greatergreater:
+ case tok::greater:
+ case tok::less:
+ Toks.push_back(Tok);
+ ConsumeToken();
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case tok::l_paren:
+ // Recursively consume properly-nested parens.
+ Toks.push_back(Tok);
+ ConsumeParen();
+ ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_square:
+ // Recursively consume properly-nested square brackets.
+ Toks.push_back(Tok);
+ ConsumeBracket();
+ ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/false);
+ break;
+ case tok::l_brace:
+ // Recursively consume properly-nested braces.
+ Toks.push_back(Tok);
+ ConsumeBrace();
+ ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
+ 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 (CIK == CIK_DefaultArgument)
+ return true; // End of the default argument.
+ if (ParenCount && !IsFirstTokenConsumed)
+ return false; // Matches something.
+ goto consume_token;
+ case tok::r_square:
+ if (BracketCount && !IsFirstTokenConsumed)
+ return false; // Matches something.
+ goto consume_token;
+ case tok::r_brace:
+ if (BraceCount && !IsFirstTokenConsumed)
+ return false; // Matches something.
+ goto consume_token;
+
+ case tok::code_completion:
+ Toks.push_back(Tok);
+ ConsumeCodeCompletionToken();
+ break;
+
+ case tok::string_literal:
+ case tok::wide_string_literal:
+ case tok::utf8_string_literal:
+ case tok::utf16_string_literal:
+ case tok::utf32_string_literal:
+ Toks.push_back(Tok);
+ ConsumeStringToken();
+ break;
+ case tok::semi:
+ if (CIK == CIK_DefaultInitializer)
+ return true; // End of the default initializer.
+ // FALL THROUGH.
+ default:
+ consume_token:
+ Toks.push_back(Tok);
+ ConsumeToken();
+ break;
+ }
+ IsFirstTokenConsumed = false;
+ }
+}
// FIXME: Can we use a smart pointer for Toks?
DefArgToks = new CachedTokens;
- if (!ConsumeAndStoreUntil(tok::comma, tok::r_paren, *DefArgToks,
- /*StopAtSemi=*/true,
- /*ConsumeFinalToken=*/false)) {
+ if (!ConsumeAndStoreInitializer(*DefArgToks, CIK_DefaultArgument)) {
delete DefArgToks;
DefArgToks = 0;
Actions.ActOnParamDefaultArgumentError(Param);
return TPR == TPResult::True();
}
+/// Try to consume a token sequence that we've already identified as
+/// (potentially) starting a decl-specifier.
+Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
+ switch (Tok.getKind()) {
+ case tok::kw__Atomic:
+ if (NextToken().isNot(tok::l_paren)) {
+ ConsumeToken();
+ break;
+ }
+ // Fall through.
+ case tok::kw_typeof:
+ case tok::kw___attribute:
+ case tok::kw___underlying_type: {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return TPResult::Error();
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, false))
+ return TPResult::Error();
+ break;
+ }
+
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ case tok::kw_enum:
+ // elaborated-type-specifier:
+ // class-key attribute-specifier-seq[opt]
+ // nested-name-specifier[opt] identifier
+ // class-key nested-name-specifier[opt] template[opt] simple-template-id
+ // enum nested-name-specifier[opt] identifier
+ //
+ // FIXME: We don't support class-specifiers nor enum-specifiers here.
+ ConsumeToken();
+
+ // Skip attributes.
+ while (Tok.is(tok::l_square) || Tok.is(tok::kw___attribute) ||
+ Tok.is(tok::kw___declspec) || Tok.is(tok::kw_alignas)) {
+ if (Tok.is(tok::l_square)) {
+ ConsumeBracket();
+ if (!SkipUntil(tok::r_square, false))
+ return TPResult::Error();
+ } else {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return TPResult::Error();
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren, false))
+ return TPResult::Error();
+ }
+ }
+
+ if (TryAnnotateCXXScopeToken())
+ return TPResult::Error();
+ if (Tok.is(tok::annot_cxxscope))
+ ConsumeToken();
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
+ return TPResult::Error();
+ ConsumeToken();
+ break;
+
+ case tok::annot_cxxscope:
+ ConsumeToken();
+ // Fall through.
+ default:
+ ConsumeToken();
+
+ if (getLangOpts().ObjC1 && Tok.is(tok::less))
+ return TryParseProtocolQualifiers();
+ break;
+ }
+
+ return TPResult::Ambiguous();
+}
+
/// simple-declaration:
/// decl-specifier-seq init-declarator-list[opt] ';'
///
/// attribute-specifier-seqopt type-specifier-seq declarator
///
Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
- if (Tok.is(tok::kw_typeof))
- TryParseTypeofSpecifier();
- else {
- if (Tok.is(tok::annot_cxxscope))
- ConsumeToken();
- ConsumeToken();
-
- if (getLangOpts().ObjC1 && Tok.is(tok::less))
- TryParseProtocolQualifiers();
- }
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+ return TPResult::Error();
// Two decl-specifiers in a row conclusively disambiguate this as being a
// simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the
// expression can never be followed directly by a braced-init-list.
return TPResult::True();
} else if (Tok.is(tok::equal) || isTokIdentifier_in()) {
- // MSVC and g++ won't examine the rest of declarators if '=' is
+ // MSVC and g++ won't examine the rest of declarators if '=' is
// encountered; they just conclude that we have a declaration.
// EDG parses the initializer completely, which is the proper behavior
// for this case.
// At present, Clang follows MSVC and g++, since the parser does not have
// the ability to parse an expression fully without recording the
// results of that parse.
- // Also allow 'in' after on objective-c declaration as in:
- // for (int (^b)(void) in array). Ideally this should be done in the
+ // FIXME: Handle this case correctly.
+ //
+ // Also allow 'in' after an Objective-C declaration as in:
+ // for (int (^b)(void) in array). Ideally this should be done in the
// context of parsing for-init-statement of a foreach statement only. But,
// in any other context 'in' is invalid after a declaration and parser
// issues the error regardless of outcome of this decision.
- // FIXME. Change if above assumption does not hold.
+ // FIXME: Change if above assumption does not hold.
return TPResult::True();
}
TentativeParsingAction PA(*this);
// type-specifier-seq
- if (Tok.is(tok::kw_typeof))
- TryParseTypeofSpecifier();
- else {
- ConsumeToken();
-
- if (getLangOpts().ObjC1 && Tok.is(tok::less))
- TryParseProtocolQualifiers();
- }
+ TryConsumeDeclarationSpecifier();
assert(Tok.is(tok::l_paren) && "Expected '('");
// declarator
TentativeParsingAction PA(*this);
// type-specifier-seq
- if (Tok.is(tok::kw_typeof))
- TryParseTypeofSpecifier();
- else {
- ConsumeToken();
-
- if (getLangOpts().ObjC1 && Tok.is(tok::less))
- TryParseProtocolQualifiers();
- }
-
+ TryConsumeDeclarationSpecifier();
assert(Tok.is(tok::l_paren) && "Expected '('");
// declarator
return CAK_NotAttributeSpecifier;
}
+Parser::TPResult Parser::TryParsePtrOperatorSeq() {
+ while (true) {
+ if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier))
+ if (TryAnnotateCXXScopeToken(true))
+ return TPResult::Error();
+
+ if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) ||
+ Tok.is(tok::ampamp) ||
+ (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
+ // ptr-operator
+ ConsumeToken();
+ while (Tok.is(tok::kw_const) ||
+ Tok.is(tok::kw_volatile) ||
+ Tok.is(tok::kw_restrict))
+ ConsumeToken();
+ } else {
+ return TPResult::True();
+ }
+ }
+}
+
+/// operator-function-id:
+/// 'operator' operator
+///
+/// operator: one of
+/// new delete new[] delete[] + - * / % ^ [...]
+///
+/// conversion-function-id:
+/// 'operator' conversion-type-id
+///
+/// conversion-type-id:
+/// type-specifier-seq conversion-declarator[opt]
+///
+/// conversion-declarator:
+/// ptr-operator conversion-declarator[opt]
+///
+/// literal-operator-id:
+/// 'operator' string-literal identifier
+/// 'operator' user-defined-string-literal
+Parser::TPResult Parser::TryParseOperatorId() {
+ assert(Tok.is(tok::kw_operator));
+ ConsumeToken();
+
+ // Maybe this is an operator-function-id.
+ switch (Tok.getKind()) {
+ case tok::kw_new: case tok::kw_delete:
+ ConsumeToken();
+ if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
+ ConsumeBracket();
+ ConsumeBracket();
+ }
+ return TPResult::True();
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \
+ case tok::Token:
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly)
+#include "clang/Basic/OperatorKinds.def"
+ ConsumeToken();
+ return TPResult::True();
+
+ case tok::l_square:
+ if (NextToken().is(tok::r_square)) {
+ ConsumeBracket();
+ ConsumeBracket();
+ return TPResult::True();
+ }
+ break;
+
+ case tok::l_paren:
+ if (NextToken().is(tok::r_paren)) {
+ ConsumeParen();
+ ConsumeParen();
+ return TPResult::True();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Maybe this is a literal-operator-id.
+ if (getLangOpts().CPlusPlus11 && isTokenStringLiteral()) {
+ bool FoundUDSuffix = false;
+ do {
+ FoundUDSuffix |= Tok.hasUDSuffix();
+ ConsumeStringToken();
+ } while (isTokenStringLiteral());
+
+ if (!FoundUDSuffix) {
+ if (Tok.is(tok::identifier))
+ ConsumeToken();
+ else
+ return TPResult::Error();
+ }
+ return TPResult::True();
+ }
+
+ // Maybe this is a conversion-function-id.
+ bool AnyDeclSpecifiers = false;
+ while (true) {
+ TPResult TPR = isCXXDeclarationSpecifier();
+ if (TPR == TPResult::Error())
+ return TPR;
+ if (TPR == TPResult::False()) {
+ if (!AnyDeclSpecifiers)
+ return TPResult::Error();
+ break;
+ }
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+ return TPResult::Error();
+ AnyDeclSpecifiers = true;
+ }
+ return TryParsePtrOperatorSeq();
+}
+
/// declarator:
/// direct-declarator
/// ptr-operator declarator
///
/// unqualified-id:
/// identifier
-/// operator-function-id [TODO]
-/// conversion-function-id [TODO]
+/// operator-function-id
+/// conversion-function-id
+/// literal-operator-id
/// '~' class-name [TODO]
+/// '~' decltype-specifier [TODO]
/// template-id [TODO]
///
Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
// declarator:
// direct-declarator
// ptr-operator declarator
-
- while (1) {
- if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier))
- if (TryAnnotateCXXScopeToken(true))
- return TPResult::Error();
-
- if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) ||
- Tok.is(tok::ampamp) ||
- (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
- // ptr-operator
- ConsumeToken();
- while (Tok.is(tok::kw_const) ||
- Tok.is(tok::kw_volatile) ||
- Tok.is(tok::kw_restrict))
- ConsumeToken();
- } else {
- break;
- }
- }
+ if (TryParsePtrOperatorSeq() == TPResult::Error())
+ return TPResult::Error();
// direct-declarator:
// direct-abstract-declarator:
if (Tok.is(tok::ellipsis))
ConsumeToken();
-
- if ((Tok.is(tok::identifier) ||
- (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) &&
+
+ if ((Tok.is(tok::identifier) || Tok.is(tok::kw_operator) ||
+ (Tok.is(tok::annot_cxxscope) && (NextToken().is(tok::identifier) ||
+ NextToken().is(tok::kw_operator)))) &&
mayHaveIdentifier) {
// declarator-id
if (Tok.is(tok::annot_cxxscope))
ConsumeToken();
- else
+ else if (Tok.is(tok::identifier))
TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
- ConsumeToken();
+ if (Tok.is(tok::kw_operator)) {
+ if (TryParseOperatorId() == TPResult::Error())
+ return TPResult::Error();
+ } else
+ ConsumeToken();
} else if (Tok.is(tok::l_paren)) {
ConsumeParen();
if (mayBeAbstract &&
case tok::kw_wchar_t:
case tok::kw_char16_t:
case tok::kw_char32_t:
- case tok::kw___underlying_type:
case tok::kw__Decimal32:
case tok::kw__Decimal64:
case tok::kw__Decimal128:
+ case tok::kw___interface:
case tok::kw___thread:
case tok::kw_thread_local:
case tok::kw__Thread_local:
case tok::kw_typeof:
+ case tok::kw___underlying_type:
case tok::kw___cdecl:
case tok::kw___stdcall:
case tok::kw___fastcall:
case tok::kw_class:
case tok::kw_struct:
case tok::kw_union:
+ case tok::kw___interface:
// enum-specifier
case tok::kw_enum:
// cv-qualifier
}
}
+bool Parser::isCXXDeclarationSpecifierAType() {
+ switch (Tok.getKind()) {
+ // typename-specifier
+ case tok::annot_decltype:
+ case tok::annot_template_id:
+ case tok::annot_typename:
+ case tok::kw_typeof:
+ case tok::kw___underlying_type:
+ return true;
+
+ // elaborated-type-specifier
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ case tok::kw___interface:
+ case tok::kw_enum:
+ return true;
+
+ // simple-type-specifier
+ case tok::kw_char:
+ case tok::kw_wchar_t:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ case tok::kw_bool:
+ case tok::kw_short:
+ case tok::kw_int:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw_void:
+ case tok::kw___unknown_anytype:
+ return true;
+
+ case tok::kw_auto:
+ return getLangOpts().CPlusPlus11;
+
+ case tok::kw__Atomic:
+ // "_Atomic foo"
+ return NextToken().is(tok::l_paren);
+
+ default:
+ return false;
+ }
+}
+
/// [GNU] typeof-specifier:
/// 'typeof' '(' expressions ')'
/// 'typeof' '(' type-name ')'
return TPResult::Error();
}
-Parser::TPResult
-Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) {
- TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
- HasMissingTypename);
- if (TPR != TPResult::Ambiguous())
- return TPR;
-
- if (Tok.is(tok::kw_typeof))
- TryParseTypeofSpecifier();
- else {
- if (Tok.is(tok::annot_cxxscope))
- ConsumeToken();
- ConsumeToken();
-
- if (getLangOpts().ObjC1 && Tok.is(tok::less))
- TryParseProtocolQualifiers();
- }
-
- return TPResult::Ambiguous();
-}
-
/// isCXXFunctionDeclarator - Disambiguates between a function declarator or
/// a constructor-style initializer, when parsing declaration statements.
/// Returns true for function declarator and false for constructor-style
/// attributes[opt] '=' assignment-expression
///
Parser::TPResult
-Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
+Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
+ bool VersusTemplateArgument) {
if (Tok.is(tok::r_paren))
return TPResult::Ambiguous();
// decl-specifier-seq
// A parameter-declaration's initializer must be preceded by an '=', so
// decl-specifier-seq '{' is not a parameter in C++11.
- TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration);
- if (TPR != TPResult::Ambiguous())
+ TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
+ InvalidAsDeclaration);
+
+ if (VersusTemplateArgument && TPR == TPResult::True()) {
+ // Consume the decl-specifier-seq. We have to look past it, since a
+ // type-id might appear here in a template argument.
+ bool SeenType = false;
+ do {
+ SeenType |= isCXXDeclarationSpecifierAType();
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+ return TPResult::Error();
+
+ // If we see a parameter name, this can't be a template argument.
+ if (SeenType && Tok.is(tok::identifier))
+ return TPResult::True();
+
+ TPR = isCXXDeclarationSpecifier(TPResult::False(),
+ InvalidAsDeclaration);
+ if (TPR == TPResult::Error())
+ return TPR;
+ } while (TPR != TPResult::False());
+ } else if (TPR == TPResult::Ambiguous()) {
+ // Disambiguate what follows the decl-specifier.
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error())
+ return TPResult::Error();
+ } else
return TPR;
// declarator
if (Tok.is(tok::kw___attribute))
return TPResult::True();
+ // If we're disambiguating a template argument in a default argument in
+ // a class definition versus a parameter declaration, an '=' here
+ // disambiguates the parse one way or the other.
+ // If this is a parameter, it must have a default argument because
+ // (a) the previous parameter did, and
+ // (b) this must be the first declaration of the function, so we can't
+ // inherit any default arguments from elsewhere.
+ // If we see an ')', then we've reached the end of a
+ // parameter-declaration-clause, and the last param is missing its default
+ // argument.
+ if (VersusTemplateArgument)
+ return (Tok.is(tok::equal) || Tok.is(tok::r_paren)) ? TPResult::True()
+ : TPResult::False();
+
if (Tok.is(tok::equal)) {
// '=' assignment-expression
// Parse through assignment-expression.
+ // FIXME: assignment-expression may contain an unparenthesized comma.
if (!SkipUntil(tok::comma, tok::r_paren, true/*StopAtSemi*/,
true/*DontConsume*/))
return TPResult::Error();
NumSFINAEErrors(0), InFunctionDeclarator(0),
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
- CurrentInstantiationScope(0), TyposCorrected(0),
- AnalysisWarnings(*this), VarDataSharingAttributesStack(0), CurScope(0),
+ CurrentInstantiationScope(0), DisableTypoCorrection(false),
+ TyposCorrected(0), AnalysisWarnings(*this),
+ VarDataSharingAttributesStack(0), CurScope(0),
Ident_super(0), Ident___float128(0)
{
TUScope = 0;
return Correction;
}
- if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking)
+ if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking ||
+ DisableTypoCorrection)
return TypoCorrection();
// In Microsoft mode, don't perform typo correction in a template member
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -verify %s
+
+template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
+
+int val;
+int foobar;
+struct S {
+ int k1 = a < b < c, d > ::val, e1;
+ int k2 = a < b, c < d > ::val, e2;
+ int k3 = b < a < c, d > ::val, e3;
+ int k4 = b < c, x, y = d > ::val, e4;
+ int k5 = T1 < b, &S::operator=(int); // expected-error {{extra qualification}}
+ int k6 = T2 < b, &S::operator= >::val;
+ int k7 = T1 < b, &S::operator>(int); // expected-error {{extra qualification}}
+ int k8 = T2 < b, &S::operator> >::val;
+ int k9 = T3 < a < b, c >> (d), e5 = 1 > (e4);
+ int k10 = 0 < T3 < a < b, c >> (d
+ ) // expected-error {{expected ';' at end of declaration}}
+ , a > (e4);
+ int k11 = 0 < 1, c<3>::*ptr;
+ int k12 = e < 0, int a<b<c>::* >(), e11;
+
+ void f1(
+ int k1 = a < b < c, d > ::val,
+ int k2 = b < a < c, d > ::val,
+ int k3 = b < c, int x = 0 > ::val,
+ int k4 = a < b, T3 < int > >(), // expected-error {{must be an expression}}
+ int k5 = a < b, c < d > ::val,
+ int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}}
+ );
+
+ void f2a(
+ // T3<int> here is a parameter type, so must be declared before it is used.
+ int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}}
+ );
+
+ template<typename, int=0> struct T3 { T3(int); operator int(); };
+
+ void f2b(
+ int k1 = c < b, T3 < int > x = 0 // ok
+ );
+
+ // This is a one-parameter function. Ensure we don't typo-correct it to
+ // int = a < b, c < foobar > ()
+ // ... which would be a function with two parameters.
+ int f3(int = a < b, c < goobar > ());
+ static constexpr int (S::*f3_test)(int) = &S::f3;
+
+ void f4(
+ int k1 = a<1,2>::val,
+ int missing_default // expected-error {{missing default argument on parameter}}
+ );
+
+ void f5(
+ int k1 = b < c,
+ int missing_default // expected-error {{missing default argument on parameter}}
+ );
+
+ void f6(
+ int k = b < c,
+ unsigned int (missing_default) // expected-error {{missing default argument on parameter}}
+ );
+
+ template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}}
+ static const int b = 0, c = 1, d = 2, goobar = 3;
+ template<int, typename> struct e { operator int(); };
+
+ int mp1 = 0 < 1,
+ a<b<c,b<c>::*mp2,
+ mp3 = 0 > a<b<c>::val,
+ a<b<c,b<c>::*mp4 = 0,
+ a<b<c,b<c>::*mp5 {0},
+ a<b<c,b<c>::*mp6;
+
+ int np1 = e<0, int a<b<c,b<c>::*>();
+
+ static const int T1 = 4;
+ template<int, int &(S::*)(int)> struct T2 { static const int val = 0; };
+};
+
+namespace NoAnnotationTokens {
+ template<bool> struct Bool { Bool(int); };
+ static const bool in_class = false;
+
+ struct Test {
+ // Check we don't keep around a Bool<false> annotation token here.
+ int f(Bool<true> = X<Y, Bool<in_class> >(0));
+
+ // But it's OK if we do here.
+ int g(Bool<true> = Z<Y, Bool<in_class> = Bool<false>(0));
+
+ static const bool in_class = true;
+ template<int, typename U> using X = U;
+ static const int Y = 0, Z = 0;
+ };
+}
+
+namespace ImplicitInstantiation {
+ template<typename T> struct HasError { typename T::error error; }; // expected-error {{has no members}}
+
+ struct S {
+ // This triggers the instantiation of the outer HasError<int> during
+ // disambiguation, even though it uses the inner HasError<int>.
+ void f(int a = X<Y, HasError<int>::Z >()); // expected-note {{in instantiation of}}
+
+ template<typename, typename> struct X { operator int(); };
+ typedef int Y;
+ template<typename> struct HasError { typedef int Z; };
+ };
+
+ HasError<int> hei;
+}
+
+namespace CWG325 {
+ template <int A, typename B> struct T { static int i; operator int(); };
+ class C {
+ int Foo (int i = T<1, int>::i);
+ };
+
+ class D {
+ int Foo (int i = T<1, int>::i);
+ template <int A, typename B> struct T {static int i;};
+ };
+
+ const int a = 0;
+ typedef int b;
+ T<a,b> c;
+ struct E {
+ int n = T<a,b>(c);
+ };
+}
+
+namespace Operators {
+ struct Y {};
+ constexpr int operator,(const Y&, const Y&) { return 8; }
+ constexpr int operator>(const Y&, const Y&) { return 8; }
+ constexpr int operator<(const Y&, const Y&) { return 8; }
+ constexpr int operator>>(const Y&, const Y&) { return 8; }
+
+ struct X {
+ typedef int (*Fn)(const Y&, const Y&);
+
+ Fn a = operator,, b = operator<, c = operator>;
+ void f(Fn a = operator,, Fn b = operator<, Fn c = operator>);
+
+ int k1 = T1<0, operator<, operator>, operator<>::val, l1;
+ int k2 = T1<0, operator>, operator,, operator,>::val, l2;
+ int k3 = T2<0, operator,(Y{}, Y{}), operator<(Y{}, Y{})>::val, l3;
+ int k4 = T2<0, operator>(Y{}, Y{}), operator,(Y{}, Y{})>::val, l4;
+ int k5 = T3<0, operator>>>::val, l5;
+ int k6 = T4<0, T3<0, operator>>>>::val, l6;
+
+ template<int, Fn, Fn, Fn> struct T1 { enum { val }; };
+ template<int, int, int> struct T2 { enum { val }; };
+ template<int, Fn> struct T3 { enum { val }; };
+ template<int, typename T> struct T4 : T {};
+ };
+}
+
+namespace ElaboratedTypeSpecifiers {
+ struct S {
+ int f(int x = T<a, struct S>());
+ int g(int x = T<a, class __declspec() C>());
+ int h(int x = T<a, union __attribute__(()) U>());
+ int i(int x = T<a, enum E>());
+ int j(int x = T<a, struct S::template T<0, enum E>>());
+ template <int, typename> struct T { operator int(); };
+ static const int a = 0;
+ enum E {};
+ };
+}
struct X {
void f(int x = 1:); // expected-error {{unexpected end of default argument expression}}
};
+
+// PR13657
+struct T {
+ template <typename A, typename B> struct T1 { enum {V};};
+ template <int A, int B> struct T2 { enum {V}; };
+ template <int, int> static int func(int);
+
+
+ void f1(T1<int, int> = T1<int, int>());
+ void f2(T1<int, double> = T1<int, double>(), T2<0, 5> = T2<0, 5>());
+ void f3(int a = T2<0, (T1<int, int>::V > 10) ? 5 : 6>::V, bool b = 4<5 );
+ void f4(bool a = 1 < 0, bool b = 2 > 0 );
+ void f5(bool a = 1 > T2<0, 0>::V, bool b = T1<int,int>::V < 3, int c = 0);
+ void f6(bool a = T2<0,3>::V < 4, bool b = 4 > T2<0,3>::V);
+ void f7(bool a = T1<int, bool>::V < 3);
+ void f8(int = func<0,1<2>(0), int = 1<0, T1<int,int>(int) = 0);
+};
int a, b;
V1() : a(), b{} {}
};
+
+template <typename, typename> struct T1 { enum {V};};
+template <int, int> struct T2 { enum {V};};
+struct A {
+ T1<int, int> a1 = T1<int, int>(), *a2 = new T1<int,int>;
+ T2<0,0> b1 = T2<0,0>(), b2 = T2<0,0>(), b3;
+ bool c1 = 1 < 2, c2 = 2 < 1, c3 = false;
+ bool d1 = T1<int, T1<int, int>>::V < 3, d2;
+ T1<int, int()> e = T1<int, int()>();
+};