Use new tree to get Bloom filters
This commit is contained in:
parent
2d4cec43f5
commit
55512faa0d
8 changed files with 147 additions and 184 deletions
|
@ -1,7 +1,5 @@
|
||||||
///<reference path='..\core\integerUtilities.ts' />
|
|
||||||
|
|
||||||
module TypeScript {
|
|
||||||
|
|
||||||
|
module ts {
|
||||||
export class BloomFilter {
|
export class BloomFilter {
|
||||||
private bitArray: boolean[];
|
private bitArray: boolean[];
|
||||||
private hashFunctionCount: number;
|
private hashFunctionCount: number;
|
||||||
|
@ -40,7 +38,7 @@ module TypeScript {
|
||||||
}
|
}
|
||||||
|
|
||||||
// m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0)))))
|
// m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0)))))
|
||||||
static computeM(expectedCount: number): number {
|
private static computeM(expectedCount: number): number {
|
||||||
var p: number = BloomFilter.falsePositiveProbability;
|
var p: number = BloomFilter.falsePositiveProbability;
|
||||||
var n: number = expectedCount;
|
var n: number = expectedCount;
|
||||||
|
|
||||||
|
@ -50,7 +48,7 @@ module TypeScript {
|
||||||
}
|
}
|
||||||
|
|
||||||
// k = round(log(2.0) * m / n)
|
// k = round(log(2.0) * m / n)
|
||||||
static computeK(expectedCount: number): number {
|
private static computeK(expectedCount: number): number {
|
||||||
var n: number = expectedCount;
|
var n: number = expectedCount;
|
||||||
var m: number = BloomFilter.computeM(expectedCount);
|
var m: number = BloomFilter.computeM(expectedCount);
|
||||||
|
|
||||||
|
@ -78,14 +76,12 @@ module TypeScript {
|
||||||
* Murmur hash is public domain. Actual code is included below as reference.
|
* Murmur hash is public domain. Actual code is included below as reference.
|
||||||
*/
|
*/
|
||||||
private computeHash(key: string, seed: number): number {
|
private computeHash(key: string, seed: number): number {
|
||||||
return Hash.computeMurmur2StringHashCode(key, seed);
|
return BloomFilter.computeMurmur2StringHashCode(key, seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addKeys(keys: ts.Map<any>) {
|
public addKeys(keys: string[]) {
|
||||||
for (var name in keys) {
|
for (var i = 0, n = keys.length; i < n; i++) {
|
||||||
if (ts.lookUp(keys, name)) {
|
this.add(keys[i]);
|
||||||
this.add(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +110,7 @@ module TypeScript {
|
||||||
&& this.hashFunctionCount === filter.hashFunctionCount;
|
&& this.hashFunctionCount === filter.hashFunctionCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static isEquivalent(array1: boolean[], array2: boolean[]): boolean {
|
private static isEquivalent(array1: boolean[], array2: boolean[]): boolean {
|
||||||
if (array1.length !== array2.length) {
|
if (array1.length !== array2.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -127,5 +123,64 @@ module TypeScript {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static integerMultiplyLow32Bits(n1: number, n2: number): number {
|
||||||
|
var n1Low16 = n1 & 0x0000ffff;
|
||||||
|
var n1High16 = n1 >>> 16;
|
||||||
|
|
||||||
|
var n2Low16 = n2 & 0x0000ffff;
|
||||||
|
var n2High16 = n2 >>> 16;
|
||||||
|
|
||||||
|
var resultLow32 = (((n1 & 0xffff0000) * n2) >>> 0) + (((n1 & 0x0000ffff) * n2) >>> 0) >>> 0;
|
||||||
|
return resultLow32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static computeMurmur2StringHashCode(key: string, seed: number): number {
|
||||||
|
// 'm' and 'r' are mixing constants generated offline.
|
||||||
|
// They're not really 'magic', they just happen to work well.
|
||||||
|
|
||||||
|
var m: number = 0x5bd1e995;
|
||||||
|
var r: number = 24;
|
||||||
|
|
||||||
|
// Initialize the hash to a 'random' value
|
||||||
|
|
||||||
|
var numberOfCharsLeft = key.length;
|
||||||
|
var h = Math.abs(seed ^ numberOfCharsLeft);
|
||||||
|
|
||||||
|
// Mix 4 bytes at a time into the hash. NOTE: 4 bytes is two chars, so we iterate
|
||||||
|
// through the string two chars at a time.
|
||||||
|
var index = 0;
|
||||||
|
while (numberOfCharsLeft >= 2) {
|
||||||
|
var c1 = key.charCodeAt(index);
|
||||||
|
var c2 = key.charCodeAt(index + 1);
|
||||||
|
|
||||||
|
var k = Math.abs(c1 | (c2 << 16));
|
||||||
|
|
||||||
|
k = BloomFilter.integerMultiplyLow32Bits(k, m);
|
||||||
|
k ^= k >> r;
|
||||||
|
k = BloomFilter.integerMultiplyLow32Bits(k, m);
|
||||||
|
|
||||||
|
h = BloomFilter.integerMultiplyLow32Bits(h, m);
|
||||||
|
h ^= k;
|
||||||
|
|
||||||
|
index += 2;
|
||||||
|
numberOfCharsLeft -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last char (or 2 bytes) if they exist. This happens if the original string had
|
||||||
|
// odd length.
|
||||||
|
if (numberOfCharsLeft === 1) {
|
||||||
|
h ^= key.charCodeAt(index);
|
||||||
|
h = BloomFilter.integerMultiplyLow32Bits(h, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a few final mixes of the hash to ensure the last few bytes are well-incorporated.
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h = BloomFilter.integerMultiplyLow32Bits(h, m);
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
/////<reference path='precompile.ts' />
|
/////<reference path='precompile.ts' />
|
||||||
/////<reference path='referenceResolver.ts' />
|
/////<reference path='referenceResolver.ts' />
|
||||||
/////<reference path='declarationEmitter.ts' />
|
/////<reference path='declarationEmitter.ts' />
|
||||||
/////<reference path='bloomFilter.ts' />
|
|
||||||
/////<reference path='identifierWalker.ts' />
|
/////<reference path='identifierWalker.ts' />
|
||||||
/////<reference path='settings.ts' />
|
/////<reference path='settings.ts' />
|
||||||
/////<reference path='typecheck\pullFlags.ts' />
|
/////<reference path='typecheck\pullFlags.ts' />
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
///<reference path='references.ts' />
|
|
||||||
|
|
||||||
module TypeScript {
|
|
||||||
export class Hash {
|
|
||||||
// This table uses FNV1a as a string hash
|
|
||||||
private static FNV_BASE = 2166136261;
|
|
||||||
private static FNV_PRIME = 16777619;
|
|
||||||
|
|
||||||
private static computeFnv1aCharArrayHashCode(text: number[], start: number, len: number): number {
|
|
||||||
var hashCode = Hash.FNV_BASE;
|
|
||||||
var end = start + len;
|
|
||||||
|
|
||||||
for (var i = start; i < end; i++) {
|
|
||||||
hashCode = IntegerUtilities.integerMultiplyLow32Bits(hashCode ^ text[i], Hash.FNV_PRIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static computeSimple31BitCharArrayHashCode(key: number[], start: number, len: number): number {
|
|
||||||
// Start with an int.
|
|
||||||
var hash = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
var ch = key[start + i];
|
|
||||||
|
|
||||||
// Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and
|
|
||||||
// V8 recognize this as not needing to go past the 53 bits needed for the float
|
|
||||||
// mantissa. Or'ing with 0 keeps this 32 bits.
|
|
||||||
hash = ((((hash << 5) - hash) | 0) + ch) | 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap
|
|
||||||
// allocation.
|
|
||||||
return hash & 0x7FFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static computeSimple31BitStringHashCode(key: string): number {
|
|
||||||
// Start with an int.
|
|
||||||
var hash = 0;
|
|
||||||
|
|
||||||
var start = 0;
|
|
||||||
var len = key.length;
|
|
||||||
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
var ch = key.charCodeAt(start + i);
|
|
||||||
|
|
||||||
// Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and
|
|
||||||
// V8 recognize this as not needing to go past the 53 bits needed for the float
|
|
||||||
// mantissa. Or'ing with 0 keeps this 32 bits.
|
|
||||||
hash = ((((hash << 5) - hash) | 0) + ch) | 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap
|
|
||||||
// allocation.
|
|
||||||
return hash & 0x7FFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static computeMurmur2StringHashCode(key: string, seed: number): number {
|
|
||||||
// 'm' and 'r' are mixing constants generated offline.
|
|
||||||
// They're not really 'magic', they just happen to work well.
|
|
||||||
|
|
||||||
var m: number = 0x5bd1e995;
|
|
||||||
var r: number = 24;
|
|
||||||
|
|
||||||
// Initialize the hash to a 'random' value
|
|
||||||
|
|
||||||
var numberOfCharsLeft = key.length;
|
|
||||||
var h = Math.abs(seed ^ numberOfCharsLeft);
|
|
||||||
|
|
||||||
// Mix 4 bytes at a time into the hash. NOTE: 4 bytes is two chars, so we iterate
|
|
||||||
// through the string two chars at a time.
|
|
||||||
var index = 0;
|
|
||||||
while (numberOfCharsLeft >= 2) {
|
|
||||||
var c1 = key.charCodeAt(index);
|
|
||||||
var c2 = key.charCodeAt(index + 1);
|
|
||||||
|
|
||||||
var k = Math.abs(c1 | (c2 << 16));
|
|
||||||
|
|
||||||
k = IntegerUtilities.integerMultiplyLow32Bits(k, m);
|
|
||||||
k ^= k >> r;
|
|
||||||
k = IntegerUtilities.integerMultiplyLow32Bits(k, m);
|
|
||||||
|
|
||||||
h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
|
|
||||||
h ^= k;
|
|
||||||
|
|
||||||
index += 2;
|
|
||||||
numberOfCharsLeft -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the last char (or 2 bytes) if they exist. This happens if the original string had
|
|
||||||
// odd length.
|
|
||||||
if (numberOfCharsLeft === 1) {
|
|
||||||
h ^= key.charCodeAt(index);
|
|
||||||
h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a few final mixes of the hash to ensure the last few bytes are well-incorporated.
|
|
||||||
|
|
||||||
h ^= h >> 13;
|
|
||||||
h = IntegerUtilities.integerMultiplyLow32Bits(h, m);
|
|
||||||
h ^= h >> 15;
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static combine(value: number, currentHash: number): number {
|
|
||||||
// Ensure we stay within 31 bits.
|
|
||||||
return (((currentHash << 5) + currentHash) + value) & 0x7FFFFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@
|
||||||
///<reference path='diagnosticCore.ts' />
|
///<reference path='diagnosticCore.ts' />
|
||||||
///<reference path='diagnosticInfo.ts' />
|
///<reference path='diagnosticInfo.ts' />
|
||||||
///<reference path='errors.ts' />
|
///<reference path='errors.ts' />
|
||||||
///<reference path='hash.ts' />
|
|
||||||
///<reference path='integerUtilities.ts' />
|
///<reference path='integerUtilities.ts' />
|
||||||
///<reference path='lineMap.ts' />
|
///<reference path='lineMap.ts' />
|
||||||
///<reference path='linePosition.ts' />
|
///<reference path='linePosition.ts' />
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
/// <reference path='breakpoints.ts' />
|
/// <reference path='breakpoints.ts' />
|
||||||
/// <reference path='indentation.ts' />
|
/// <reference path='indentation.ts' />
|
||||||
/// <reference path='formatting\formatting.ts' />
|
/// <reference path='formatting\formatting.ts' />
|
||||||
/// <reference path='compiler\bloomFilter.ts' />
|
/// <reference path='bloomFilter.ts' />
|
||||||
|
|
||||||
/// <reference path='core\references.ts' />
|
/// <reference path='core\references.ts' />
|
||||||
/// <reference path='resources\references.ts' />
|
/// <reference path='resources\references.ts' />
|
||||||
|
@ -71,7 +71,7 @@ module ts {
|
||||||
export interface SourceFile {
|
export interface SourceFile {
|
||||||
getSourceUnit(): TypeScript.SourceUnitSyntax;
|
getSourceUnit(): TypeScript.SourceUnitSyntax;
|
||||||
getSyntaxTree(): TypeScript.SyntaxTree;
|
getSyntaxTree(): TypeScript.SyntaxTree;
|
||||||
getBloomFilter(): TypeScript.BloomFilter;
|
getBloomFilter(): BloomFilter;
|
||||||
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
|
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ module ts {
|
||||||
public isOpen: boolean;
|
public isOpen: boolean;
|
||||||
public languageVersion: ScriptTarget;
|
public languageVersion: ScriptTarget;
|
||||||
|
|
||||||
private bloomFilter: TypeScript.BloomFilter;
|
private bloomFilter: BloomFilter;
|
||||||
private syntaxTree: TypeScript.SyntaxTree;
|
private syntaxTree: TypeScript.SyntaxTree;
|
||||||
private scriptSnapshot: TypeScript.IScriptSnapshot;
|
private scriptSnapshot: TypeScript.IScriptSnapshot;
|
||||||
|
|
||||||
|
@ -356,61 +356,27 @@ module ts {
|
||||||
return TypeScript.isDTSFile(this.filename);
|
return TypeScript.isDTSFile(this.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static isNameOfPropertyDeclaration(node: TypeScript.ISyntaxElement): boolean {
|
public getBloomFilter(): BloomFilter {
|
||||||
if (node.kind() !== TypeScript.SyntaxKind.IdentifierName && node.kind() !== TypeScript.SyntaxKind.StringLiteral && node.kind() !== TypeScript.SyntaxKind.NumericLiteral) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (node.parent.kind()) {
|
|
||||||
case TypeScript.SyntaxKind.VariableDeclarator:
|
|
||||||
return (<TypeScript.VariableDeclaratorSyntax>node.parent).propertyName === node;
|
|
||||||
|
|
||||||
case TypeScript.SyntaxKind.PropertySignature:
|
|
||||||
return (<TypeScript.PropertySignatureSyntax>node.parent).propertyName === node;
|
|
||||||
|
|
||||||
case TypeScript.SyntaxKind.SimplePropertyAssignment:
|
|
||||||
return (<TypeScript.SimplePropertyAssignmentSyntax>node.parent).propertyName === node;
|
|
||||||
|
|
||||||
case TypeScript.SyntaxKind.EnumElement:
|
|
||||||
return (<TypeScript.EnumElementSyntax>node.parent).propertyName === node;
|
|
||||||
|
|
||||||
case TypeScript.SyntaxKind.ModuleDeclaration:
|
|
||||||
return (<TypeScript.ModuleDeclarationSyntax>node.parent).name === node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static isElementAccessLiteralIndexer(node: TypeScript.ISyntaxElement): boolean {
|
|
||||||
return (node.kind() === TypeScript.SyntaxKind.StringLiteral || node.kind() === TypeScript.SyntaxKind.NumericLiteral) &&
|
|
||||||
node.parent.kind() === TypeScript.SyntaxKind.ElementAccessExpression && (<TypeScript.ElementAccessExpressionSyntax> node.parent).argumentExpression === node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getBloomFilter(): TypeScript.BloomFilter {
|
|
||||||
if (!this.bloomFilter) {
|
if (!this.bloomFilter) {
|
||||||
var identifiers = TypeScript.createIntrinsicsObject<boolean>();
|
var identifiers: string[] = [];
|
||||||
var pre = function (cur: TypeScript.ISyntaxElement) {
|
|
||||||
if (TypeScript.ASTHelpers.isValidAstNode(cur)) {
|
|
||||||
if (cur.kind() === TypeScript.SyntaxKind.IdentifierName ||
|
|
||||||
SourceFileObject.isNameOfPropertyDeclaration(cur) ||
|
|
||||||
SourceFileObject.isElementAccessLiteralIndexer(cur)) {
|
|
||||||
var nodeText = TypeScript.tokenValueText((<TypeScript.ISyntaxToken>cur));
|
|
||||||
|
|
||||||
identifiers[nodeText] = true;
|
forEachChild(this, function visit (node: Node) {
|
||||||
}
|
switch (node.kind) {
|
||||||
}
|
case SyntaxKind.Identifier:
|
||||||
};
|
identifiers.push((<Identifier>node).text);
|
||||||
|
return undefined;
|
||||||
|
case SyntaxKind.StringLiteral:
|
||||||
|
case SyntaxKind.NumericLiteral:
|
||||||
|
if (isNameOfPropertyDeclaration(node) || isLiteralIndexOfIndexAccess(node)) {
|
||||||
|
identifiers.push((<LiteralExpression>node).text);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
default:
|
||||||
|
return forEachChild(node, visit);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
TypeScript.getAstWalkerFactory().simpleWalk(this.getSourceUnit(), pre, null, identifiers);
|
this.bloomFilter = new BloomFilter(identifiers.length);
|
||||||
|
|
||||||
var identifierCount = 0;
|
|
||||||
for (var name in identifiers) {
|
|
||||||
if (identifiers[name]) {
|
|
||||||
identifierCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bloomFilter = new TypeScript.BloomFilter(identifierCount);
|
|
||||||
this.bloomFilter.addKeys(identifiers);
|
this.bloomFilter.addKeys(identifiers);
|
||||||
}
|
}
|
||||||
return this.bloomFilter;
|
return this.bloomFilter;
|
||||||
|
@ -2375,7 +2341,7 @@ module ts {
|
||||||
// type to the search set
|
// type to the search set
|
||||||
if (isNameOfPropertyAssignment(location)) {
|
if (isNameOfPropertyAssignment(location)) {
|
||||||
var symbolFromContextualType = getPropertySymbolFromContextualType(location);
|
var symbolFromContextualType = getPropertySymbolFromContextualType(location);
|
||||||
if (symbolFromContextualType) result.push(symbolFromContextualType);
|
if (symbolFromContextualType) result.push(typeInfoResolver.getRootSymbol(symbolFromContextualType));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
// Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
|
||||||
|
@ -2429,7 +2395,7 @@ module ts {
|
||||||
// compare to our searchSymbol
|
// compare to our searchSymbol
|
||||||
if (isNameOfPropertyAssignment(referenceLocation)) {
|
if (isNameOfPropertyAssignment(referenceLocation)) {
|
||||||
var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation);
|
var symbolFromContextualType = getPropertySymbolFromContextualType(referenceLocation);
|
||||||
if (searchSymbols.indexOf(symbolFromContextualType) >= 0) {
|
if (symbolFromContextualType && searchSymbols.indexOf(typeInfoResolver.getRootSymbol(symbolFromContextualType)) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
tests/cases/fourslash/referencesBloomFilters.ts
Normal file
20
tests/cases/fourslash/referencesBloomFilters.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference path='fourslash.ts'/>
|
||||||
|
|
||||||
|
// Ensure BloomFilter building logic is correct, by having one reference per file
|
||||||
|
|
||||||
|
// @Filename: declaration.ts
|
||||||
|
////var container = { /*1*/searchProp : 1 };
|
||||||
|
|
||||||
|
// @Filename: expression.ts
|
||||||
|
////function blah() { return (1 + 2 + container./*2*/searchProp()) === 2; };
|
||||||
|
|
||||||
|
// @Filename: stringIndexer.ts
|
||||||
|
////function blah2() { container[/*3*/"searchProp"] };
|
||||||
|
|
||||||
|
// @Filename: redeclaration.ts
|
||||||
|
////container = { /*4*/"searchProp" : 18 };
|
||||||
|
|
||||||
|
test.markers().forEach(m => {
|
||||||
|
goTo.position(m.position, m.fileName);
|
||||||
|
verify.referencesCountIs(4);
|
||||||
|
});
|
20
tests/cases/fourslash/referencesBloomFilters2.ts
Normal file
20
tests/cases/fourslash/referencesBloomFilters2.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference path='fourslash.ts'/>
|
||||||
|
|
||||||
|
// Ensure BloomFilter building logic is correct, by having one reference per file
|
||||||
|
|
||||||
|
// @Filename: declaration.ts
|
||||||
|
////var container = { /*1*/42 : 1 };
|
||||||
|
|
||||||
|
// @Filename: expression.ts
|
||||||
|
////function blah() { return (container[/*2*/42]) === 2; };
|
||||||
|
|
||||||
|
// @Filename: stringIndexer.ts
|
||||||
|
////function blah2() { container[/*3*/"42"] };
|
||||||
|
|
||||||
|
// @Filename: redeclaration.ts
|
||||||
|
////container = { /*4*/"42" : 18 };
|
||||||
|
|
||||||
|
test.markers().forEach(m => {
|
||||||
|
goTo.position(m.position, m.fileName);
|
||||||
|
verify.referencesCountIs(4);
|
||||||
|
});
|
16
tests/cases/fourslash/referencesBloomFilters3.ts
Normal file
16
tests/cases/fourslash/referencesBloomFilters3.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/// <reference path='fourslash.ts'/>
|
||||||
|
|
||||||
|
// Ensure BloomFilter building logic is correct, by having one reference per file
|
||||||
|
|
||||||
|
|
||||||
|
// @Filename: declaration.ts
|
||||||
|
////enum Test { /*1*/"42" = 1 };
|
||||||
|
|
||||||
|
// @Filename: expression.ts
|
||||||
|
////(Test[/*2*/42]);
|
||||||
|
|
||||||
|
|
||||||
|
test.markers().forEach(m => {
|
||||||
|
goTo.position(m.position, m.fileName);
|
||||||
|
verify.referencesCountIs(2);
|
||||||
|
});
|
Loading…
Reference in a new issue