blob: 2355b50d21e5886f44afd076b4a8a089fbd5429f [file] [log] [blame]
// Copyright (c) 2020, 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 'package:kernel/kernel.dart';
import 'package:kernel/target/targets.dart';
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show
Message,
LocatedMessage,
messageJsInteropAnonymousFactoryPositionalParameters,
messageJsInteropEnclosingClassJSAnnotation,
messageJsInteropEnclosingClassJSAnnotationContext,
messageJsInteropIndexNotSupported,
messageJsInteropNamedParameters,
messageJsInteropNonExternalConstructor;
import 'src/js_interop.dart';
class JsInteropChecks extends RecursiveVisitor<void> {
final DiagnosticReporter<Message, LocatedMessage> _diagnosticsReporter;
JsInteropChecks(this._diagnosticsReporter);
@override
void defaultMember(Member member) {
_checkMemberJSInteropAnnotation(member);
super.defaultMember(member);
}
@override
void visitProcedure(Procedure procedure) {
_checkMemberJSInteropAnnotation(procedure);
if (!procedure.isExternal || !isJSInteropMember(procedure)) return;
if (!procedure.isStatic &&
(procedure.name.name == '[]=' || procedure.name.name == '[]')) {
_diagnosticsReporter.report(
messageJsInteropIndexNotSupported,
procedure.fileOffset,
procedure.name.name.length,
procedure.location.file);
}
var isAnonymousFactory =
isAnonymousClassMember(procedure) && procedure.isFactory;
if (isAnonymousFactory) {
if (procedure.function != null &&
!procedure.function.positionalParameters.isEmpty) {
var firstPositionalParam = procedure.function.positionalParameters[0];
_diagnosticsReporter.report(
messageJsInteropAnonymousFactoryPositionalParameters,
firstPositionalParam.fileOffset,
firstPositionalParam.name.length,
firstPositionalParam.location.file);
}
} else {
// Only factory constructors for anonymous classes are allowed to have
// named parameters.
_checkNoNamedParameters(procedure.function);
}
}
@override
void visitConstructor(Constructor constructor) {
_checkMemberJSInteropAnnotation(constructor);
if (!isJSInteropMember(constructor)) return;
if (!constructor.isExternal && !constructor.isSynthetic) {
_diagnosticsReporter.report(
messageJsInteropNonExternalConstructor,
constructor.fileOffset,
constructor.name.name.length,
constructor.location.file);
}
_checkNoNamedParameters(constructor.function);
}
/// Reports an error if [functionNode] has named parameters.
void _checkNoNamedParameters(FunctionNode functionNode) {
if (functionNode != null && !functionNode.namedParameters.isEmpty) {
var firstNamedParam = functionNode.namedParameters[0];
_diagnosticsReporter.report(
messageJsInteropNamedParameters,
firstNamedParam.fileOffset,
firstNamedParam.name.length,
firstNamedParam.location.file);
}
}
/// Reports an error if [m] has a JS interop annotation and is part of a class
/// that does not.
void _checkMemberJSInteropAnnotation(Member m) {
if (!hasJSInteropAnnotation(m)) return;
var enclosingClass = m.enclosingClass;
if (enclosingClass != null && !hasJSInteropAnnotation(enclosingClass)) {
_diagnosticsReporter.report(messageJsInteropEnclosingClassJSAnnotation,
m.fileOffset, m.name.name.length, m.location.file,
context: <LocatedMessage>[
messageJsInteropEnclosingClassJSAnnotationContext.withLocation(
enclosingClass.location.file,
enclosingClass.fileOffset,
enclosingClass.name.length)
]);
}
}
}