Merge pull request #6905 from Microsoft/portJsDocFixes

Port Salsa fixes
This commit is contained in:
Bill Ticehurst
2016-02-04 12:40:49 -08:00
14 changed files with 233 additions and 26 deletions
+14 -5
View File
@@ -1384,7 +1384,7 @@ namespace ts {
// Export assignment in some sort of block construct
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node));
}
else if (boundExpression.kind === SyntaxKind.Identifier) {
else if (boundExpression.kind === SyntaxKind.Identifier && node.kind === SyntaxKind.ExportAssignment) {
// An export default clause with an identifier exports all meanings of that identifier
declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
}
@@ -1435,7 +1435,8 @@ namespace ts {
// Declare a 'member' in case it turns out the container was an ES5 class
if (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) {
container.symbol.members = container.symbol.members || {};
declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
// It's acceptable for multiple 'this' assignments of the same identifier to occur
declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
}
@@ -1444,8 +1445,16 @@ namespace ts {
// Look up the function in the local scope, since prototype assignments should
// follow the function declaration
const classId = <Identifier>(<PropertyAccessExpression>(<PropertyAccessExpression>node.left).expression).expression;
const funcSymbol = container.locals[classId.text];
const leftSideOfAssignment = node.left as PropertyAccessExpression;
const classPrototype = leftSideOfAssignment.expression as PropertyAccessExpression;
const constructorFunction = classPrototype.expression as Identifier;
// Fix up parent pointers since we're going to use these nodes before we bind into them
leftSideOfAssignment.parent = node;
constructorFunction.parent = classPrototype;
classPrototype.parent = leftSideOfAssignment;
const funcSymbol = container.locals[constructorFunction.text];
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function)) {
return;
}
@@ -1456,7 +1465,7 @@ namespace ts {
}
// Declare the method/property
declareSymbol(funcSymbol.members, funcSymbol, <PropertyAccessExpression>node.left, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
function bindCallExpression(node: CallExpression) {
+6 -3
View File
@@ -2845,7 +2845,7 @@ namespace ts {
}
// Handle module.exports = expr
if (declaration.kind === SyntaxKind.BinaryExpression) {
return links.type = checkExpression((<BinaryExpression>declaration).right);
return links.type = getUnionType(map(symbol.declarations, (decl: BinaryExpression) => checkExpressionCached(decl.right)));
}
if (declaration.kind === SyntaxKind.PropertyAccessExpression) {
// Declarations only exist for property access expressions for certain
@@ -10356,9 +10356,11 @@ namespace ts {
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
const returnTag = getJSDocReturnTag(func);
if (returnTag) {
if (returnTag && returnTag.typeExpression) {
return getTypeFromTypeNode(returnTag.typeExpression.type);
}
return undefined;
}
function createPromiseType(promisedType: Type): Type {
@@ -10433,7 +10435,8 @@ namespace ts {
}
else {
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
return unknownType;
// Defer to unioning the return types so we get a) downstream errors earlier and b) better Salsa experience
return getUnionType(types);
}
}
+5 -5
View File
@@ -4051,7 +4051,7 @@ namespace ts {
setDecoratorContext(/*val*/ true);
}
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseOptionalIdentifier() {
@@ -4335,13 +4335,13 @@ namespace ts {
const labeledStatement = <LabeledStatement>createNode(SyntaxKind.LabeledStatement, fullStart);
labeledStatement.label = <Identifier>expression;
labeledStatement.statement = parseStatement();
return finishNode(labeledStatement);
return addJSDocComment(finishNode(labeledStatement));
}
else {
const expressionStatement = <ExpressionStatement>createNode(SyntaxKind.ExpressionStatement, fullStart);
expressionStatement.expression = expression;
parseSemicolon();
return finishNode(expressionStatement);
return addJSDocComment(finishNode(expressionStatement));
}
}
@@ -4778,7 +4778,7 @@ namespace ts {
parseExpected(SyntaxKind.ConstructorKeyword);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected);
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseMethodDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray, asteriskToken: Node, name: PropertyName, questionToken: Node, diagnosticMessage?: DiagnosticMessage): MethodDeclaration {
@@ -4792,7 +4792,7 @@ namespace ts {
const isAsync = !!(method.flags & NodeFlags.Async);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, method);
method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage);
return finishNode(method);
return addJSDocComment(finishNode(method));
}
function parsePropertyDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray, name: PropertyName, questionToken: Node): ClassElement {
+13 -1
View File
@@ -1207,7 +1207,19 @@ namespace ts {
node.parent.parent.parent.kind === SyntaxKind.VariableStatement;
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
return variableStatementNode && variableStatementNode.jsDocComment;
if (variableStatementNode) {
return variableStatementNode.jsDocComment;
}
// Also recognize when the node is the RHS of an assignment expression
const isSourceOfAssignmentExpressionStatement =
node.parent && node.parent.parent &&
node.parent.kind === SyntaxKind.BinaryExpression &&
(node.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
node.parent.parent.kind === SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
return node.parent.parent.jsDocComment;
}
}
return undefined;
+14 -11
View File
@@ -62,7 +62,7 @@ namespace ts {
export interface SourceFile {
/* @internal */ version: string;
/* @internal */ scriptSnapshot: IScriptSnapshot;
/* @internal */ nameTable: Map<string>;
/* @internal */ nameTable: Map<number>;
/* @internal */ getNamedDeclarations(): Map<Declaration[]>;
@@ -808,7 +808,7 @@ namespace ts {
public languageVersion: ScriptTarget;
public languageVariant: LanguageVariant;
public identifiers: Map<string>;
public nameTable: Map<string>;
public nameTable: Map<number>;
public resolvedModules: Map<ResolvedModule>;
public imports: LiteralExpression[];
public moduleAugmentations: LiteralExpression[];
@@ -1957,8 +1957,6 @@ namespace ts {
const text = scriptSnapshot.getText(0, scriptSnapshot.getLength());
const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents);
setSourceFileFields(sourceFile, scriptSnapshot, version);
// after full parsing we can use table with interned strings as name table
sourceFile.nameTable = sourceFile.identifiers;
return sourceFile;
}
@@ -3834,7 +3832,7 @@ namespace ts {
if (isRightOfDot && isSourceFileJavaScript(sourceFile)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries);
addRange(entries, getJavaScriptCompletionEntries(sourceFile, uniqueNames));
addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames));
}
else {
if (!symbols || symbols.length === 0) {
@@ -3867,12 +3865,17 @@ namespace ts {
return { isMemberCompletion, isNewIdentifierLocation, entries };
function getJavaScriptCompletionEntries(sourceFile: SourceFile, uniqueNames: Map<string>): CompletionEntry[] {
function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map<string>): CompletionEntry[] {
const entries: CompletionEntry[] = [];
const target = program.getCompilerOptions().target;
const nameTable = getNameTable(sourceFile);
for (const name in nameTable) {
// Skip identifiers produced only from the current location
if (nameTable[name] === position) {
continue;
}
if (!uniqueNames[name]) {
uniqueNames[name] = name;
const displayName = getCompletionEntryDisplayName(name, target, /*performCharacterChecks*/ true);
@@ -5487,7 +5490,7 @@ namespace ts {
const nameTable = getNameTable(sourceFile);
if (lookUp(nameTable, internedName)) {
if (lookUp(nameTable, internedName) !== undefined) {
result = result || [];
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex);
}
@@ -7525,7 +7528,7 @@ namespace ts {
}
/* @internal */
export function getNameTable(sourceFile: SourceFile): Map<string> {
export function getNameTable(sourceFile: SourceFile): Map<number> {
if (!sourceFile.nameTable) {
initializeNameTable(sourceFile);
}
@@ -7534,7 +7537,7 @@ namespace ts {
}
function initializeNameTable(sourceFile: SourceFile): void {
const nameTable: Map<string> = {};
const nameTable: Map<number> = {};
walk(sourceFile);
sourceFile.nameTable = nameTable;
@@ -7542,7 +7545,7 @@ namespace ts {
function walk(node: Node) {
switch (node.kind) {
case SyntaxKind.Identifier:
nameTable[(<Identifier>node).text] = (<Identifier>node).text;
nameTable[(<Identifier>node).text] = nameTable[(<Identifier>node).text] === undefined ? node.pos : -1;
break;
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
@@ -7554,7 +7557,7 @@ namespace ts {
node.parent.kind === SyntaxKind.ExternalModuleReference ||
isArgumentOfElementAccessExpression(node)) {
nameTable[(<LiteralExpression>node).text] = (<LiteralExpression>node).text;
nameTable[(<LiteralExpression>node).text] = nameTable[(<LiteralExpression>node).text] === undefined ? node.pos : -1;
}
break;
default:
@@ -0,0 +1,35 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: file.js
//// "use strict";
////
//// class Something {
////
//// /**
//// * @param {number} a
//// */
//// constructor(a, b) {
//// a/*body*/
//// }
////
//// /**
//// * @param {number} a
//// */
//// method(a) {
//// a/*method*/
//// }
//// }
//// let x = new Something(/*sig*/);
goTo.marker('body');
edit.insert('.');
verify.completionListContains('toFixed', undefined, undefined, 'method');
edit.backspace();
goTo.marker('sig');
verify.currentSignatureHelpIs('Something(a: number, b: any): Something');
goTo.marker('method');
edit.insert('.');
verify.completionListContains('toFixed', undefined, undefined, 'method');
@@ -0,0 +1,21 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: file.js
//// /**
//// * @param {number} a
//// * @param {string} b
//// */
//// exports.foo = function(a, b) {
//// a/*a*/;
//// b/*b*/
//// };
goTo.marker('a');
edit.insert('.');
verify.completionListContains('toFixed', undefined, undefined, 'method');
goTo.marker('b');
edit.insert('.');
verify.completionListContains('substr', undefined, undefined, 'method');
@@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: file.js
//// function fn() {
//// if (foo) {
//// return 0;
//// } else {
//// return '0';
//// }
//// }
//// let x = fn();
//// if(typeof x === 'string') {
//// x/*str*/
//// } else {
//// x/*num*/
//// }
goTo.marker('str');
edit.insert('.');
verify.completionListContains('substr', undefined, undefined, 'method');
goTo.marker('num');
edit.insert('.');
verify.completionListContains('toFixed', undefined, undefined, 'method');
@@ -0,0 +1,21 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: file.js
//// /**
//// * A person
//// * @constructor
//// * @param {string} name - The name of the person.
//// * @param {number} age - The age of the person.
//// */
//// function Person(name, age) {
//// this.name = name;
//// this.age = age;
//// }
////
////
//// Person.getName = 10;
//// Person.getNa/**/ = 10;
goTo.marker();
verify.not.memberListContains('getNa');
@@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: file.js
//// /**
//// * This is a very cool function that is very nice.
//// * @returns something
//// * @param p anotherthing
//// */
//// function a1(p) {
//// try {
//// throw new Error('x');
//// } catch (x) { x--; }
//// return 23;
//// }
////
//// x - /**/a1()
goTo.marker();
verify.quickInfoExists();
@@ -0,0 +1,14 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @Filename: a.js
//// function Person(age) {
//// if (age >= 18) {
//// this.canVote = true;
//// } else {
//// this.canVote = false;
//// }
//// }
verify.getSyntacticDiagnostics(`[]`);
verify.getSemanticDiagnostics(`[]`);
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @Filename: a.js
//// function Person(age) {
//// if (age >= 18) {
//// this.canVote = true;
//// } else {
//// this.canVote = 23;
//// }
//// }
//// let x = new Person(100);
//// x.canVote/**/;
goTo.marker();
verify.quickInfoIs('(property) Person.canVote: boolean | number');
+3 -1
View File
@@ -23,4 +23,6 @@ verify.completionListContains('y');
verify.not.completionListContains('invisible');
edit.insert('x.');
verify.completionListContains('a');
verify.memberListContains('a', undefined, undefined, 'property');
edit.insert('a.');
verify.memberListContains('toFixed', undefined, undefined, 'method');
@@ -0,0 +1,26 @@
///<reference path="fourslash.ts" />
// Assignments to 'module.exports' create an external module
// @allowJs: true
// @Filename: myMod.js
//// var x = { a: 10 };
//// module.exports = x;
// @Filename: isGlobal.js
//// var y = 10;
// @Filename: consumer.js
//// var x = require('myMod');
//// /**/;
goTo.file('consumer.js');
goTo.marker();
verify.completionListContains('y');
verify.not.completionListContains('invisible');
edit.insert('x.');
verify.memberListContains('a', undefined, undefined, 'property');
edit.insert('a.');
verify.memberListContains('toFixed', undefined, undefined, 'method');