Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 0 additions & 9 deletions internal/ast/parseoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,9 @@ import (
type SourceFileParseOptions struct {
FileName string
Path tspath.Path
CompilerOptions core.SourceFileAffectingCompilerOptions
ExternalModuleIndicatorOptions ExternalModuleIndicatorOptions
}

func GetSourceFileAffectingCompilerOptions(fileName string, options *core.CompilerOptions) core.SourceFileAffectingCompilerOptions {
// Declaration files are not parsed/bound differently depending on compiler options.
if tspath.IsDeclarationFileName(fileName) {
return core.SourceFileAffectingCompilerOptions{}
}
return options.SourceFileAffecting()
}

type ExternalModuleIndicatorOptions struct {
JSX bool
Force bool
Expand Down
71 changes: 16 additions & 55 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ type Binder struct {
seenThisKeyword bool
hasExplicitReturn bool
hasFlowEffects bool
inStrictMode bool
inAssignmentPattern bool
seenParseError bool
symbolCount int
Expand All @@ -83,10 +82,6 @@ type Binder struct {
expandoAssignments []ExpandoAssignmentInfo
}

func (b *Binder) options() core.SourceFileAffectingCompilerOptions {
return b.file.ParseOptions().CompilerOptions
}

type ActiveLabel struct {
next *ActiveLabel
breakTarget *ast.FlowLabel
Expand Down Expand Up @@ -128,7 +123,6 @@ func bindSourceFile(file *ast.SourceFile) {
b := getBinder()
defer putBinder(b)
b.file = file
b.inStrictMode = b.options().BindInStrictMode && !file.IsDeclarationFile || ast.IsExternalModule(file)
b.unreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable)
b.bind(file.AsNode())
b.bindDeferredExpandoAssignments()
Expand Down Expand Up @@ -590,7 +584,6 @@ func (b *Binder) bind(node *ast.Node) bool {
if node == nil {
return false
}
saveInStrictMode := b.inStrictMode
// Even though in the AST the jsdoc @typedef node belongs to the current node,
// its symbol might be in the same scope with the current node's symbol. Consider:
//
Expand Down Expand Up @@ -692,7 +685,6 @@ func (b *Binder) bind(node *ast.Node) bool {
case ast.KindFunctionExpression, ast.KindArrowFunction:
b.bindFunctionExpression(node)
case ast.KindClassExpression, ast.KindClassDeclaration:
b.inStrictMode = true
b.bindClassLikeDeclaration(node)
case ast.KindInterfaceDeclaration:
b.bindBlockScopedDeclaration(node, ast.SymbolFlagsInterface, ast.SymbolFlagsInterfaceExcludes)
Expand Down Expand Up @@ -723,14 +715,7 @@ func (b *Binder) bind(node *ast.Node) bool {
case ast.KindExportAssignment, ast.KindJSExportAssignment:
b.bindExportAssignment(node)
case ast.KindSourceFile:
b.updateStrictModeStatementList(node.StatementList())
b.bindSourceFileIfExternalModule()
case ast.KindBlock:
if ast.IsFunctionLikeOrClassStaticBlockDeclaration(node.Parent) {
b.updateStrictModeStatementList(node.StatementList())
}
case ast.KindModuleBlock:
b.updateStrictModeStatementList(node.StatementList())
case ast.KindJsxAttributes:
b.bindJsxAttributes(node)
case ast.KindJsxAttribute:
Expand Down Expand Up @@ -760,7 +745,6 @@ func (b *Binder) bind(node *ast.Node) bool {
node.Flags |= ast.NodeFlagsThisNodeOrAnySubNodesHasError
b.seenParseError = true
}
b.inStrictMode = saveInStrictMode
return false
}

Expand Down Expand Up @@ -1151,9 +1135,7 @@ func (b *Binder) bindEnumDeclaration(node *ast.Node) {
}

func (b *Binder) bindVariableDeclarationOrBindingElement(node *ast.Node) {
if b.inStrictMode {
b.checkStrictModeEvalOrArguments(node, node.Name())
}
b.checkStrictModeEvalOrArguments(node, node.Name())
if name := node.Name(); name != nil && !ast.IsBindingPattern(name) {
switch {
case ast.IsVariableDeclarationInitializedToRequire(node):
Expand All @@ -1179,7 +1161,7 @@ func (b *Binder) bindVariableDeclarationOrBindingElement(node *ast.Node) {

func (b *Binder) bindParameter(node *ast.Node) {
decl := node.AsParameterDeclaration()
if b.inStrictMode && node.Flags&ast.NodeFlagsAmbient == 0 {
if node.Flags&ast.NodeFlagsAmbient == 0 {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
b.checkStrictModeEvalOrArguments(node, decl.Name())
Expand All @@ -1201,11 +1183,7 @@ func (b *Binder) bindParameter(node *ast.Node) {

func (b *Binder) bindFunctionDeclaration(node *ast.Node) {
b.checkStrictModeFunctionName(node)
if b.inStrictMode {
b.bindBlockScopedDeclaration(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes)
} else {
b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes)
}
b.bindBlockScopedDeclaration(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes)
}

func (b *Binder) getInferTypeContainer(node *ast.Node) *ast.Node {
Expand Down Expand Up @@ -1298,7 +1276,7 @@ func (b *Binder) checkContextualIdentifier(node *ast.Node) {
if originalKeywordKind == ast.KindIdentifier {
return
}
if b.inStrictMode && originalKeywordKind >= ast.KindFirstFutureReservedWord && originalKeywordKind <= ast.KindLastFutureReservedWord {
if originalKeywordKind >= ast.KindFirstFutureReservedWord && originalKeywordKind <= ast.KindLastFutureReservedWord {
b.errorOnNode(node, b.getStrictModeIdentifierMessage(node), scanner.DeclarationNameToString(node))
} else if originalKeywordKind == ast.KindAwaitKeyword {
if ast.IsExternalModule(b.file) && ast.IsInTopLevelContext(node) {
Expand Down Expand Up @@ -1333,15 +1311,6 @@ func (b *Binder) getStrictModeIdentifierMessage(node *ast.Node) *diagnostics.Mes
return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode
}

func (b *Binder) updateStrictModeStatementList(statements *ast.NodeList) {
if !b.inStrictMode {
useStrictDirective := FindUseStrictPrologue(b.file, statements.Nodes)
if useStrictDirective != nil {
b.inStrictMode = true
}
}
}

// Should be called only on prologue directives (ast.IsPrologueDirective(node) should be true)
func isUseStrictPrologueDirective(sourceFile *ast.SourceFile, node *ast.Node) bool {
nodeText := scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, node.Expression(), false /*includeTrivia*/)
Expand All @@ -1365,7 +1334,7 @@ func FindUseStrictPrologue(sourceFile *ast.SourceFile, statements []*ast.Node) *
}

func (b *Binder) checkStrictModeFunctionName(node *ast.Node) {
if b.inStrictMode && node.Flags&ast.NodeFlagsAmbient == 0 {
if node.Flags&ast.NodeFlagsAmbient == 0 {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1))
b.checkStrictModeEvalOrArguments(node, node.Name())
}
Expand All @@ -1384,7 +1353,7 @@ func (b *Binder) getStrictModeBlockScopeFunctionDeclarationMessage(node *ast.Nod

func (b *Binder) checkStrictModeBinaryExpression(node *ast.Node) {
expr := node.AsBinaryExpression()
if b.inStrictMode && ast.IsLeftHandSideExpression(expr.Left) && ast.IsAssignmentOperator(expr.OperatorToken.Kind) {
if ast.IsLeftHandSideExpression(expr.Left) && ast.IsAssignmentOperator(expr.OperatorToken.Kind) {
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3)
b.checkStrictModeEvalOrArguments(node, expr.Left)
Expand All @@ -1395,15 +1364,15 @@ func (b *Binder) checkStrictModeCatchClause(node *ast.Node) {
// It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the
// Catch production is eval or arguments
clause := node.AsCatchClause()
if b.inStrictMode && clause.VariableDeclaration != nil {
if clause.VariableDeclaration != nil {
b.checkStrictModeEvalOrArguments(node, clause.VariableDeclaration.AsVariableDeclaration().Name())
}
}

func (b *Binder) checkStrictModeDeleteExpression(node *ast.Node) {
// Grammar checking
expr := node.AsDeleteExpression()
if b.inStrictMode && expr.Expression.Kind == ast.KindIdentifier {
if expr.Expression.Kind == ast.KindIdentifier {
// When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
// UnaryExpression is a direct reference to a variable, function argument, or function name
b.errorOnNode(expr.Expression, diagnostics.X_delete_cannot_be_called_on_an_identifier_in_strict_mode)
Expand All @@ -1415,35 +1384,27 @@ func (b *Binder) checkStrictModePostfixUnaryExpression(node *ast.Node) {
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
// operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator.
if b.inStrictMode {
b.checkStrictModeEvalOrArguments(node, node.AsPostfixUnaryExpression().Operand)
}
b.checkStrictModeEvalOrArguments(node, node.AsPostfixUnaryExpression().Operand)
}

func (b *Binder) checkStrictModePrefixUnaryExpression(node *ast.Node) {
// Grammar checking
if b.inStrictMode {
expr := node.AsPrefixUnaryExpression()
if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken {
b.checkStrictModeEvalOrArguments(node, expr.Operand)
}
expr := node.AsPrefixUnaryExpression()
if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken {
b.checkStrictModeEvalOrArguments(node, expr.Operand)
}
}

func (b *Binder) checkStrictModeWithStatement(node *ast.Node) {
// Grammar checking for withStatement
if b.inStrictMode {
b.errorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_strict_mode)
}
b.errorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_strict_mode)
}

func (b *Binder) checkStrictModeLabeledStatement(node *ast.Node) {
// Grammar checking for labeledStatement
if b.inStrictMode {
data := node.AsLabeledStatement()
if ast.IsDeclarationStatement(data.Statement) || ast.IsVariableStatement(data.Statement) {
b.errorOnFirstToken(data.Label, diagnostics.A_label_is_not_allowed_here)
}
data := node.AsLabeledStatement()
if ast.IsDeclarationStatement(data.Statement) || ast.IsVariableStatement(data.Statement) {
b.errorOnFirstToken(data.Label, diagnostics.A_label_is_not_allowed_here)
}
}

Expand Down
8 changes: 2 additions & 6 deletions internal/binder/binder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,9 @@ func BenchmarkBind(b *testing.B) {
path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames())
sourceText := f.ReadFile(b)

compilerOptions := &core.CompilerOptions{Target: core.ScriptTargetESNext, Module: core.ModuleKindNodeNext}
sourceAffecting := compilerOptions.SourceFileAffecting()

parseOptions := ast.SourceFileParseOptions{
FileName: fileName,
Path: path,
CompilerOptions: sourceAffecting,
FileName: fileName,
Path: path,
}
scriptKind := core.GetScriptKindFromFileName(fileName)

Expand Down
1 change: 0 additions & 1 deletion internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,6 @@ func (p *fileLoader) parseSourceFile(t *parseTask) *ast.SourceFile {
sourceFile := p.opts.Host.GetSourceFile(ast.SourceFileParseOptions{
FileName: t.normalizedFilePath,
Path: path,
CompilerOptions: ast.GetSourceFileAffectingCompilerOptions(t.normalizedFilePath, options),
ExternalModuleIndicatorOptions: ast.GetExternalModuleIndicatorOptions(t.normalizedFilePath, options, t.metadata),
})
return sourceFile
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,10 @@ func (p *Program) verifyCompilerOptions() {
createRemovedOptionDiagnostic("module", "UMD", "")
}

if options.AlwaysStrict.IsFalse() {
createRemovedOptionDiagnostic("alwaysStrict", "false", "")
}

if options.StrictPropertyInitialization.IsTrue() && !options.GetStrictOptionValue(options.StrictNullChecks) {
createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks")
}
Expand Down
22 changes: 2 additions & 20 deletions internal/core/compileroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package core
import (
"reflect"
"strings"
"sync"

"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/tspath"
Expand All @@ -25,7 +24,6 @@ type CompilerOptions struct {
AllowUnreachableCode Tristate `json:"allowUnreachableCode,omitzero"`
AllowUnusedLabels Tristate `json:"allowUnusedLabels,omitzero"`
AssumeChangesOnlyAffectDirectDependencies Tristate `json:"assumeChangesOnlyAffectDirectDependencies,omitzero"`
AlwaysStrict Tristate `json:"alwaysStrict,omitzero"`
CheckJs Tristate `json:"checkJs,omitzero"`
CustomConditions []string `json:"customConditions,omitzero"`
Composite Tristate `json:"composite,omitzero"`
Expand Down Expand Up @@ -121,6 +119,8 @@ type CompilerOptions struct {
VerbatimModuleSyntax Tristate `json:"verbatimModuleSyntax,omitzero"`
MaxNodeModuleJsDepth *int `json:"maxNodeModuleJsDepth,omitzero"`

// Deprecated: Do not use outside of options parsing and validation.
AlwaysStrict Tristate `json:"alwaysStrict,omitzero"`
// Deprecated: Do not use outside of options parsing and validation.
BaseUrl string `json:"baseUrl,omitzero"`
// Deprecated: Do not use outside of options parsing and validation.
Expand Down Expand Up @@ -152,9 +152,6 @@ type CompilerOptions struct {
SingleThreaded Tristate `json:"singleThreaded,omitzero"`
Quiet Tristate `json:"quiet,omitzero"`
Checkers *int `json:"checkers,omitzero"`

sourceFileAffectingCompilerOptionsOnce sync.Once
sourceFileAffectingCompilerOptions SourceFileAffectingCompilerOptions
}

// noCopy may be embedded into structs which must not be copied
Expand Down Expand Up @@ -367,21 +364,6 @@ func (options *CompilerOptions) GetPathsBasePath(currentDirectory string) string
return currentDirectory
}

// SourceFileAffectingCompilerOptions are the precomputed CompilerOptions values which
// affect the parse and bind of a source file.
type SourceFileAffectingCompilerOptions struct {
BindInStrictMode bool
}

func (options *CompilerOptions) SourceFileAffecting() SourceFileAffectingCompilerOptions {
options.sourceFileAffectingCompilerOptionsOnce.Do(func() {
options.sourceFileAffectingCompilerOptions = SourceFileAffectingCompilerOptions{
BindInStrictMode: options.AlwaysStrict.IsTrue() || options.Strict.IsTrue(),
}
})
return options.sourceFileAffectingCompilerOptions
}

type ModuleDetectionKind int32

const (
Expand Down
6 changes: 2 additions & 4 deletions internal/project/autoimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/compiler"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/ls/autoimport"
"github.com/microsoft/typescript-go/internal/packagejson"
"github.com/microsoft/typescript-go/internal/tspath"
Expand Down Expand Up @@ -152,9 +151,8 @@ func (a *autoImportRegistryCloneHost) GetSourceFile(fileName string, path tspath
return nil
}
opts := ast.SourceFileParseOptions{
FileName: fileName,
Path: path,
CompilerOptions: core.EmptyCompilerOptions.SourceFileAffecting(),
FileName: fileName,
Path: path,
}
key := NewParseCacheKey(opts, fh.Hash(), fh.Kind())
result := a.parseCache.Load(key, fh)
Expand Down
7 changes: 5 additions & 2 deletions internal/project/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,12 @@ func TestSession(t *testing.T) {
files := maps.Clone(defaultFiles)
files["/home/projects/TS/p2/tsconfig.json"] = `{
"compilerOptions": {
"noLib": true,
"module": "nodenext",
"jsx": "react"
}
"strict": true,
"moduleDetection": "auto"
},
"include": ["src"]
}`
files["/home/projects/TS/p2/src/index.ts"] = `import { x } from "../../p1/src/x";`
session, _ := projecttestutil.Setup(files)
Expand Down
5 changes: 2 additions & 3 deletions internal/testutil/tsbaseline/js_emit_baseline.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ func DoJSEmitBaseline(
}
if len(result.Diagnostics) == 0 && strings.HasSuffix(file.UnitName, tspath.ExtensionJson) {
fileParseResult := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: file.UnitName,
Path: tspath.Path(file.UnitName),
CompilerOptions: options.SourceFileAffecting(),
FileName: file.UnitName,
Path: tspath.Path(file.UnitName),
}, file.Content, core.ScriptKindJSON)
if len(fileParseResult.Diagnostics()) > 0 {
jsCode.WriteString(GetErrorBaseline(t, []*harnessutil.TestFile{file}, diagnosticwriter.WrapASTDiagnostics(fileParseResult.Diagnostics()), diagnosticwriter.CompareASTDiagnostics, false /*pretty*/))
Expand Down
13 changes: 4 additions & 9 deletions internal/transformers/estransforms/usestrict.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,8 @@ func (tx *useStrictTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node
return node.AsNode()
}

if isExternalModule ||
tx.compilerOptions.AlwaysStrict.IsTrueOrUnknown() {
statements := tx.Factory().EnsureUseStrict(node.Statements.Nodes)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).AsSourceFile().AsNode()
}

return node.AsNode()
statements := tx.Factory().EnsureUseStrict(node.Statements.Nodes)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).AsSourceFile().AsNode()
}
Loading
Loading