blob: 270cc84a26b68cd5079adf97345a6904c1ad3e5a [file] [log] [blame]
// Copyright (c) 2014, 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.
library analyzer.parser;
import 'package:_fe_analyzer_shared/src/parser/parser.dart' as fasta;
import 'package:_fe_analyzer_shared/src/parser/type_info.dart' as fasta;
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
import 'package:analyzer/src/fasta/ast_builder.dart';
import 'package:analyzer/src/generated/source.dart';
export 'package:analyzer/src/dart/error/syntactic_errors.dart';
/// A simple data-holder for a method that needs to return multiple values.
class CommentAndMetadata {
/// The documentation comment that was parsed, or `null` if none was given.
final Comment? comment;
/// The metadata that was parsed, or `null` if none was given.
final List<Annotation>? metadata;
/// Initialize a newly created holder with the given [comment] and [metadata].
CommentAndMetadata(this.comment, this.metadata);
/// Return `true` if some metadata was parsed.
bool get hasMetadata => metadata != null && metadata!.isNotEmpty;
}
/// A simple data-holder for a method that needs to return multiple values.
class FinalConstVarOrType {
/// The 'final', 'const' or 'var' keyword, or `null` if none was given.
final Token keyword;
/// The type, or `null` if no type was specified.
final TypeAnnotation type;
/// Initialize a newly created holder with the given [keyword] and [type].
FinalConstVarOrType(this.keyword, this.type);
}
/// A simple data-holder for a method that needs to return multiple values.
class Modifiers {
/// The token representing the keyword 'abstract', or `null` if the keyword
/// was not found.
Token? abstractKeyword;
/// The token representing the keyword 'const', or `null` if the keyword was
/// not found.
Token? constKeyword;
/// The token representing the keyword 'covariant', or `null` if the keyword
/// was not found.
Token? covariantKeyword;
/// The token representing the keyword 'external', or `null` if the keyword
/// was not found.
Token? externalKeyword;
/// The token representing the keyword 'factory', or `null` if the keyword was
/// not found.
Token? factoryKeyword;
/// The token representing the keyword 'final', or `null` if the keyword was
/// not found.
Token? finalKeyword;
/// The token representing the keyword 'static', or `null` if the keyword was
/// not found.
Token? staticKeyword;
/// The token representing the keyword 'var', or `null` if the keyword was not
/// found.
Token? varKeyword;
@override
String toString() {
StringBuffer buffer = StringBuffer();
bool needsSpace = _appendKeyword(buffer, false, abstractKeyword);
needsSpace = _appendKeyword(buffer, needsSpace, constKeyword);
needsSpace = _appendKeyword(buffer, needsSpace, externalKeyword);
needsSpace = _appendKeyword(buffer, needsSpace, factoryKeyword);
needsSpace = _appendKeyword(buffer, needsSpace, finalKeyword);
needsSpace = _appendKeyword(buffer, needsSpace, staticKeyword);
_appendKeyword(buffer, needsSpace, varKeyword);
return buffer.toString();
}
/// If the given [keyword] is not `null`, append it to the given [builder],
/// prefixing it with a space if [needsSpace] is `true`. Return `true` if
/// subsequent keywords need to be prefixed with a space.
bool _appendKeyword(StringBuffer buffer, bool needsSpace, Token? keyword) {
if (keyword != null) {
if (needsSpace) {
buffer.writeCharCode(0x20);
}
buffer.write(keyword.lexeme);
return true;
}
return needsSpace;
}
}
/// A parser used to parse tokens into an AST structure.
class Parser {
late Token currentToken;
/// The fasta parser being wrapped.
late final fasta.Parser fastaParser;
/// The builder which creates the analyzer AST data structures
/// based on the Fasta parser.
final AstBuilder astBuilder;
Parser(Source source, AnalysisErrorListener errorListener,
{required FeatureSet featureSet, bool allowNativeClause = true})
: astBuilder = AstBuilder(
ErrorReporter(
errorListener,
source,
isNonNullableByDefault:
featureSet.isEnabled(Feature.non_nullable),
),
source.uri,
true,
featureSet) {
fastaParser = fasta.Parser(astBuilder);
astBuilder.parser = fastaParser;
astBuilder.allowNativeClause = allowNativeClause;
}
set allowNativeClause(bool value) {
astBuilder.allowNativeClause = value;
}
bool get enableOptionalNewAndConst => false;
set enableOptionalNewAndConst(bool enable) {}
set enableSetLiterals(bool value) {
// TODO(danrubel): Remove this method once the reference to this flag
// has been removed from dartfmt.
}
set parseFunctionBodies(bool parseFunctionBodies) {
astBuilder.parseFunctionBodies = parseFunctionBodies;
}
/// Append the given token to the end of the token stream,
/// and update the token's offset.
void appendToken(Token token, Token newToken) {
while (!token.next!.isEof) {
token = token.next!;
}
newToken
..offset = token.end
..setNext(token.next!);
token.setNext(newToken);
}
Expression parseAdditiveExpression() => parseExpression2();
Expression parseArgument() {
currentToken = SimpleToken(TokenType.OPEN_PAREN, 0)..setNext(currentToken);
appendToken(currentToken, SimpleToken(TokenType.CLOSE_PAREN, 0));
currentToken = fastaParser
.parseArguments(fastaParser.syntheticPreviousToken(currentToken))
.next!;
var invocation = astBuilder.pop() as MethodInvocation;
return invocation.argumentList.arguments[0];
}
Expression parseAssignableExpression(bool primaryAllowed) =>
parseExpression2();
Expression parseBitwiseAndExpression() => parseExpression2();
Expression parseBitwiseOrExpression() => parseExpression2();
Expression parseBitwiseXorExpression() => parseExpression2();
CompilationUnitImpl parseCompilationUnit(Token token) {
currentToken = token;
return parseCompilationUnit2();
}
CompilationUnitImpl parseCompilationUnit2() {
currentToken = fastaParser.parseUnit(currentToken);
return astBuilder.pop() as CompilationUnitImpl;
}
Expression parseConditionalExpression() => parseExpression2();
Configuration parseConfiguration() {
currentToken = fastaParser
.parseConditionalUri(fastaParser.syntheticPreviousToken(currentToken))
.next!;
return astBuilder.pop() as Configuration;
}
Expression parseConstExpression() => parseExpression2();
CompilationUnit parseDirectives(Token token) {
currentToken = token;
return parseDirectives2();
}
CompilationUnit parseDirectives2() {
currentToken = fastaParser.parseDirectives(currentToken);
return astBuilder.pop() as CompilationUnit;
}
DottedName parseDottedName() {
currentToken = fastaParser
.parseDottedName(fastaParser.syntheticPreviousToken(currentToken))
.next!;
return astBuilder.pop() as DottedName;
}
Expression parseEqualityExpression() => parseExpression2();
Expression parseExpression(Token token) {
currentToken = token;
return parseExpression2();
}
Expression parseExpression2() {
currentToken = fastaParser
.parseExpression(fastaParser.syntheticPreviousToken(currentToken))
.next!;
return astBuilder.pop() as Expression;
}
Expression parseExpressionWithoutCascade() => parseExpression2();
FormalParameterList parseFormalParameterList({bool inFunctionType = false}) {
currentToken = fastaParser
.parseFormalParametersRequiredOpt(
fastaParser.syntheticPreviousToken(currentToken),
inFunctionType
? fasta.MemberKind.GeneralizedFunctionType
: fasta.MemberKind.NonStaticMethod)
.next!;
return astBuilder.pop() as FormalParameterList;
}
FunctionBody parseFunctionBody(
bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
currentToken = fastaParser.parseAsyncModifierOpt(
fastaParser.syntheticPreviousToken(currentToken));
currentToken =
fastaParser.parseFunctionBody(currentToken, inExpression, mayBeEmpty);
return astBuilder.pop() as FunctionBody;
}
FunctionExpression parseFunctionExpression() =>
parseExpression2() as FunctionExpression;
Expression parseLogicalAndExpression() => parseExpression2();
Expression parseLogicalOrExpression() => parseExpression2();
Expression parseMultiplicativeExpression() => parseExpression2();
InstanceCreationExpression parseNewExpression() =>
parseExpression2() as InstanceCreationExpression;
Expression parsePostfixExpression() => parseExpression2();
Identifier parsePrefixedIdentifier() => parseExpression2() as Identifier;
Expression parsePrimaryExpression() {
currentToken = fastaParser
.parsePrimary(fastaParser.syntheticPreviousToken(currentToken),
fasta.IdentifierContext.expression)
.next!;
return astBuilder.pop() as Expression;
}
Expression parseRelationalExpression() => parseExpression2();
Expression parseRethrowExpression() => parseExpression2();
Expression parseShiftExpression() => parseExpression2();
SimpleIdentifier parseSimpleIdentifier(
{bool allowKeyword = false, bool isDeclaration = false}) =>
parseExpression2() as SimpleIdentifier;
Statement parseStatement(Token token) {
currentToken = token;
return parseStatement2();
}
Statement parseStatement2() {
currentToken = fastaParser
.parseStatement(fastaParser.syntheticPreviousToken(currentToken))
.next!;
return astBuilder.pop() as Statement;
}
StringLiteral parseStringLiteral() => parseExpression2() as StringLiteral;
SymbolLiteral parseSymbolLiteral() => parseExpression2() as SymbolLiteral;
Expression parseThrowExpression() => parseExpression2();
Expression parseThrowExpressionWithoutCascade() => parseExpression2();
AnnotatedNode parseTopLevelDeclaration(bool isDirective) {
currentToken = fastaParser.parseTopLevelDeclaration(currentToken);
return (isDirective ? astBuilder.directives : astBuilder.declarations)
.removeLast();
}
TypeAnnotation parseTypeAnnotation(bool inExpression) {
Token previous = fastaParser.syntheticPreviousToken(currentToken);
currentToken = fasta
.computeType(previous, true, !inExpression)
.parseType(previous, fastaParser)
.next!;
return astBuilder.pop() as TypeAnnotation;
}
TypeArgumentList parseTypeArgumentList() {
Token previous = fastaParser.syntheticPreviousToken(currentToken);
currentToken = fasta
.computeTypeParamOrArg(previous)
.parseArguments(previous, fastaParser)
.next!;
return astBuilder.pop() as TypeArgumentList;
}
TypeName parseTypeName(bool inExpression) {
Token previous = fastaParser.syntheticPreviousToken(currentToken);
currentToken = fasta
.computeType(previous, true, !inExpression)
.parseType(previous, fastaParser)
.next!;
return astBuilder.pop() as TypeName;
}
TypeParameter parseTypeParameter() {
currentToken = SyntheticBeginToken(TokenType.LT, 0)
..endGroup = SyntheticToken(TokenType.GT, 0)
..setNext(currentToken);
appendToken(currentToken, currentToken.endGroup!);
TypeParameterList typeParams = parseTypeParameterList()!;
return typeParams.typeParameters[0];
}
TypeParameterList? parseTypeParameterList() {
Token token = fastaParser.syntheticPreviousToken(currentToken);
currentToken = fasta
.computeTypeParamOrArg(token, true)
.parseVariables(token, fastaParser)
.next!;
return astBuilder.pop() as TypeParameterList?;
}
Expression parseUnaryExpression() => parseExpression2();
}