diff --git a/change-notes/1.20/extractor-javascript.md b/change-notes/1.20/extractor-javascript.md index 6510c606ca2..78987512db4 100644 --- a/change-notes/1.20/extractor-javascript.md +++ b/change-notes/1.20/extractor-javascript.md @@ -19,3 +19,4 @@ ## Changes to code extraction * The extractor now supports [Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing) expressions. +* The TypeScript extractor now handles the control-flow of logical operators and destructuring assignments more accurately. diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index d508217f167..07cab3804a2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -41,7 +41,7 @@ public class Main { * such a way that it may produce different tuples for the same file under the same * {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2018-11-23"; + public static final String EXTRACTOR_VERSION = "2018-12-19"; public static final Pattern NEWLINE = Pattern.compile("\n"); diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java index 557bcc04272..51cc396dec6 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java @@ -60,6 +60,7 @@ import com.semmle.js.ast.ImportSpecifier; import com.semmle.js.ast.InvokeExpression; import com.semmle.js.ast.LabeledStatement; import com.semmle.js.ast.Literal; +import com.semmle.js.ast.LogicalExpression; import com.semmle.js.ast.MemberDefinition; import com.semmle.js.ast.MemberExpression; import com.semmle.js.ast.DeclarationFlags; @@ -825,8 +826,9 @@ public class TypeScriptASTConverter { Expression left = convertChild(node, "left"); Expression right = convertChild(node, "right"); JsonObject operatorToken = node.get("operatorToken").getAsJsonObject(); - String operatorKind = getKind(operatorToken); - if ("CommaToken".equals(operatorKind)) { + String operator = getSourceLocation(operatorToken).getSource(); + switch (operator) { + case ",": List expressions = new ArrayList(); if (left instanceof SequenceExpression) expressions.addAll(((SequenceExpression) left).getExpressions()); @@ -837,10 +839,30 @@ public class TypeScriptASTConverter { else expressions.add(right); return new SequenceExpression(loc, expressions); - } else { - String operator = getSourceLocation(operatorToken).getSource(); - if ("EqualsToken".equals(operatorKind)) - left = convertLValue(left); + + case "||": + case "&&": + return new LogicalExpression(loc, operator, left, right); + + case "=": + left = convertLValue(left); // For plain assignments, the lhs can be a destructuring pattern. + return new AssignmentExpression(loc, operator, left, right); + + case "+=": + case "-=": + case "*=": + case "**=": + case "/=": + case "%=": + case "^=": + case "&=": + case "|=": + case ">>=": + case "<<=": + case ">>>=": + return new AssignmentExpression(loc, operator, convertLValue(left), right); + + default: return new BinaryExpression(loc, operator, left, right); } } diff --git a/javascript/extractor/tests/ts/input/logicalOr.ts b/javascript/extractor/tests/ts/input/logicalOr.ts new file mode 100644 index 00000000000..75536385516 --- /dev/null +++ b/javascript/extractor/tests/ts/input/logicalOr.ts @@ -0,0 +1,4 @@ +function f(x,y) { + if (x || y) {} + if (x && y) {} +} diff --git a/javascript/extractor/tests/ts/output/trap/exprs.ts.trap b/javascript/extractor/tests/ts/output/trap/exprs.ts.trap index 7ebb546ca4d..77aeaf139da 100644 --- a/javascript/extractor/tests/ts/output/trap/exprs.ts.trap +++ b/javascript/extractor/tests/ts/output/trap/exprs.ts.trap @@ -326,11 +326,11 @@ hasLocation(#20098,#20096) exit_cfg_node(#20099,#20004) hasLocation(#20099,#20094) successor(#20012,#20017) -successor(#20033,#20037) -successor(#20043,#20035) +successor(#20033,#20043) successor(#20037,#20039) -successor(#20041,#20043) +successor(#20041,#20035) successor(#20039,#20041) +successor(#20043,#20037) successor(#20035,#20099) successor(#20025,#20027) successor(#20027,#20029) diff --git a/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap b/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap new file mode 100644 index 00000000000..681e53f8b79 --- /dev/null +++ b/javascript/extractor/tests/ts/output/trap/logicalOr.ts.trap @@ -0,0 +1,351 @@ +#10000=@"/logicalOr.ts;sourcefile" +files(#10000,"/logicalOr.ts","logicalOr","ts",0) +#10001=@"/;folder" +folders(#10001,"/","") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +toplevels(#20001,0) +#20002=@"loc,{#10000},1,1,5,0" +locations_default(#20002,#10000,1,1,5,0) +hasLocation(#20001,#20002) +#20003=@"var;{f};{#20000}" +variables(#20003,"f",#20000) +#20004=* +stmts(#20004,17,#20001,0,"functio ... y) {}\n}") +#20005=@"loc,{#10000},1,1,4,1" +locations_default(#20005,#10000,1,1,4,1) +hasLocation(#20004,#20005) +stmtContainers(#20004,#20001) +#20006=* +exprs(#20006,78,#20004,-1,"f") +#20007=@"loc,{#10000},1,10,1,10" +locations_default(#20007,#10000,1,10,1,10) +hasLocation(#20006,#20007) +exprContainers(#20006,#20004) +literals("f","f",#20006) +decl(#20006,#20003) +#20008=* +scopes(#20008,1) +scopenodes(#20004,#20008) +scopenesting(#20008,#20000) +#20009=@"var;{x};{#20008}" +variables(#20009,"x",#20008) +#20010=* +exprs(#20010,78,#20004,0,"x") +#20011=@"loc,{#10000},1,12,1,12" +locations_default(#20011,#10000,1,12,1,12) +hasLocation(#20010,#20011) +exprContainers(#20010,#20004) +literals("x","x",#20010) +decl(#20010,#20009) +#20012=@"var;{y};{#20008}" +variables(#20012,"y",#20008) +#20013=* +exprs(#20013,78,#20004,1,"y") +#20014=@"loc,{#10000},1,14,1,14" +locations_default(#20014,#10000,1,14,1,14) +hasLocation(#20013,#20014) +exprContainers(#20013,#20004) +literals("y","y",#20013) +decl(#20013,#20012) +#20015=@"var;{arguments};{#20008}" +variables(#20015,"arguments",#20008) +isArgumentsObject(#20015) +#20016=* +stmts(#20016,1,#20004,-2,"{\n if ... y) {}\n}") +#20017=@"loc,{#10000},1,17,4,1" +locations_default(#20017,#10000,1,17,4,1) +hasLocation(#20016,#20017) +stmtContainers(#20016,#20004) +#20018=* +stmts(#20018,3,#20016,0,"if (x || y) {}") +#20019=@"loc,{#10000},2,3,2,16" +locations_default(#20019,#10000,2,3,2,16) +hasLocation(#20018,#20019) +stmtContainers(#20018,#20004) +#20020=* +exprs(#20020,45,#20018,0,"x || y") +#20021=@"loc,{#10000},2,7,2,12" +locations_default(#20021,#10000,2,7,2,12) +hasLocation(#20020,#20021) +enclosingStmt(#20020,#20018) +exprContainers(#20020,#20004) +#20022=* +exprs(#20022,79,#20020,0,"x") +#20023=@"loc,{#10000},2,7,2,7" +locations_default(#20023,#10000,2,7,2,7) +hasLocation(#20022,#20023) +enclosingStmt(#20022,#20018) +exprContainers(#20022,#20004) +literals("x","x",#20022) +bind(#20022,#20009) +#20024=* +exprs(#20024,79,#20020,1,"y") +#20025=@"loc,{#10000},2,12,2,12" +locations_default(#20025,#10000,2,12,2,12) +hasLocation(#20024,#20025) +enclosingStmt(#20024,#20018) +exprContainers(#20024,#20004) +literals("y","y",#20024) +bind(#20024,#20012) +#20026=* +stmts(#20026,1,#20018,1,"{}") +#20027=@"loc,{#10000},2,15,2,16" +locations_default(#20027,#10000,2,15,2,16) +hasLocation(#20026,#20027) +stmtContainers(#20026,#20004) +#20028=* +stmts(#20028,3,#20016,1,"if (x && y) {}") +#20029=@"loc,{#10000},3,3,3,16" +locations_default(#20029,#10000,3,3,3,16) +hasLocation(#20028,#20029) +stmtContainers(#20028,#20004) +#20030=* +exprs(#20030,44,#20028,0,"x && y") +#20031=@"loc,{#10000},3,7,3,12" +locations_default(#20031,#10000,3,7,3,12) +hasLocation(#20030,#20031) +enclosingStmt(#20030,#20028) +exprContainers(#20030,#20004) +#20032=* +exprs(#20032,79,#20030,0,"x") +#20033=@"loc,{#10000},3,7,3,7" +locations_default(#20033,#10000,3,7,3,7) +hasLocation(#20032,#20033) +enclosingStmt(#20032,#20028) +exprContainers(#20032,#20004) +literals("x","x",#20032) +bind(#20032,#20009) +#20034=* +exprs(#20034,79,#20030,1,"y") +#20035=@"loc,{#10000},3,12,3,12" +locations_default(#20035,#10000,3,12,3,12) +hasLocation(#20034,#20035) +enclosingStmt(#20034,#20028) +exprContainers(#20034,#20004) +literals("y","y",#20034) +bind(#20034,#20012) +#20036=* +stmts(#20036,1,#20028,1,"{}") +#20037=@"loc,{#10000},3,15,3,16" +locations_default(#20037,#10000,3,15,3,16) +hasLocation(#20036,#20037) +stmtContainers(#20036,#20004) +numlines(#20004,4,4,0) +#20038=* +lines(#20038,#20001,"function f(x,y) {"," +") +#20039=@"loc,{#10000},1,1,1,17" +locations_default(#20039,#10000,1,1,1,17) +hasLocation(#20038,#20039) +#20040=* +lines(#20040,#20001," if (x || y) {}"," +") +#20041=@"loc,{#10000},2,1,2,16" +locations_default(#20041,#10000,2,1,2,16) +hasLocation(#20040,#20041) +indentation(#10000,2," ",2) +#20042=* +lines(#20042,#20001," if (x && y) {}"," +") +#20043=@"loc,{#10000},3,1,3,16" +locations_default(#20043,#10000,3,1,3,16) +hasLocation(#20042,#20043) +indentation(#10000,3," ",2) +#20044=* +lines(#20044,#20001,"}"," +") +#20045=@"loc,{#10000},4,1,4,1" +locations_default(#20045,#10000,4,1,4,1) +hasLocation(#20044,#20045) +numlines(#20001,4,4,0) +#20046=* +tokeninfo(#20046,7,#20001,0,"function") +#20047=@"loc,{#10000},1,1,1,8" +locations_default(#20047,#10000,1,1,1,8) +hasLocation(#20046,#20047) +#20048=* +tokeninfo(#20048,6,#20001,1,"f") +hasLocation(#20048,#20007) +#20049=* +tokeninfo(#20049,8,#20001,2,"(") +#20050=@"loc,{#10000},1,11,1,11" +locations_default(#20050,#10000,1,11,1,11) +hasLocation(#20049,#20050) +#20051=* +tokeninfo(#20051,6,#20001,3,"x") +hasLocation(#20051,#20011) +#20052=* +tokeninfo(#20052,8,#20001,4,",") +#20053=@"loc,{#10000},1,13,1,13" +locations_default(#20053,#10000,1,13,1,13) +hasLocation(#20052,#20053) +#20054=* +tokeninfo(#20054,6,#20001,5,"y") +hasLocation(#20054,#20014) +#20055=* +tokeninfo(#20055,8,#20001,6,")") +#20056=@"loc,{#10000},1,15,1,15" +locations_default(#20056,#10000,1,15,1,15) +hasLocation(#20055,#20056) +#20057=* +tokeninfo(#20057,8,#20001,7,"{") +#20058=@"loc,{#10000},1,17,1,17" +locations_default(#20058,#10000,1,17,1,17) +hasLocation(#20057,#20058) +#20059=* +tokeninfo(#20059,7,#20001,8,"if") +#20060=@"loc,{#10000},2,3,2,4" +locations_default(#20060,#10000,2,3,2,4) +hasLocation(#20059,#20060) +#20061=* +tokeninfo(#20061,8,#20001,9,"(") +#20062=@"loc,{#10000},2,6,2,6" +locations_default(#20062,#10000,2,6,2,6) +hasLocation(#20061,#20062) +#20063=* +tokeninfo(#20063,6,#20001,10,"x") +hasLocation(#20063,#20023) +#20064=* +tokeninfo(#20064,8,#20001,11,"||") +#20065=@"loc,{#10000},2,9,2,10" +locations_default(#20065,#10000,2,9,2,10) +hasLocation(#20064,#20065) +#20066=* +tokeninfo(#20066,6,#20001,12,"y") +hasLocation(#20066,#20025) +#20067=* +tokeninfo(#20067,8,#20001,13,")") +#20068=@"loc,{#10000},2,13,2,13" +locations_default(#20068,#10000,2,13,2,13) +hasLocation(#20067,#20068) +#20069=* +tokeninfo(#20069,8,#20001,14,"{") +#20070=@"loc,{#10000},2,15,2,15" +locations_default(#20070,#10000,2,15,2,15) +hasLocation(#20069,#20070) +#20071=* +tokeninfo(#20071,8,#20001,15,"}") +#20072=@"loc,{#10000},2,16,2,16" +locations_default(#20072,#10000,2,16,2,16) +hasLocation(#20071,#20072) +#20073=* +tokeninfo(#20073,7,#20001,16,"if") +#20074=@"loc,{#10000},3,3,3,4" +locations_default(#20074,#10000,3,3,3,4) +hasLocation(#20073,#20074) +#20075=* +tokeninfo(#20075,8,#20001,17,"(") +#20076=@"loc,{#10000},3,6,3,6" +locations_default(#20076,#10000,3,6,3,6) +hasLocation(#20075,#20076) +#20077=* +tokeninfo(#20077,6,#20001,18,"x") +hasLocation(#20077,#20033) +#20078=* +tokeninfo(#20078,8,#20001,19,"&&") +#20079=@"loc,{#10000},3,9,3,10" +locations_default(#20079,#10000,3,9,3,10) +hasLocation(#20078,#20079) +#20080=* +tokeninfo(#20080,6,#20001,20,"y") +hasLocation(#20080,#20035) +#20081=* +tokeninfo(#20081,8,#20001,21,")") +#20082=@"loc,{#10000},3,13,3,13" +locations_default(#20082,#10000,3,13,3,13) +hasLocation(#20081,#20082) +#20083=* +tokeninfo(#20083,8,#20001,22,"{") +#20084=@"loc,{#10000},3,15,3,15" +locations_default(#20084,#10000,3,15,3,15) +hasLocation(#20083,#20084) +#20085=* +tokeninfo(#20085,8,#20001,23,"}") +#20086=@"loc,{#10000},3,16,3,16" +locations_default(#20086,#10000,3,16,3,16) +hasLocation(#20085,#20086) +#20087=* +tokeninfo(#20087,8,#20001,24,"}") +hasLocation(#20087,#20045) +#20088=* +tokeninfo(#20088,0,#20001,25,"") +#20089=@"loc,{#10000},5,1,5,0" +locations_default(#20089,#10000,5,1,5,0) +hasLocation(#20088,#20089) +#20090=* +entry_cfg_node(#20090,#20001) +#20091=@"loc,{#10000},1,1,1,0" +locations_default(#20091,#10000,1,1,1,0) +hasLocation(#20090,#20091) +#20092=* +exit_cfg_node(#20092,#20001) +hasLocation(#20092,#20089) +successor(#20004,#20092) +#20093=* +entry_cfg_node(#20093,#20004) +hasLocation(#20093,#20091) +#20094=* +exit_cfg_node(#20094,#20004) +#20095=@"loc,{#10000},4,2,4,1" +locations_default(#20095,#10000,4,2,4,1) +hasLocation(#20094,#20095) +successor(#20016,#20018) +successor(#20028,#20030) +successor(#20030,#20032) +#20096=* +guard_node(#20096,1,#20032) +hasLocation(#20096,#20033) +successor(#20096,#20034) +#20097=* +guard_node(#20097,0,#20032) +hasLocation(#20097,#20033) +successor(#20097,#20094) +successor(#20032,#20096) +successor(#20032,#20097) +#20098=* +guard_node(#20098,1,#20034) +hasLocation(#20098,#20035) +successor(#20098,#20036) +#20099=* +guard_node(#20099,0,#20034) +hasLocation(#20099,#20035) +successor(#20099,#20094) +successor(#20034,#20098) +successor(#20034,#20099) +successor(#20036,#20094) +successor(#20018,#20020) +successor(#20020,#20022) +#20100=* +guard_node(#20100,1,#20022) +hasLocation(#20100,#20023) +successor(#20100,#20026) +#20101=* +guard_node(#20101,0,#20022) +hasLocation(#20101,#20023) +successor(#20101,#20024) +successor(#20022,#20100) +successor(#20022,#20101) +#20102=* +guard_node(#20102,1,#20024) +hasLocation(#20102,#20025) +successor(#20102,#20026) +#20103=* +guard_node(#20103,0,#20024) +hasLocation(#20103,#20025) +successor(#20103,#20028) +successor(#20024,#20102) +successor(#20024,#20103) +successor(#20026,#20028) +successor(#20013,#20016) +successor(#20010,#20013) +successor(#20093,#20010) +successor(#20006,#20004) +successor(#20090,#20006) +numlines(#10000,4,4,0) +filetype(#10000,"typescript")