blob: 469ffc7f427e9eccfdffea0d3dbf711cdb4fd39a [file] [log] [blame]
// Copyright (c) 2024, 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 '../../code_generator.dart';
import '../../config_provider/config_types.dart';
import '../../context.dart';
import '../clang_bindings/clang_bindings.dart' as clang_types;
import '../utils.dart';
import 'api_availability.dart';
import 'objcinterfacedecl_parser.dart';
ObjCProtocol? parseObjCProtocolDeclaration(
Context context,
clang_types.CXCursor cursor,
) {
final logger = context.logger;
final config = context.config;
final bindingsIndex = context.bindingsIndex;
if (cursor.kind != clang_types.CXCursorKind.CXCursor_ObjCProtocolDecl) {
return null;
}
final objcProtocols = config.objectiveC?.protocols;
if (objcProtocols == null) {
return null;
}
final usr = cursor.usr();
final name = cursor.spelling();
final decl = Declaration(usr: usr, originalName: name);
final cachedProtocol = bindingsIndex.getSeenObjCProtocol(usr);
if (cachedProtocol != null) {
return cachedProtocol;
}
// There's a strange shape in the AST for protocols seen in certain contexts,
// where instead of the AST looking like (decl -> methods/etc), it looks like
// (stubDecl --superProto-> decl -> methods/etc). If we try and parse the stub
// in this case, we'll be left with an empty protocol with itself as its super
// protocol. The USR is the same for both the stub and the real decl, so at
// least this case is easy to detect and fix.
final selfSuperCursor = cursor.findChildWhere((child) {
if (child.kind == clang_types.CXCursorKind.CXCursor_ObjCProtocolRef) {
return clang.clang_getCursorDefinition(child).usr() == usr;
}
return false;
});
if (selfSuperCursor != null) {
cursor = clang.clang_getCursorDefinition(selfSuperCursor);
}
final apiAvailability = ApiAvailability.fromCursor(cursor, context);
logger.fine(
'++++ Adding ObjC protocol: '
'Name: $name, ${cursor.completeStringRepr()}',
);
final protocol = ObjCProtocol(
context: context,
usr: usr,
originalName: name,
name: objcProtocols.rename(decl),
lookupName: applyModulePrefix(name, objcProtocols.module(decl)),
dartDoc: getCursorDocComment(
context,
cursor,
fallbackComment: name,
availability: apiAvailability.dartDoc,
),
apiAvailability: apiAvailability,
);
// Make sure to add the protocol to the index before parsing the AST, to break
// cycles.
bindingsIndex.addObjCProtocolToSeen(usr, protocol);
cursor.visitChildren((child) {
switch (child.kind) {
case clang_types.CXCursorKind.CXCursor_ObjCProtocolRef:
final declCursor = clang.clang_getCursorDefinition(child);
logger.fine(
' > Super protocol: ${declCursor.completeStringRepr()}',
);
final superProtocol = parseObjCProtocolDeclaration(context, declCursor);
if (superProtocol != null) {
protocol.superProtocols.add(superProtocol);
}
break;
case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl:
final (getter, setter) = parseObjCProperty(
context,
child,
decl,
objcProtocols,
);
protocol.addMethod(getter);
protocol.addMethod(setter);
break;
case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl:
case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl:
protocol.addMethod(
parseObjCMethod(context, child, decl, objcProtocols),
);
break;
}
});
return protocol;
}