Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per file option: strictNullChecks #50347

Closed
wants to merge 11 commits into from
Closed
2 changes: 1 addition & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ namespace ts {
return bindSourceFile;

function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean {
if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) {
if (getStrictOptionValue(file, opts, "alwaysStrict") && !file.isDeclarationFile) {
// bind in strict mode source files with alwaysStrict option
return true;
}
Expand Down
881 changes: 509 additions & 372 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

208 changes: 172 additions & 36 deletions src/compiler/commandLineParser.ts

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,7 @@ namespace ts {
* (0.4 allows 1 substitution/transposition for every 5 characters,
* and 1 insertion/deletion at 3 characters)
*/
export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined {
export function getSpellingSuggestion<T>(name: string, candidates: readonly T[], getName: (candidate: T) => string | undefined): T | undefined {
const maximumLengthDifference = Math.max(2, Math.floor(name.length * 0.34));
let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother.
let bestCandidate: T | undefined;
Expand Down Expand Up @@ -2510,4 +2510,8 @@ namespace ts {
}
return s.slice(0, end + 1);
}

export function is<T>(): <U extends T>(arg: U) => U {
return identity;
}
}
46 changes: 45 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9516,13 +9516,15 @@ namespace ts {
export interface PragmaContext {
languageVersion: ScriptTarget;
pragmas?: PragmaMap;
localOptions?: CompilerOptions;
checkJsDirective?: CheckJsDirective;
referencedFiles: FileReference[];
typeReferenceDirectives: FileReference[];
libReferenceDirectives: FileReference[];
amdDependencies: AmdDependency[];
hasNoDefaultLib?: boolean;
moduleName?: string;

}

function parseResolutionMode(mode: string | undefined, pos: number, end: number, reportDiagnostic: PragmaDiagnosticReporter): ModuleKind.ESNext | ModuleKind.CommonJS | undefined {
Expand Down Expand Up @@ -9639,12 +9641,54 @@ namespace ts {
});
break;
}
case "ts-strict":
case "ts-noimplicitany":
case "ts-strictnullchecks":
case "ts-strictfunctiontypes":
case "ts-strictbindcallapply":
case "ts-noimplicitthis":
case "ts-strictpropertyinitialization":
case "ts-useunknownincatchvariables":
case "ts-alwaysstrict":
case "ts-nounusedlocals":
case "ts-nounusedparameters":
case "ts-exactoptionalpropertytypes":
case "ts-nopropertyaccessfromindexsignature":
case "ts-noimplicitreturns":
case "ts-nofallthroughcasesinswitch":
case "ts-nouncheckedindexedaccess":
case "ts-noimplicitoverride": {
const optName = key.slice(3);
const opt = find(optionsAllowedAsPragmaOption, o => o.name.toLowerCase() === optName)!;
const entry = (isArray(entryOrList) ? last(entryOrList) : entryOrList);
const unparsedValue = (entry.arguments as PragmaArgumentType<`ts-${Lowercase<FileLocalOptionName>}`>).value;
const optContainer: OptionsBase = {};
const errors: Diagnostic[] = [];
const parsedValue = unparsedValue === undefined ? true : (parseOptionValue([unparsedValue], 0, /*diagnostics*/ undefined, opt, optContainer, errors), optContainer[opt.name]);
if (unparsedValue === undefined && opt.type !== "boolean") {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, optName));
}
for (const err of errors) {
reportDiagnostic(entry.range.pos, entry.range.end - entry.range.pos, {
category: err.category,
code: err.code,
message: err.messageText as string,
reportsDeprecated: err.reportsDeprecated,
reportsUnnecessary: err.reportsUnnecessary,
key: err.messageText as string
});
}
if (!length(errors)) {
(context.localOptions ??= {})[opt.name as string] = parsedValue;
}
break;
}
case "jsx":
case "jsxfrag":
case "jsximportsource":
case "jsxruntime":
return; // Accessed directly
default: Debug.fail("Unhandled pragma kind"); // Can this be made into an assertNever in the future?
default: Debug.assertNever(key, `Unhandled pragma kind: ${key}`);
}
});
}
Expand Down
12 changes: 6 additions & 6 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3259,7 +3259,7 @@ namespace ts {
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
const shouldAddFile = resolvedFileName
&& !getResolutionDiagnostic(optionsForFile, resolution)
&& !getResolutionDiagnostic(file, optionsForFile, resolution)
&& !optionsForFile.noResolve
&& index < file.imports.length
&& !elideImport
Expand Down Expand Up @@ -3361,10 +3361,10 @@ namespace ts {
}

function verifyCompilerOptions() {
if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
if (options.strictPropertyInitialization && !getStrictOptionValue(/*file*/ undefined, options, "strictNullChecks")) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
}
if (options.exactOptionalPropertyTypes && !getStrictOptionValue(options, "strictNullChecks")) {
if (options.exactOptionalPropertyTypes && !getStrictOptionValue(/*file*/ undefined, options, "strictNullChecks")) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "exactOptionalPropertyTypes", "strictNullChecks");
}

