summaryrefslogtreecommitdiff
path: root/clang/lib/Parse/ParseTentative.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Parse/ParseTentative.cpp')
-rw-r--r--clang/lib/Parse/ParseTentative.cpp360
1 files changed, 202 insertions, 158 deletions
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 4d69fb4693fb8..f026f3a1bfb29 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -186,25 +186,10 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
ConsumeToken();
// Skip attributes.
- while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec,
- tok::kw_alignas)) {
- if (Tok.is(tok::l_square)) {
- ConsumeBracket();
- if (!SkipUntil(tok::r_square))
- return TPResult::Error;
- } else {
- ConsumeToken();
- if (Tok.isNot(tok::l_paren))
- return TPResult::Error;
- ConsumeParen();
- if (!SkipUntil(tok::r_paren))
- return TPResult::Error;
- }
- }
+ if (!TrySkipAttributes())
+ return TPResult::Error;
- if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype,
- tok::annot_template_id) &&
- TryAnnotateCXXScopeToken())
+ if (TryAnnotateOptionalCXXScopeToken())
return TPResult::Error;
if (Tok.is(tok::annot_cxxscope))
ConsumeAnnotationToken();
@@ -443,6 +428,38 @@ struct Parser::ConditionDeclarationOrInitStatementState {
}
};
+bool Parser::isEnumBase(bool AllowSemi) {
+ assert(Tok.is(tok::colon) && "should be looking at the ':'");
+
+ RevertingTentativeParsingAction PA(*this);
+ // ':'
+ ConsumeToken();
+
+ // type-specifier-seq
+ bool InvalidAsDeclSpec = false;
+ // FIXME: We could disallow non-type decl-specifiers here, but it makes no
+ // difference: those specifiers are ill-formed regardless of the
+ // interpretation.
+ TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True,
+ &InvalidAsDeclSpec);
+ if (R == TPResult::Ambiguous) {
+ // We either have a decl-specifier followed by '(' or an undeclared
+ // identifier.
+ if (TryConsumeDeclarationSpecifier() == TPResult::Error)
+ return true;
+
+ // If we get to the end of the enum-base, we hit either a '{' or a ';'.
+ // Don't bother checking the enumerator-list.
+ if (Tok.is(tok::l_brace) || (AllowSemi && Tok.is(tok::semi)))
+ return true;
+
+ // A second decl-specifier unambiguously indicatges an enum-base.
+ R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec);
+ }
+
+ return R != TPResult::False;
+}
+
/// Disambiguates between a declaration in a condition, a
/// simple-declaration in an init-statement, and an expression for
/// a condition of a if/switch statement.
@@ -783,19 +800,49 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate,
return CAK_NotAttributeSpecifier;
}
+bool Parser::TrySkipAttributes() {
+ while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec,
+ tok::kw_alignas)) {
+ if (Tok.is(tok::l_square)) {
+ ConsumeBracket();
+ if (Tok.isNot(tok::l_square))
+ return false;
+ ConsumeBracket();
+ if (!SkipUntil(tok::r_square) || Tok.isNot(tok::r_square))
+ return false;
+ // Note that explicitly checking for `[[` and `]]` allows to fail as
+ // expected in the case of the Objective-C message send syntax.
+ ConsumeBracket();
+ } else {
+ ConsumeToken();
+ if (Tok.isNot(tok::l_paren))
+ return false;
+ ConsumeParen();
+ if (!SkipUntil(tok::r_paren))
+ return false;
+ }
+ }
+
+ return true;
+}
+
Parser::TPResult Parser::TryParsePtrOperatorSeq() {
while (true) {
- if (Tok.isOneOf(tok::coloncolon, tok::identifier))
- if (TryAnnotateCXXScopeToken(true))
- return TPResult::Error;
+ if (TryAnnotateOptionalCXXScopeToken(true))
+ return TPResult::Error;
if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) ||
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
// ptr-operator
ConsumeAnyToken();
+
+ // Skip attributes.
+ if (!TrySkipAttributes())
+ return TPResult::Error;
+
while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
tok::kw__Nonnull, tok::kw__Nullable,
- tok::kw__Null_unspecified))
+ tok::kw__Null_unspecified, tok::kw__Atomic))
ConsumeToken();
} else {
return TPResult::True;
@@ -969,10 +1016,16 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
NextToken().is(tok::kw_operator)))) &&
mayHaveIdentifier) {
// declarator-id
- if (Tok.is(tok::annot_cxxscope))
+ if (Tok.is(tok::annot_cxxscope)) {
+ CXXScopeSpec SS;
+ Actions.RestoreNestedNameSpecifierAnnotation(
+ Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+ if (SS.isInvalid())
+ return TPResult::Error;
ConsumeAnnotationToken();
- else if (Tok.is(tok::identifier))
+ } else if (Tok.is(tok::identifier)) {
TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo());
+ }
if (Tok.is(tok::kw_operator)) {
if (TryParseOperatorId() == TPResult::Error)
return TPResult::Error;
@@ -1046,130 +1099,6 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
return TPResult::Ambiguous;
}
-Parser::TPResult
-Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
- switch (Kind) {
- // Obviously starts an expression.
- case tok::numeric_constant:
- case tok::char_constant:
- case tok::wide_char_constant:
- case tok::utf8_char_constant:
- case tok::utf16_char_constant:
- case tok::utf32_char_constant:
- case tok::string_literal:
- case tok::wide_string_literal:
- case tok::utf8_string_literal:
- case tok::utf16_string_literal:
- case tok::utf32_string_literal:
- case tok::l_square:
- case tok::l_paren:
- case tok::amp:
- case tok::ampamp:
- case tok::star:
- case tok::plus:
- case tok::plusplus:
- case tok::minus:
- case tok::minusminus:
- case tok::tilde:
- case tok::exclaim:
- case tok::kw_sizeof:
- case tok::kw___func__:
- case tok::kw_const_cast:
- case tok::kw_delete:
- case tok::kw_dynamic_cast:
- case tok::kw_false:
- case tok::kw_new:
- case tok::kw_operator:
- case tok::kw_reinterpret_cast:
- case tok::kw_static_cast:
- case tok::kw_this:
- case tok::kw_throw:
- case tok::kw_true:
- case tok::kw_typeid:
- case tok::kw_alignof:
- case tok::kw_noexcept:
- case tok::kw_nullptr:
- case tok::kw__Alignof:
- case tok::kw___null:
- case tok::kw___alignof:
- case tok::kw___builtin_choose_expr:
- case tok::kw___builtin_offsetof:
- case tok::kw___builtin_va_arg:
- case tok::kw___imag:
- case tok::kw___real:
- case tok::kw___FUNCTION__:
- case tok::kw___FUNCDNAME__:
- case tok::kw___FUNCSIG__:
- case tok::kw_L__FUNCTION__:
- case tok::kw_L__FUNCSIG__:
- case tok::kw___PRETTY_FUNCTION__:
- case tok::kw___uuidof:
-#define TYPE_TRAIT(N,Spelling,K) \
- case tok::kw_##Spelling:
-#include "clang/Basic/TokenKinds.def"
- return TPResult::True;
-
- // Obviously starts a type-specifier-seq:
- case tok::kw_char:
- case tok::kw_const:
- case tok::kw_double:
- case tok::kw__Float16:
- case tok::kw___float128:
- case tok::kw_enum:
- case tok::kw_half:
- case tok::kw_float:
- case tok::kw_int:
- case tok::kw_long:
- case tok::kw___int64:
- case tok::kw___int128:
- case tok::kw_restrict:
- case tok::kw_short:
- case tok::kw_signed:
- case tok::kw_struct:
- case tok::kw_union:
- case tok::kw_unsigned:
- case tok::kw_void:
- case tok::kw_volatile:
- case tok::kw__Bool:
- case tok::kw__Complex:
- case tok::kw_class:
- case tok::kw_typename:
- case tok::kw_wchar_t:
- case tok::kw_char8_t:
- case tok::kw_char16_t:
- case tok::kw_char32_t:
- 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___thiscall:
- case tok::kw___regcall:
- case tok::kw___vectorcall:
- case tok::kw___unaligned:
- case tok::kw___vector:
- case tok::kw___pixel:
- case tok::kw___bool:
- case tok::kw__Atomic:
-#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
-#include "clang/Basic/OpenCLImageTypes.def"
- case tok::kw___unknown_anytype:
- return TPResult::False;
-
- default:
- break;
- }
-
- return TPResult::Ambiguous;
-}
-
bool Parser::isTentativelyDeclared(IdentifierInfo *II) {
return std::find(TentativelyDeclaredIdentifiers.begin(),
TentativelyDeclaredIdentifiers.end(), II)
@@ -1181,8 +1110,9 @@ class TentativeParseCCC final : public CorrectionCandidateCallback {
public:
TentativeParseCCC(const Token &Next) {
WantRemainingKeywords = false;
- WantTypeSpecifiers = Next.isOneOf(tok::l_paren, tok::r_paren, tok::greater,
- tok::l_brace, tok::identifier);
+ WantTypeSpecifiers =
+ Next.isOneOf(tok::l_paren, tok::r_paren, tok::greater, tok::l_brace,
+ tok::identifier, tok::comma);
}
bool ValidateCandidate(const TypoCorrection &Candidate) override {
@@ -1316,6 +1246,18 @@ public:
Parser::TPResult
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
bool *InvalidAsDeclSpec) {
+ auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId,
+ int Lookahead) {
+ // We have a placeholder-constraint (we check for 'auto' or 'decltype' to
+ // distinguish 'C<int>;' from 'C<int> auto c = 1;')
+ return TemplateId->Kind == TNK_Concept_template &&
+ GetLookAheadToken(Lookahead + 1).isOneOf(tok::kw_auto, tok::kw_decltype,
+ // If we have an identifier here, the user probably forgot the
+ // 'auto' in the placeholder constraint, e.g. 'C<int> x = 2;'
+ // This will be diagnosed nicely later, so disambiguate as a
+ // declaration.
+ tok::identifier);
+ };
switch (Tok.getKind()) {
case tok::identifier: {
// Check for need to substitute AltiVec __vector keyword
@@ -1334,6 +1276,15 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// this is ambiguous. Typo-correct to type and expression keywords and
// to types and identifiers, in order to try to recover from errors.
TentativeParseCCC CCC(Next);
+ // Tentative parsing may not be done in the right evaluation context
+ // for the ultimate expression. Enter an unevaluated context to prevent
+ // Sema from immediately e.g. treating this lookup as a potential ODR-use.
+ // If we generate an expression annotation token and the parser actually
+ // claims it as an expression, we'll transform the expression to a
+ // potentially-evaluated one then.
+ EnterExpressionEvaluationContext Unevaluated(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated,
+ Sema::ReuseLambdaContextDecl);
switch (TryAnnotateName(&CCC)) {
case ANK_Error:
return TPResult::Error;
@@ -1511,7 +1462,9 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
// If lookup for the template-name found nothing, don't assume we have a
// definitive disambiguation result yet.
- if (TemplateId->Kind == TNK_Undeclared_template && InvalidAsDeclSpec) {
+ if ((TemplateId->hasInvalidName() ||
+ TemplateId->Kind == TNK_Undeclared_template) &&
+ InvalidAsDeclSpec) {
// 'template-id(' can be a valid expression but not a valid decl spec if
// the template-name is not declared, but we don't consider this to be a
// definitive disambiguation. In any other context, it's an error either
@@ -1519,10 +1472,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
*InvalidAsDeclSpec = NextToken().is(tok::l_paren);
return TPResult::Ambiguous;
}
+ if (TemplateId->hasInvalidName())
+ return TPResult::Error;
+ if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0))
+ return TPResult::True;
if (TemplateId->Kind != TNK_Type_template)
return TPResult::False;
CXXScopeSpec SS;
- AnnotateTemplateIdTokenAsType();
+ AnnotateTemplateIdTokenAsType(SS);
assert(Tok.is(tok::annot_typename));
goto case_typename;
}
@@ -1532,6 +1489,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
if (TryAnnotateTypeOrScopeToken())
return TPResult::Error;
if (!Tok.is(tok::annot_typename)) {
+ if (Tok.is(tok::annot_cxxscope) &&
+ NextToken().is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId =
+ takeTemplateIdAnnotation(NextToken());
+ if (TemplateId->hasInvalidName()) {
+ if (InvalidAsDeclSpec) {
+ *InvalidAsDeclSpec = NextToken().is(tok::l_paren);
+ return TPResult::Ambiguous;
+ }
+ return TPResult::Error;
+ }
+ if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1))
+ return TPResult::True;
+ }
// If the next token is an identifier or a type qualifier, then this
// can't possibly be a valid expression either.
if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) {
@@ -1670,6 +1641,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_void:
@@ -1724,6 +1696,24 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw__Atomic:
return TPResult::True;
+ case tok::kw__ExtInt: {
+ if (NextToken().isNot(tok::l_paren))
+ return TPResult::Error;
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeToken();
+ ConsumeParen();
+
+ if (!SkipUntil(tok::r_paren, StopAtSemi))
+ return TPResult::Error;
+
+ if (Tok.is(tok::l_paren))
+ return TPResult::Ambiguous;
+
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))
+ return BracedCastResult;
+
+ return TPResult::True;
+ }
default:
return TPResult::False;
}
@@ -1756,6 +1746,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::kw_bool:
case tok::kw_short:
case tok::kw_int:
+ case tok::kw__ExtInt:
case tok::kw_long:
case tok::kw___int64:
case tok::kw___int128:
@@ -1764,6 +1755,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::kw_half:
case tok::kw_float:
case tok::kw_double:
+ case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
case tok::kw_void:
@@ -1975,17 +1967,14 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
// (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.
+ // FIXME: If we reach a ')' without consuming any '>'s, then this must
+ // also be a function parameter (that's missing its default argument).
if (VersusTemplateArgument)
- return Tok.isOneOf(tok::equal, tok::r_paren) ? TPResult::True
- : TPResult::False;
+ return Tok.is(tok::equal) ? 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, StopAtSemi | StopBeforeMatch))
return TPResult::Error;
}
@@ -2137,3 +2126,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
return TPResult::Ambiguous;
return TPResult::False;
}
+
+/// Determine whether we might be looking at the '(' of a C++20 explicit(bool)
+/// in an earlier language mode.
+Parser::TPResult Parser::isExplicitBool() {
+ assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token");
+
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeParen();
+
+ // We can only have 'explicit' on a constructor, conversion function, or
+ // deduction guide. The declarator of a deduction guide cannot be
+ // parenthesized, so we know this isn't a deduction guide. So the only
+ // thing we need to check for is some number of parens followed by either
+ // the current class name or 'operator'.
+ while (Tok.is(tok::l_paren))
+ ConsumeParen();
+
+ if (TryAnnotateOptionalCXXScopeToken())
+ return TPResult::Error;
+
+ // Class-scope constructor and conversion function names can't really be
+ // qualified, but we get better diagnostics if we assume they can be.
+ CXXScopeSpec SS;
+ if (Tok.is(tok::annot_cxxscope)) {
+ Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
+ Tok.getAnnotationRange(),
+ SS);
+ ConsumeAnnotationToken();
+ }
+
+ // 'explicit(operator' might be explicit(bool) or the declaration of a
+ // conversion function, but it's probably a conversion function.
+ if (Tok.is(tok::kw_operator))
+ return TPResult::Ambiguous;
+
+ // If this can't be a constructor name, it can only be explicit(bool).
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
+ return TPResult::True;
+ if (!Actions.isCurrentClassName(Tok.is(tok::identifier)
+ ? *Tok.getIdentifierInfo()
+ : *takeTemplateIdAnnotation(Tok)->Name,
+ getCurScope(), &SS))
+ return TPResult::True;
+ // Formally, we must have a right-paren after the constructor name to match
+ // the grammar for a constructor. But clang permits a parenthesized
+ // constructor declarator, so also allow a constructor declarator to follow
+ // with no ')' token after the constructor name.
+ if (!NextToken().is(tok::r_paren) &&
+ !isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
+ /*DeductionGuide=*/false))
+ return TPResult::True;
+
+ // Might be explicit(bool) or a parenthesized constructor name.
+ return TPResult::Ambiguous;
+}