blob: e82a5681dbeb7f06cd8fd5084d8a8928da0d9a83 [file] [log] [blame]
// Copyright (c) 2023, 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 'dart:typed_data';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor2.dart';
/// Union of a set of names.
class ElementNameUnion {
static const _maxLength = 63;
/// The 0-th element is the length of the longest name in the union.
///
/// Each following element is a bit mask for the A-Z letters in any name at
/// this position. There are only 26 letters, so they fit into 30 bits.
final Uint32List mask;
ElementNameUnion.empty() : mask = Uint32List(1 + _maxLength);
/// Constructs the union during reading from summary.
ElementNameUnion.read(this.mask);
void add(String name) {
// If already overflow, no reason adding anything.
if (mask[0] >= _maxLength) {
return;
}
var index = 1;
var codeUnits = name.codeUnits;
for (var i = 0; i < codeUnits.length; i++) {
var char = codeUnits[i];
if (0x41 <= char && char <= 0x5A) {
mask[index++] |= 1 << (char - 0x41);
} else if (0x61 <= char && char <= 0x7A) {
mask[index++] |= 1 << (char - 0x61);
}
if (index > _maxLength) {
mask[0] = _maxLength;
return;
}
}
// Update the length.
var length = index - 1;
var maxLength = mask[0];
if (maxLength < length) {
mask[0] = length;
}
}
/// Returns `true` if this union might contain a name that matches [pattern],
/// and `false` if there is definitely no such name. So, it can have false
/// positives, but no false negatives.
///
/// Specifically, that there might be a name that contains all characters
/// from `A-Z` and `a-z` sets from [pattern], in the same order. Other
/// characters are ignored.
bool contains(String pattern) {
// If overflow, then contains any name.
var maxLength = mask[0];
if (maxLength >= _maxLength) {
return true;
}
var index = 1;
for (var i = 0; i < pattern.length; i++) {
var patternChar = pattern.codeUnitAt(i);
int patternMask;
if (0x41 <= patternChar && patternChar <= 0x5A) {
patternMask = 1 << (patternChar - 0x41);
} else if (0x61 <= patternChar && patternChar <= 0x7A) {
patternMask = 1 << (patternChar - 0x61);
} else {
continue;
}
while (true) {
if (index > maxLength) {
return false;
}
var indexMask = mask[index++];
if ((indexMask & patternMask) != 0) {
break;
}
}
}
return true;
}
static ElementNameUnion forLibrary(LibraryElement libraryElement) {
var result = ElementNameUnion.empty();
libraryElement.accept2(_ElementVisitor2(result));
return result;
}
static bool _hasInterestingElements(Element element) {
if (element is ExecutableElement) {
return false;
}
return true;
}
static bool _isInterestingElement(Element element) {
return element.enclosingElement2 is LibraryElement ||
element is FieldElement ||
element is MethodElement ||
element is PropertyAccessorElement;
}
}
class _ElementVisitor2 extends GeneralizingElementVisitor2<void> {
final ElementNameUnion union;
_ElementVisitor2(this.union);
@override
void visitElement(Element element) {
if (ElementNameUnion._isInterestingElement(element)) {
var name = element.name3;
if (name != null) {
union.add(name);
}
}
if (ElementNameUnion._hasInterestingElements(element)) {
super.visitElement(element);
}
}
}