Expand Down Expand Up @@ -3493,7 +3493,7 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
}

if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) {
if (options.noImplicitUseStrict && getStrictOptionValue(/*file*/ undefined, options, "alwaysStrict")) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
}

Expand Down Expand Up @@ -4293,7 +4293,7 @@ namespace ts {
* The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
* This returns a diagnostic even if the module will be an untyped module.
*/
export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
export function getResolutionDiagnostic(file: SourceFile, options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
switch (extension) {
case Extension.Ts:
case Extension.Dts:
Expand All @@ -4313,7 +4313,7 @@ namespace ts {
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
}
function needAllowJs() {
return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
return getAllowJSCompilerOption(options) || !getStrictOptionValue(file, options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
}
function needResolveJsonModule() {
return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/module/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ namespace ts {
startLexicalEnvironment();

const statements: Statement[] = [];
const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
const ensureUseStrict = getStrictOptionValue(node, compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict && !isJsonSourceFile(node), topLevelVisitor);

if (shouldEmitUnderscoreUnderscoreESModule()) {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/module/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ namespace ts {
startLexicalEnvironment();

// Add any prologue directives.
const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
const ensureUseStrict = getStrictOptionValue(node, compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile));
const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict, topLevelVisitor);

// var __moduleName = context_1 && context_1.id;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ namespace ts {
}

function visitSourceFile(node: SourceFile) {
const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") &&
const alwaysStrict = getStrictOptionValue(node, compilerOptions, "alwaysStrict") &&
!(isExternalModule(node) && moduleKind >= ModuleKind.ES2015) &&
!isJsonSourceFile(node);

Expand Down
3 changes: 1 addition & 2 deletions src/compiler/transformers/typeSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ namespace ts {
const resolver = context.getEmitResolver();
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");

let currentLexicalScope: SourceFile | CaseBlock | ModuleBlock | Block;
let currentNameScope: ClassLikeDeclaration | undefined;
Expand Down Expand Up @@ -344,7 +343,7 @@ namespace ts {
return factory.createIdentifier("Object"); // Reduce to `any` in a union or intersection
}

if (!strictNullChecks && ((isLiteralTypeNode(typeNode) && typeNode.literal.kind === SyntaxKind.NullKeyword) || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
if (!getStrictOptionValue(getSourceFileOfNode(currentLexicalScope), compilerOptions, "strictNullChecks") && ((isLiteralTypeNode(typeNode) && typeNode.literal.kind === SyntaxKind.NullKeyword) || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
}

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ namespace ts {
function getCompilerOptionsOfBuildOptions(buildOptions: BuildOptions): CompilerOptions {
const result = {} as CompilerOptions;
commonOptionsWithBuild.forEach(option => {
if (hasProperty(buildOptions, option.name)) result[option.name] = buildOptions[option.name];
if (hasProperty(buildOptions, option.name)) result[option.name as string] = buildOptions[option.name];
});
return result;
}
Expand Down
Loading