Skip to content

Disallow expressions of type void to be used in truthiness checks #26234

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

Merged
merged 2 commits into from
Aug 7, 2018
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20949,6 +20949,7 @@ namespace ts {
}
return numberType;
case SyntaxKind.ExclamationToken:
checkTruthinessExpression(node.operand);
const facts = getTypeFacts(operandType) & (TypeFacts.Truthy | TypeFacts.Falsy);
return facts === TypeFacts.Truthy ? falseType :
facts === TypeFacts.Falsy ? trueType :
Expand Down Expand Up @@ -21316,7 +21317,14 @@ namespace ts {
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode);
}
let leftType = checkExpression(left, checkMode);
let leftType: Type;
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
leftType = checkTruthinessExpression(left, checkMode);
}
else {
leftType = checkExpression(left, checkMode);
}

let rightType = checkExpression(right, checkMode);
switch (operator) {
case SyntaxKind.AsteriskToken:
Expand Down Expand Up @@ -21649,7 +21657,7 @@ namespace ts {
}

function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
checkExpression(node.condition);
checkTruthinessExpression(node.condition);
const type1 = checkExpression(node.whenTrue, checkMode);
const type2 = checkExpression(node.whenFalse, checkMode);
return getUnionType([type1, type2], UnionReduction.Subtype);
Expand Down Expand Up @@ -24512,7 +24520,7 @@ namespace ts {
// Grammar checking
checkGrammarStatementInAmbientContext(node);

checkExpression(node.expression);
checkTruthinessExpression(node.expression);
checkSourceElement(node.thenStatement);

if (node.thenStatement.kind === SyntaxKind.EmptyStatement) {
Expand All @@ -24527,17 +24535,25 @@ namespace ts {
checkGrammarStatementInAmbientContext(node);

checkSourceElement(node.statement);
checkExpression(node.expression);
checkTruthinessExpression(node.expression);
}

function checkWhileStatement(node: WhileStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);

checkExpression(node.expression);
checkTruthinessExpression(node.expression);
checkSourceElement(node.statement);
}

function checkTruthinessExpression(node: Expression, checkMode?: CheckMode) {
const type = checkExpression(node, checkMode);
if (type.flags & TypeFlags.Void) {
error(node, Diagnostics.An_expression_of_type_void_cannot_be_tested_for_truthiness);
}
return type;
}

function checkForStatement(node: ForStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
Expand All @@ -24555,7 +24571,7 @@ namespace ts {
}
}

if (node.condition) checkExpression(node.condition);
if (node.condition) checkTruthinessExpression(node.condition);
if (node.incrementor) checkExpression(node.incrementor);
checkSourceElement(node.statement);
if (node.locals) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,10 @@
"category": "Error",
"code": 1344
},
"An expression of type 'void' cannot be tested for truthiness": {
"category": "Error",
"code": 1345
},

"Duplicate identifier '{0}'.": {
"category": "Error",
Expand Down
3 changes: 3 additions & 0 deletions src/server/scriptInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,15 @@ namespace ts.server {
if (this.isDynamicOrHasMixedContent()) {
this.textStorage.reload("");
this.markContainingProjectsAsDirty();
return true;
}
else {
if (this.textStorage.reloadWithFileText(tempFileName)) {
this.markContainingProjectsAsDirty();
return true;
}
}
return false;
}

/*@internal*/
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8114,7 +8114,7 @@ declare namespace ts.server {
setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences | undefined): void;
getLatestVersion(): string;
saveTo(fileName: string): void;
reloadFromFile(tempFileName?: NormalizedPath): void;
reloadFromFile(tempFileName?: NormalizedPath): boolean;
editContent(start: number, end: number, newText: string): void;
markContainingProjectsAsDirty(): void;
isOrphan(): boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts(36,1): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts(39,1): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts(58,20): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts(61,23): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts(63,32): error TS1345: An expression of type 'void' cannot be tested for truthiness


==== tests/cases/conformance/expressions/conditonalOperator/conditionalOperatorConditionIsObjectType.ts (5 errors) ====
//Cond ? Expr1 : Expr2, Cond is of object type, Expr1 and Expr2 have the same type
var condObject: Object;

var exprAny1: any;
var exprBoolean1: boolean;
var exprNumber1: number;
var exprString1: string;
var exprIsObject1: Object;

var exprAny2: any;
var exprBoolean2: boolean;
var exprNumber2: number;
var exprString2: string;
var exprIsObject2: Object;

function foo() { };
class C { static doIt: () => void };

//Cond is an object type variable
condObject ? exprAny1 : exprAny2;
condObject ? exprBoolean1 : exprBoolean2;
condObject ? exprNumber1 : exprNumber2;
condObject ? exprString1 : exprString2;
condObject ? exprIsObject1 : exprIsObject2;
condObject ? exprString1 : exprBoolean1; // union

//Cond is an object type literal
((a: string) => a.length) ? exprAny1 : exprAny2;
((a: string) => a.length) ? exprBoolean1 : exprBoolean2;
({}) ? exprNumber1 : exprNumber2;
({ a: 1, b: "s" }) ? exprString1 : exprString2;
({ a: 1, b: "s" }) ? exprIsObject1 : exprIsObject2;
({ a: 1, b: "s" }) ? exprString1: exprBoolean1; // union

//Cond is an object type expression
foo() ? exprAny1 : exprAny2;
~~~~~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
new Date() ? exprBoolean1 : exprBoolean2;
new C() ? exprNumber1 : exprNumber2;
C.doIt() ? exprString1 : exprString2;
~~~~~~~~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
condObject.valueOf() ? exprIsObject1 : exprIsObject2;
new Date() ? exprString1 : exprBoolean1; // union

//Results shoud be same as Expr1 and Expr2
var resultIsAny1 = condObject ? exprAny1 : exprAny2;
var resultIsBoolean1 = condObject ? exprBoolean1 : exprBoolean2;
var resultIsNumber1 = condObject ? exprNumber1 : exprNumber2;
var resultIsString1 = condObject ? exprString1 : exprString2;
var resultIsObject1 = condObject ? exprIsObject1 : exprIsObject2;
var resultIsStringOrBoolean1 = condObject ? exprString1 : exprBoolean1; // union

var resultIsAny2 = ((a: string) => a.length) ? exprAny1 : exprAny2;
var resultIsBoolean2 = ((a: string) => a.length) ? exprBoolean1 : exprBoolean2;
var resultIsNumber2 = ({}) ? exprNumber1 : exprNumber2;
var resultIsString2 = ({ a: 1, b: "s" }) ? exprString1 : exprString2;
var resultIsObject2 = ({ a: 1, b: "s" }) ? exprIsObject1 : exprIsObject2;
var resultIsStringOrBoolean2 = ({ a: 1, b: "s" }) ? exprString1 : exprBoolean1; // union

var resultIsAny3 = foo() ? exprAny1 : exprAny2;
~~~~~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
var resultIsBoolean3 = new Date() ? exprBoolean1 : exprBoolean2;
var resultIsNumber3 = new C() ? exprNumber1 : exprNumber2;
var resultIsString3 = C.doIt() ? exprString1 : exprString2;
~~~~~~~~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
var resultIsObject3 = condObject.valueOf() ? exprIsObject1 : exprIsObject2;
var resultIsStringOrBoolean3 = C.doIt() ? exprString1 : exprBoolean1; // union
~~~~~~~~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness

107 changes: 107 additions & 0 deletions tests/baselines/reference/logicalAndOperatorStrictMode.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(46,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(47,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(48,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(49,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(50,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(51,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(52,12): error TS1345: An expression of type 'void' cannot be tested for truthiness
tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts(53,12): error TS1345: An expression of type 'void' cannot be tested for truthiness


==== tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts (8 errors) ====
const a = [0];
const s = "";
const x = 0;
const b = false;
const v: void = undefined;
const u = undefined;
const n = null;
const z = s || x || u;

const a1 = a && a;
const a2 = a && s;
const a3 = a && x;
const a4 = a && b;
const a5 = a && v;
const a6 = a && u;
const a7 = a && n;
const a8 = a && z;

const s1 = s && a;
const s2 = s && s;
const s3 = s && x;
const s4 = s && b;
const s5 = s && v;
const s6 = s && u;
const s7 = s && n;
const s8 = s && z;

const x1 = x && a;
const x2 = x && s;
const x3 = x && x;
const x4 = x && b;
const x5 = x && v;
const x6 = x && u;
const x7 = x && n;
const x8 = x && z;

const b1 = b && a;
const b2 = b && s;
const b3 = b && x;
const b4 = b && b;
const b5 = b && v;
const b6 = b && u;
const b7 = b && n;
const b8 = b && z;

const v1 = v && a;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v2 = v && s;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v3 = v && x;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v4 = v && b;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v5 = v && v;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v6 = v && u;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v7 = v && n;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness
const v8 = v && z;
~
!!! error TS1345: An expression of type 'void' cannot be tested for truthiness

const u1 = u && a;
const u2 = u && s;
const u3 = u && x;
const u4 = u && b;
const u5 = u && v;
const u6 = u && u;
const u7 = u && n;
const u8 = u && z;

const n1 = n && a;
const n2 = n && s;
const n3 = n && x;
const n4 = n && b;
const n5 = n && v;
const n6 = n && u;
const n7 = n && n;
const n8 = n && z;

const z1 = z && a;
const z2 = z && s;
const z3 = z && x;
const z4 = z && b;
const z5 = z && v;
const z6 = z && u;
const z7 = z && n;
const z8 = z && z;
Loading