Skip to content

Commit

Permalink
Functional extension loading & lint passes
Browse files Browse the repository at this point in the history
  • Loading branch information
weswigham committed Jun 20, 2016
1 parent 6d99b2f commit 8b3aeb8
Show file tree
Hide file tree
Showing 12 changed files with 806 additions and 26 deletions.
5 changes: 3 additions & 2 deletions Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ var harnessSources = harnessCoreSources.concat([
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts",
"matchFiles.ts"
"matchFiles.ts",
"extensionAPI.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
Expand Down Expand Up @@ -527,7 +528,7 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca

// Node package definition file to be distributed without the package. Created by replacing
// 'ts' namespace with '"typescript"' as a module.
var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts/g, 'declare module "typescript"');
var nodeStandaloneDefinitionsFileContents = definitionFileContents.replace(/declare (namespace|module) ts {/g, 'declare module "typescript" {\n import * as ts from "typescript";');
fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents);
});

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17258,6 +17258,10 @@ namespace ts {
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent, /*includeOptionality*/ true);
}

if (node.kind === SyntaxKind.SourceFile) {
return unknownType;
}

if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
const symbol = getSymbolAtLocation(node);
const declaredType = symbol && getDeclaredTypeOfSymbol(symbol);
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ namespace ts {
experimental: true,
description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators
},
{
name: "extensions",
type: "object",
isTSConfigOnly: true,
description: Diagnostics.List_of_compiler_extensions_to_require
},
{
name: "moduleResolution",
type: {
Expand Down
20 changes: 20 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ namespace ts {
return array1.concat(array2);
}

export function flatten<T>(array1: T[][]): T[] {
if (!array1 || !array1.length) return <any>array1;
return [].concat(...array1);
}

export function groupBy<T>(array: T[], classifier: (item: T) => string): {[index: string]: T[]};
export function groupBy<T>(array: T[], classifier: (item: T) => number): {[index: number]: T[]};
export function groupBy<T>(array: T[], classifier: (item: T) => (string | number)): {[index: string]: T[], [index: number]: T[]} {
if (!array || !array.length) return undefined;
const ret: {[index: string]: T[], [index: number]: T[]} = {};
for (let i = 0, len = array.length; i < len; i++) {
const result = classifier(array[i]);
if (!ret[result]) {
ret[result] = [];
}
ret[result].push(array[i]);
}
return ret;
}

export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
let result: T[];
if (array) {
Expand Down
15 changes: 14 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2656,7 +2656,7 @@
"category": "Message",
"code": 6099
},
"'package.json' does not have 'types' field.": {
"'package.json' does not have '{0}' field.": {
"category": "Message",
"code": 6100
},
Expand Down Expand Up @@ -2789,6 +2789,19 @@
"code": 6132
},

"List of compiler extensions to require.": {
"category": "Message",
"code": 6150
},
"Extension loading failed with error '{0}'.": {
"category": "Error",
"code": 6151
},
"Extension '{0}' exported member '{1}' has extension kind '{2}', but was type '{3}' when type '{4}' was expected.": {
"category": "Error",
"code": 6152
},

"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
219 changes: 204 additions & 15 deletions src/compiler/program.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace ts {
getMemoryUsage?(): number;
exit(exitCode?: number): void;
realpath?(path: string): string;
loadExtension?(name: string): any;
}

export interface FileWatcher {
Expand Down Expand Up @@ -547,6 +548,9 @@ namespace ts {
},
realpath(path: string): string {
return _fs.realpathSync(path);
},
loadExtension(name) {
return require(name);
}
};
}
Expand Down
18 changes: 13 additions & 5 deletions src/compiler/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ namespace ts {
}

const category = DiagnosticCategory[diagnostic.category].toLowerCase();
output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
if (diagnostic.category === DiagnosticCategory.Extension) {
output += `${ category } ${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
}
else {
output += `${ category } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }${ sys.newLine }`;
}

sys.write(output);
}
Expand Down Expand Up @@ -604,13 +609,16 @@ namespace ts {
// First get and report any syntactic errors.
diagnostics = program.getSyntacticDiagnostics();

// Count extension diagnostics and ignore them for determining continued error reporting
const extensionDiagnostics = filter(diagnostics, d => d.category === DiagnosticCategory.Extension).length;

// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.
if (diagnostics.length === 0) {
diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics());
if (diagnostics.length === extensionDiagnostics) {
diagnostics = diagnostics.concat(program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()));

if (diagnostics.length === 0) {
diagnostics = program.getSemanticDiagnostics();
if (diagnostics.length === extensionDiagnostics) {
diagnostics = diagnostics.concat(program.getSemanticDiagnostics());
}
}

Expand Down
69 changes: 68 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,11 @@ namespace ts {
*/
getTypeChecker(): TypeChecker;

/**
* Gets a map of loaded compiler extensions
*/
getCompilerExtensions(): ExtensionCollectionMap;

/* @internal */ getCommonSourceDirectory(): string;

// For testing purposes only. Should not be used by any other consumers (including the
Expand Down Expand Up @@ -1812,6 +1817,7 @@ namespace ts {
getSourceFiles(): SourceFile[];
getSourceFile(fileName: string): SourceFile;
getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective>;
getCompilerExtensions(): ExtensionCollectionMap;
}

export interface TypeChecker {
Expand Down Expand Up @@ -2496,13 +2502,14 @@ namespace ts {
length: number;
messageText: string | DiagnosticMessageChain;
category: DiagnosticCategory;
code: number;
code: number | string;
}

export enum DiagnosticCategory {
Warning,
Error,
Message,
Extension,
}

export enum ModuleResolutionKind {
Expand Down Expand Up @@ -2586,6 +2593,7 @@ namespace ts {
typesSearchPaths?: string[];
/*@internal*/ version?: boolean;
/*@internal*/ watch?: boolean;
extensions?: string[] | Map<any>;

[option: string]: CompilerOptionsValue | undefined;
}
Expand Down Expand Up @@ -2893,6 +2901,57 @@ namespace ts {
failedLookupLocations: string[];
}

export type LintErrorMethod = (err: string, span: Node) => void;
export type LintAcceptMethod = () => void;

/*
* Walkers call accept to decend into the node's children
* Walkers call error to add errors to the output.
*/
export interface LintWalker {
visit(node: Node, accept: LintAcceptMethod, error: LintErrorMethod): void;
}

export interface SyntacticLintProviderStatic {
new (typescript: typeof ts, args: any): LintWalker;
}

export interface SemanticLintProviderStatic {
new (typescript: typeof ts, checker: TypeChecker, args: any): LintWalker;
}

export namespace ExtensionKind {
export const SemanticLint: "semantic-lint" = "semantic-lint";
export type SemanticLint = "semantic-lint";
export const SyntacticLint: "syntactic-lint" = "syntactic-lint";
export type SyntacticLint = "syntactic-lint";
}
export type ExtensionKind = ExtensionKind.SemanticLint | ExtensionKind.SyntacticLint;

export interface ExtensionCollectionMap {
"syntactic-lint"?: SyntacticLintExtension[];
"semantic-lint"?: SemanticLintExtension[];
[index: string]: Extension[] | undefined;
}

export interface ExtensionBase {
name: string;
args: any;
kind: ExtensionKind;
}

// @kind(ExtensionKind.SyntacticLint)
export interface SyntacticLintExtension extends ExtensionBase {
ctor: SyntacticLintProviderStatic;
}

// @kind(ExtensionKind.SemanticLint)
export interface SemanticLintExtension extends ExtensionBase {
ctor: SemanticLintProviderStatic;
}

export type Extension = SyntacticLintExtension | SemanticLintExtension;

export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
Expand All @@ -2919,6 +2978,14 @@ namespace ts {
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];

/**
* Delegates the loading of compiler extensions to the compiler host.
* The function should return the result of executing the code of an extension
* - its exported members. These members will be searched for objects who have been decorated with
* specific flags.
*/
loadExtension?(extension: string): any;
}

export interface TextSpan {
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,19 @@ namespace ts {
return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2);
}

export function createExtensionDiagnosticForNode(node: Node, extension: string, message: string): Diagnostic {
const sourceFile = getSourceFileOfNode(node);
const span = getErrorSpanForNode(sourceFile, node);
return {
file: sourceFile,
messageText: message,
code: extension,
start: span.start,
length: span.length,
category: DiagnosticCategory.Extension
};
}

export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain): Diagnostic {
const sourceFile = getSourceFileOfNode(node);
const span = getErrorSpanForNode(sourceFile, node);
Expand Down
4 changes: 2 additions & 2 deletions src/services/shims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,11 +541,11 @@ namespace ts {
}
}

export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; }[] {
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number | string; }[] {
return diagnostics.map(d => realizeDiagnostic(d, newLine));
}

function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; code: number; } {
function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; code: number | string; } {
return {
message: flattenDiagnosticMessageText(diagnostic.messageText, newLine),
start: diagnostic.start,
Expand Down
Loading

1 comment on commit 8b3aeb8

@alexeagle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Wes, we meet again... I have an intern @ScottSWu working on nearly the same thing, hooking tslint into tsc for correctness issues we want to report at type-check-time.
Can we make some time to discuss your plans? Are you in Redmond this summer?

Please sign in to comment.