blob: b9bbf762c676086d7965a715660bec304d09ed24 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '../../scanner/token.dart' show Token;
import '../fasta_codes.dart' show Message, Template, templateExpectedIdentifier;
import '../scanner/token_constants.dart' show IDENTIFIER_TOKEN;
import 'identifier_context_impl.dart';
import 'parser.dart' show Parser;
/// Information about the parser state that is passed to the listener at the
/// time an identifier is encountered. It is also used by the parser for error
/// recovery when a recovery template is defined.
///
/// This can be used by the listener to determine the context in which the
/// identifier appears; that in turn can help the listener decide how to resolve
/// the identifier (if the listener is doing resolution).
class IdentifierContext {
/// Identifier is being declared as the name of an import prefix (i.e. `Foo`
/// in `import "..." as Foo;`)
static const importPrefixDeclaration = const ImportPrefixIdentifierContext();
/// Identifier is the start of a dotted name in a conditional import or
/// export.
static const dottedName = const DottedNameIdentifierContext();
/// Identifier is part of a dotted name in a conditional import or export, but
/// it's not the first identifier of the dotted name.
static const dottedNameContinuation =
const DottedNameIdentifierContext.continuation();
/// Identifier is one of the shown/hidden names in an import/export
/// combinator.
static const combinator = const IdentifierContext('combinator');
/// Identifier is the start of a name in an annotation that precedes a
/// declaration (i.e. it appears directly after an `@`).
static const metadataReference =
const IdentifierContext('metadataReference', isScopeReference: true);
/// Identifier is part of a name in an annotation that precedes a declaration,
/// but it's not the first identifier in the name.
static const metadataContinuation =
const IdentifierContext('metadataContinuation', isContinuation: true);
/// Identifier is part of a name in an annotation that precedes a declaration,
/// but it appears after type parameters (e.g. `foo` in `@X<Y>.foo()`).
static const metadataContinuationAfterTypeArguments = const IdentifierContext(
'metadataContinuationAfterTypeArguments',
isContinuation: true);
/// Identifier is the name being declared by a typedef declaration.
static const typedefDeclaration = const TypedefDeclarationIdentifierContext();
/// Identifier is a field initializer in a formal parameter list (i.e. it
/// appears directly after `this.`).
static const fieldInitializer = const FieldInitializerIdentifierContext();
/// Identifier is a formal parameter being declared as part of a function,
/// method, or typedef declaration.
static const formalParameterDeclaration = const IdentifierContext(
'formalParameterDeclaration',
inDeclaration: true);
/// Identifier is the start of a library name (e.g. `foo` in the directive
/// 'library foo;`).
static const libraryName = const LibraryIdentifierContext();
/// Identifier is part of a library name, but it's not the first identifier in
/// the name.
static const libraryNameContinuation =
const LibraryIdentifierContext.continuation();
/// Identifier is the start of a library name referenced by a `part of`
/// directive (e.g. `foo` in the directive `part of foo;`).
static const partName =
const IdentifierContext('partName', inLibraryOrPartOfDeclaration: true);
/// Identifier is part of a library name referenced by a `part of` directive,
/// but it's not the first identifier in the name.
static const partNameContinuation = const IdentifierContext(
'partNameContinuation',
inLibraryOrPartOfDeclaration: true,
isContinuation: true);
/// Identifier is the type name being declared by an enum declaration.
static const enumDeclaration = const EnumDeclarationIdentifierContext();
/// Identifier is an enumerated value name being declared by an enum
/// declaration.
static const enumValueDeclaration =
const EnumValueDeclarationIdentifierContext();
/// Identifier is the name being declared by a class declaration or a named
/// mixin application, for example, `Foo` in `class Foo = X with Y;`.
static const classOrNamedMixinDeclaration =
const ClassOrNamedMixinIdentifierContext();
/// Identifier is the name of a type variable being declared (e.g. `Foo` in
/// `class C<Foo extends num> {}`).
static const typeVariableDeclaration = const IdentifierContext(
'typeVariableDeclaration',
inDeclaration: true,
isBuiltInIdentifierAllowed: false);
/// Identifier is the start of a reference to a type that starts with prefix.
static const prefixedTypeReference =
const TypeReferenceIdentifierContext.prefixed();
/// Identifier is the start of a reference to a type declared elsewhere.
static const typeReference = const TypeReferenceIdentifierContext();
/// Identifier is part of a reference to a type declared elsewhere, but it's
/// not the first identifier of the reference.
static const typeReferenceContinuation =
const TypeReferenceIdentifierContext.continuation();
/// Identifier is a name being declared by a top level variable declaration.
static const topLevelVariableDeclaration =
const TopLevelVariableIdentifierContext();
/// Identifier is a name being declared by a field declaration.
static const fieldDeclaration = const FieldDeclarationIdentifierContext();
/// Identifier is the name being declared by a top level function declaration.
static const topLevelFunctionDeclaration = const IdentifierContext(
'topLevelFunctionDeclaration',
inDeclaration: true);
/// Identifier is the start of the name being declared by a method
/// declaration.
static const methodDeclaration = const MethodDeclarationIdentifierContext();
/// Identifier is part of the name being declared by a method declaration,
/// but it's not the first identifier of the name.
///
/// In valid Dart, this can only happen if the identifier is the name of a
/// named constructor which is being declared, e.g. `foo` in
/// `class C { C.foo(); }`.
static const methodDeclarationContinuation =
const MethodDeclarationIdentifierContext.continuation();
/// Identifier appears after the word `operator` in a method declaration.
///
/// TODO(paulberry,ahe): Does this ever occur in valid Dart, or does it only
/// occur as part of error recovery? If it's only as part of error recovery,
/// perhaps we should just re-use methodDeclaration.
static const operatorName = const IdentifierContext('operatorName');
/// Identifier is the name being declared by a local function declaration that
/// uses a "get" or "set" keyword.
///
/// TODO(paulberry,ahe): Does this ever occur in valid Dart, or does it only
/// occur as part of error recovery? If it's only as part of error recovery,
/// perhaps we should just re-use localFunctionDeclaration.
static const localAccessorDeclaration =
const IdentifierContext('localAccessorDeclaration', inDeclaration: true);
/// Identifier is the start of the name being declared by a local function
/// declaration.
static const localFunctionDeclaration =
const IdentifierContext('localFunctionDeclaration', inDeclaration: true);
/// Identifier is part of the name being declared by a local function
/// declaration, but it's not the first identifier of the name.
///
/// TODO(paulberry,ahe): Does this ever occur in valid Dart, or does it only
/// occur as part of error recovery?
static const localFunctionDeclarationContinuation = const IdentifierContext(
'localFunctionDeclarationContinuation',
inDeclaration: true,
isContinuation: true);
/// Identifier is the name appearing in a function expression.
///
/// TODO(paulberry,ahe): What is an example of valid Dart code where this
/// would occur?
static const functionExpressionName =
const IdentifierContext('functionExpressionName');
/// Identifier is the start of a reference to a constructor declared
/// elsewhere.
static const constructorReference =
const IdentifierContext('constructorReference', isScopeReference: true);
/// Identifier is part of a reference to a constructor declared elsewhere, but
/// it's not the first identifier of the reference.
static const constructorReferenceContinuation = const IdentifierContext(
'constructorReferenceContinuation',
isContinuation: true);
/// Identifier is part of a reference to a constructor declared elsewhere, but
/// it appears after type parameters (e.g. `foo` in `X<Y>.foo`).
static const constructorReferenceContinuationAfterTypeArguments =
const IdentifierContext(
'constructorReferenceContinuationAfterTypeArguments',
isContinuation: true);
/// Identifier is the declaration of a label (i.e. it is followed by `:` and
/// then a statement).
static const labelDeclaration =
const IdentifierContext('labelDeclaration', inDeclaration: true);
/// Identifier is the start of a reference occurring in a literal symbol (e.g.
/// `foo` in `#foo`).
static const literalSymbol =
const IdentifierContext('literalSymbol', inSymbol: true);
/// Identifier is part of a reference occurring in a literal symbol, but it's
/// not the first identifier of the reference (e.g. `foo` in `#prefix.foo`).
static const literalSymbolContinuation = const IdentifierContext(
'literalSymbolContinuation',
inSymbol: true,
isContinuation: true);
/// Identifier appears in an expression, and it does not immediately follow a
/// `.`.
static const expression = const ExpressionIdentifierContext();
/// Identifier appears in an expression, and it immediately follows a `.`.
static const expressionContinuation =
const ExpressionIdentifierContext.continuation();
/// Identifier is a reference to a named argument of a function or method
/// invocation (e.g. `foo` in `f(foo: 0);`.
static const namedArgumentReference = const IdentifierContext(
'namedArgumentReference',
allowedInConstantExpression: true);
/// Identifier is a name being declared by a local variable declaration.
static const localVariableDeclaration =
const LocalVariableDeclarationIdentifierContext();
/// Identifier is a reference to a label (e.g. `foo` in `break foo;`).
/// Labels have their own scope.
static const labelReference = const IdentifierContext('labelReference');
final String _name;
/// Indicates whether the identifier represents a name which is being
/// declared.
final bool inDeclaration;
/// Indicates whether the identifier is within a `library` or `part of`
/// declaration.
final bool inLibraryOrPartOfDeclaration;
/// Indicates whether the identifier is within a symbol literal.
final bool inSymbol;
/// Indicates whether the identifier follows a `.`.
final bool isContinuation;
/// Indicates whether the identifier should be looked up in the current scope.
final bool isScopeReference;
/// Indicates whether built-in identifiers are allowed in this context.
final bool isBuiltInIdentifierAllowed;
/// Indicated whether the identifier is allowed in a context where constant
/// expressions are required.
final bool allowedInConstantExpression;
final Template<_MessageWithArgument<Token>> recoveryTemplate;
const IdentifierContext(this._name,
{this.inDeclaration: false,
this.inLibraryOrPartOfDeclaration: false,
this.inSymbol: false,
this.isContinuation: false,
this.isScopeReference: false,
this.isBuiltInIdentifierAllowed: true,
bool allowedInConstantExpression,
this.recoveryTemplate: templateExpectedIdentifier})
: this.allowedInConstantExpression =
// Generally, declarations are legal in constant expressions. A
// continuation doesn't affect constant expressions: if what it's
// continuing is a problem, it has already been reported.
allowedInConstantExpression ??
(inDeclaration || isContinuation || inSymbol);
String toString() => _name;
/// Ensure that the next token is an identifier (or keyword which should be
/// treated as an identifier) and return that identifier.
/// Report errors as necessary via [parser].
Token ensureIdentifier(Token token, Parser parser) {
assert(token.next.kind != IDENTIFIER_TOKEN);
// TODO(danrubel): Implement this method for each identifier context
// such that they return a non-null value.
return null;
}
}
// TODO(ahe): Remove when analyzer supports generalized function syntax.
typedef _MessageWithArgument<T> = Message Function(T);