Resolve first identifier of the jsxFactory as part of type check

This commit is contained in:
Sheetal Nandi 2016-11-09 13:15:13 -08:00
parent f7bac98948
commit 06affa6672
10 changed files with 548 additions and 119 deletions

View File

@ -335,6 +335,7 @@ namespace ts {
});
let jsxElementType: Type;
let _jsxNamespace: string;
/** Things we lazy load from the JSX namespace */
const jsxTypes = createMap<Type>();
const JsxNames = {
@ -372,6 +373,22 @@ namespace ts {
return checker;
function getJsxNamespace(): string {
if (_jsxNamespace === undefined) {
_jsxNamespace = "React";
if (compilerOptions.jsxFactory) {
const jsxEntity = host.getJsxFactoryEntity();
if (jsxEntity) {
_jsxNamespace = getFirstIdentifier(jsxEntity).text;
}
}
else if (compilerOptions.reactNamespace) {
_jsxNamespace = compilerOptions.reactNamespace;
}
}
return _jsxNamespace;
}
function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
// Ensure we have all the type information in place for this file so that all the
// emitter questions of this resolver will return the right information.
@ -11337,10 +11354,10 @@ namespace ts {
function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
checkGrammarJsxElement(node);
checkJsxPreconditions(node);
// The reactNamespace symbol should be marked as 'used' so we don't incorrectly elide its import. And if there
// is no reactNamespace symbol in scope when targeting React emit, we should issue an error.
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const reactNamespace = compilerOptions.reactNamespace ? compilerOptions.reactNamespace : "React";
const reactNamespace = getJsxNamespace();
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
if (reactSym) {
// Mark local symbol as referenced here because it might not have been marked

View File

@ -329,6 +329,9 @@ namespace ts {
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
// ReactNamespace and jsxFactory information
let jsxFactoryEntity: EntityName;
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
@ -421,7 +424,8 @@ namespace ts {
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
isSourceFileFromExternalLibrary,
dropDiagnosticsProducingTypeChecker
dropDiagnosticsProducingTypeChecker,
getJsxFactoryEntity: () => jsxFactoryEntity
};
verifyCompilerOptions();
@ -1674,7 +1678,8 @@ namespace ts {
if (options.reactNamespace) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"));
}
if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
jsxFactoryEntity = parseIsolatedEntityName(options.jsxFactory, languageVersion);
if (!jsxFactoryEntity) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory));
}
}

View File

@ -2177,6 +2177,7 @@ namespace ts {
getTypeChecker(): TypeChecker;
/* @internal */ getCommonSourceDirectory(): string;
/* @internal */ getJsxFactoryEntity(): EntityName;
// For testing purposes only. Should not be used by any other consumers (including the
// language service).
@ -2250,6 +2251,7 @@ namespace ts {
/* @internal */
export interface TypeCheckerHost {
getCompilerOptions(): CompilerOptions;
getJsxFactoryEntity(): EntityName;
getSourceFiles(): SourceFile[];
getSourceFile(fileName: string): SourceFile;

View File

@ -1,57 +0,0 @@
tests/cases/compiler/test.tsx(12,5): error TS2304: Cannot find name 'React'.
tests/cases/compiler/test.tsx(13,5): error TS2304: Cannot find name 'React'.
==== tests/cases/compiler/Element.ts (0 errors) ====
declare namespace JSX {
interface Element {
name: string;
isIntrinsic: boolean;
isCustomElement: boolean;
toString(renderId?: number): string;
bindDOM(renderId?: number): number;
resetComponent(): void;
instantiateComponents(renderId?: number): number;
props: any;
}
}
export namespace Element {
export function isElement(el: any): el is JSX.Element {
return el.markAsChildOfRootElement !== undefined;
}
export function createElement(args: any[]) {
return {
}
}
}
export let createElement = Element.createElement;
function toCamelCase(text: string): string {
return text[0].toLowerCase() + text.substring(1);
}
==== tests/cases/compiler/test.tsx (2 errors) ====
import { Element} from './Element';
let createElement = Element.createElement;
let c: {
a?: {
b: string
}
};
class A {
view() {
return [
<meta content="helloworld"></meta>,
~~~~
!!! error TS2304: Cannot find name 'React'.
<meta content={c.a!.b}></meta>
~~~~
!!! error TS2304: Cannot find name 'React'.
];
}
}

View File

@ -0,0 +1,125 @@
=== tests/cases/compiler/Element.ts ===
declare namespace JSX {
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
interface Element {
>Element : Symbol(Element, Decl(Element.ts, 1, 23))
name: string;
>name : Symbol(Element.name, Decl(Element.ts, 2, 23))
isIntrinsic: boolean;
>isIntrinsic : Symbol(Element.isIntrinsic, Decl(Element.ts, 3, 21))
isCustomElement: boolean;
>isCustomElement : Symbol(Element.isCustomElement, Decl(Element.ts, 4, 29))
toString(renderId?: number): string;
>toString : Symbol(Element.toString, Decl(Element.ts, 5, 33))
>renderId : Symbol(renderId, Decl(Element.ts, 6, 17))
bindDOM(renderId?: number): number;
>bindDOM : Symbol(Element.bindDOM, Decl(Element.ts, 6, 44))
>renderId : Symbol(renderId, Decl(Element.ts, 7, 16))
resetComponent(): void;
>resetComponent : Symbol(Element.resetComponent, Decl(Element.ts, 7, 43))
instantiateComponents(renderId?: number): number;
>instantiateComponents : Symbol(Element.instantiateComponents, Decl(Element.ts, 8, 31))
>renderId : Symbol(renderId, Decl(Element.ts, 9, 30))
props: any;
>props : Symbol(Element.props, Decl(Element.ts, 9, 57))
}
}
export namespace Element {
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
export function isElement(el: any): el is JSX.Element {
>isElement : Symbol(isElement, Decl(Element.ts, 13, 26))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
>Element : Symbol(JSX.Element, Decl(Element.ts, 1, 23))
return el.markAsChildOfRootElement !== undefined;
>el : Symbol(el, Decl(Element.ts, 14, 30))
>undefined : Symbol(undefined)
}
export function createElement(args: any[]) {
>createElement : Symbol(createElement, Decl(Element.ts, 16, 5))
>args : Symbol(args, Decl(Element.ts, 18, 34))
return {
}
}
}
export let createElement = Element.createElement;
>createElement : Symbol(createElement, Decl(Element.ts, 25, 10))
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
function toCamelCase(text: string): string {
>toCamelCase : Symbol(toCamelCase, Decl(Element.ts, 25, 49))
>text : Symbol(text, Decl(Element.ts, 27, 21))
return text[0].toLowerCase() + text.substring(1);
>text[0].toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text.substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
}
=== tests/cases/compiler/test.tsx ===
import { Element} from './Element';
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
let createElement = Element.createElement;
>createElement : Symbol(createElement, Decl(test.tsx, 1, 3))
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
let c: {
>c : Symbol(c, Decl(test.tsx, 2, 3))
a?: {
>a : Symbol(a, Decl(test.tsx, 2, 8))
b: string
>b : Symbol(b, Decl(test.tsx, 3, 6))
}
};
class A {
>A : Symbol(A, Decl(test.tsx, 6, 2))
view() {
>view : Symbol(A.view, Decl(test.tsx, 8, 9))
return [
<meta content="helloworld"></meta>,
>meta : Symbol(unknown)
>content : Symbol(unknown)
>meta : Symbol(unknown)
<meta content={c.a!.b}></meta>
>meta : Symbol(unknown)
>content : Symbol(unknown)
>c.a!.b : Symbol(b, Decl(test.tsx, 3, 6))
>c.a : Symbol(a, Decl(test.tsx, 2, 8))
>c : Symbol(c, Decl(test.tsx, 2, 3))
>a : Symbol(a, Decl(test.tsx, 2, 8))
>b : Symbol(b, Decl(test.tsx, 3, 6))
>meta : Symbol(unknown)
];
}
}

View File

@ -0,0 +1,140 @@
=== tests/cases/compiler/Element.ts ===
declare namespace JSX {
>JSX : any
interface Element {
>Element : Element
name: string;
>name : string
isIntrinsic: boolean;
>isIntrinsic : boolean
isCustomElement: boolean;
>isCustomElement : boolean
toString(renderId?: number): string;
>toString : (renderId?: number) => string
>renderId : number
bindDOM(renderId?: number): number;
>bindDOM : (renderId?: number) => number
>renderId : number
resetComponent(): void;
>resetComponent : () => void
instantiateComponents(renderId?: number): number;
>instantiateComponents : (renderId?: number) => number
>renderId : number
props: any;
>props : any
}
}
export namespace Element {
>Element : typeof Element
export function isElement(el: any): el is JSX.Element {
>isElement : (el: any) => el is JSX.Element
>el : any
>el : any
>JSX : any
>Element : JSX.Element
return el.markAsChildOfRootElement !== undefined;
>el.markAsChildOfRootElement !== undefined : boolean
>el.markAsChildOfRootElement : any
>el : any
>markAsChildOfRootElement : any
>undefined : undefined
}
export function createElement(args: any[]) {
>createElement : (args: any[]) => {}
>args : any[]
return {
>{ } : {}
}
}
}
export let createElement = Element.createElement;
>createElement : (args: any[]) => {}
>Element.createElement : (args: any[]) => {}
>Element : typeof Element
>createElement : (args: any[]) => {}
function toCamelCase(text: string): string {
>toCamelCase : (text: string) => string
>text : string
return text[0].toLowerCase() + text.substring(1);
>text[0].toLowerCase() + text.substring(1) : string
>text[0].toLowerCase() : string
>text[0].toLowerCase : () => string
>text[0] : string
>text : string
>0 : 0
>toLowerCase : () => string
>text.substring(1) : string
>text.substring : (start: number, end?: number) => string
>text : string
>substring : (start: number, end?: number) => string
>1 : 1
}
=== tests/cases/compiler/test.tsx ===
import { Element} from './Element';
>Element : typeof Element
let createElement = Element.createElement;
>createElement : (args: any[]) => {}
>Element.createElement : (args: any[]) => {}
>Element : typeof Element
>createElement : (args: any[]) => {}
let c: {
>c : { a?: { b: string; }; }
a?: {
>a : { b: string; }
b: string
>b : string
}
};
class A {
>A : A
view() {
>view : () => any[]
return [
>[ <meta content="helloworld"></meta>, <meta content={c.a!.b}></meta> ] : any[]
<meta content="helloworld"></meta>,
><meta content="helloworld"></meta> : any
>meta : any
>content : any
>meta : any
<meta content={c.a!.b}></meta>
><meta content={c.a!.b}></meta> : any
>meta : any
>content : any
>c.a!.b : string
>c.a! : { b: string; }
>c.a : { b: string; }
>c : { a?: { b: string; }; }
>a : { b: string; }
>b : string
>meta : any
];
}
}

View File

@ -1,57 +0,0 @@
tests/cases/compiler/test.tsx(12,5): error TS2304: Cannot find name 'React'.
tests/cases/compiler/test.tsx(13,5): error TS2304: Cannot find name 'React'.
==== tests/cases/compiler/Element.ts (0 errors) ====
declare namespace JSX {
interface Element {
name: string;
isIntrinsic: boolean;
isCustomElement: boolean;
toString(renderId?: number): string;
bindDOM(renderId?: number): number;
resetComponent(): void;
instantiateComponents(renderId?: number): number;
props: any;
}
}
export namespace Element {
export function isElement(el: any): el is JSX.Element {
return el.markAsChildOfRootElement !== undefined;
}
export function createElement(args: any[]) {
return {
}
}
}
export let createElement = Element.createElement;
function toCamelCase(text: string): string {
return text[0].toLowerCase() + text.substring(1);
}
==== tests/cases/compiler/test.tsx (2 errors) ====
import { Element} from './Element';
let c: {
a?: {
b: string
}
};
class A {
view() {
return [
<meta content="helloworld"></meta>,
~~~~
!!! error TS2304: Cannot find name 'React'.
<meta content={c.a!.b}></meta>
~~~~
!!! error TS2304: Cannot find name 'React'.
];
}
}

View File

@ -69,6 +69,7 @@ function toCamelCase(text) {
}
//// [test.js]
"use strict";
const Element_1 = require("./Element");
let c;
class A {
view() {

View File

@ -0,0 +1,119 @@
=== tests/cases/compiler/Element.ts ===
declare namespace JSX {
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
interface Element {
>Element : Symbol(Element, Decl(Element.ts, 1, 23))
name: string;
>name : Symbol(Element.name, Decl(Element.ts, 2, 23))
isIntrinsic: boolean;
>isIntrinsic : Symbol(Element.isIntrinsic, Decl(Element.ts, 3, 21))
isCustomElement: boolean;
>isCustomElement : Symbol(Element.isCustomElement, Decl(Element.ts, 4, 29))
toString(renderId?: number): string;
>toString : Symbol(Element.toString, Decl(Element.ts, 5, 33))
>renderId : Symbol(renderId, Decl(Element.ts, 6, 17))
bindDOM(renderId?: number): number;
>bindDOM : Symbol(Element.bindDOM, Decl(Element.ts, 6, 44))
>renderId : Symbol(renderId, Decl(Element.ts, 7, 16))
resetComponent(): void;
>resetComponent : Symbol(Element.resetComponent, Decl(Element.ts, 7, 43))
instantiateComponents(renderId?: number): number;
>instantiateComponents : Symbol(Element.instantiateComponents, Decl(Element.ts, 8, 31))
>renderId : Symbol(renderId, Decl(Element.ts, 9, 30))
props: any;
>props : Symbol(Element.props, Decl(Element.ts, 9, 57))
}
}
export namespace Element {
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
export function isElement(el: any): el is JSX.Element {
>isElement : Symbol(isElement, Decl(Element.ts, 13, 26))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>el : Symbol(el, Decl(Element.ts, 14, 30))
>JSX : Symbol(JSX, Decl(Element.ts, 0, 0))
>Element : Symbol(JSX.Element, Decl(Element.ts, 1, 23))
return el.markAsChildOfRootElement !== undefined;
>el : Symbol(el, Decl(Element.ts, 14, 30))
>undefined : Symbol(undefined)
}
export function createElement(args: any[]) {
>createElement : Symbol(createElement, Decl(Element.ts, 16, 5))
>args : Symbol(args, Decl(Element.ts, 18, 34))
return {
}
}
}
export let createElement = Element.createElement;
>createElement : Symbol(createElement, Decl(Element.ts, 25, 10))
>Element.createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
>Element : Symbol(Element, Decl(Element.ts, 12, 1))
>createElement : Symbol(Element.createElement, Decl(Element.ts, 16, 5))
function toCamelCase(text: string): string {
>toCamelCase : Symbol(toCamelCase, Decl(Element.ts, 25, 49))
>text : Symbol(text, Decl(Element.ts, 27, 21))
return text[0].toLowerCase() + text.substring(1);
>text[0].toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>text.substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
>text : Symbol(text, Decl(Element.ts, 27, 21))
>substring : Symbol(String.substring, Decl(lib.es5.d.ts, --, --))
}
=== tests/cases/compiler/test.tsx ===
import { Element} from './Element';
>Element : Symbol(Element, Decl(test.tsx, 0, 8))
let c: {
>c : Symbol(c, Decl(test.tsx, 2, 3))
a?: {
>a : Symbol(a, Decl(test.tsx, 2, 8))
b: string
>b : Symbol(b, Decl(test.tsx, 3, 6))
}
};
class A {
>A : Symbol(A, Decl(test.tsx, 6, 2))
view() {
>view : Symbol(A.view, Decl(test.tsx, 8, 9))
return [
<meta content="helloworld"></meta>,
>meta : Symbol(unknown)
>content : Symbol(unknown)
>meta : Symbol(unknown)
<meta content={c.a!.b}></meta>
>meta : Symbol(unknown)
>content : Symbol(unknown)
>c.a!.b : Symbol(b, Decl(test.tsx, 3, 6))
>c.a : Symbol(a, Decl(test.tsx, 2, 8))
>c : Symbol(c, Decl(test.tsx, 2, 3))
>a : Symbol(a, Decl(test.tsx, 2, 8))
>b : Symbol(b, Decl(test.tsx, 3, 6))
>meta : Symbol(unknown)
];
}
}

View File

@ -0,0 +1,134 @@
=== tests/cases/compiler/Element.ts ===
declare namespace JSX {
>JSX : any
interface Element {
>Element : Element
name: string;
>name : string
isIntrinsic: boolean;
>isIntrinsic : boolean
isCustomElement: boolean;
>isCustomElement : boolean
toString(renderId?: number): string;
>toString : (renderId?: number) => string
>renderId : number
bindDOM(renderId?: number): number;
>bindDOM : (renderId?: number) => number
>renderId : number
resetComponent(): void;
>resetComponent : () => void
instantiateComponents(renderId?: number): number;
>instantiateComponents : (renderId?: number) => number
>renderId : number
props: any;
>props : any
}
}
export namespace Element {
>Element : typeof Element
export function isElement(el: any): el is JSX.Element {
>isElement : (el: any) => el is JSX.Element
>el : any
>el : any
>JSX : any
>Element : JSX.Element
return el.markAsChildOfRootElement !== undefined;
>el.markAsChildOfRootElement !== undefined : boolean
>el.markAsChildOfRootElement : any
>el : any
>markAsChildOfRootElement : any
>undefined : undefined
}
export function createElement(args: any[]) {
>createElement : (args: any[]) => {}
>args : any[]
return {
>{ } : {}
}
}
}
export let createElement = Element.createElement;
>createElement : (args: any[]) => {}
>Element.createElement : (args: any[]) => {}
>Element : typeof Element
>createElement : (args: any[]) => {}
function toCamelCase(text: string): string {
>toCamelCase : (text: string) => string
>text : string
return text[0].toLowerCase() + text.substring(1);
>text[0].toLowerCase() + text.substring(1) : string
>text[0].toLowerCase() : string
>text[0].toLowerCase : () => string
>text[0] : string
>text : string
>0 : 0
>toLowerCase : () => string
>text.substring(1) : string
>text.substring : (start: number, end?: number) => string
>text : string
>substring : (start: number, end?: number) => string
>1 : 1
}
=== tests/cases/compiler/test.tsx ===
import { Element} from './Element';
>Element : typeof Element
let c: {
>c : { a?: { b: string; }; }
a?: {
>a : { b: string; }
b: string
>b : string
}
};
class A {
>A : A
view() {
>view : () => any[]
return [
>[ <meta content="helloworld"></meta>, <meta content={c.a!.b}></meta> ] : any[]
<meta content="helloworld"></meta>,
><meta content="helloworld"></meta> : any
>meta : any
>content : any
>meta : any
<meta content={c.a!.b}></meta>
><meta content={c.a!.b}></meta> : any
>meta : any
>content : any
>c.a!.b : string
>c.a! : { b: string; }
>c.a : { b: string; }
>c : { a?: { b: string; }; }
>a : { b: string; }
>b : string
>meta : any
];
}
}