Skip to content

Commit d9916a3

Browse files
rahul-kamatcopybara-github
authored andcommitted
Restrict goog.callerLocation usage to being a default value in a function's parameter's list
Throw a generic JSC_CALLER_LOCATION_MISUSE_ERROR if `goog.callerLocation` is used anywhere it shouldn't be PiperOrigin-RevId: 693487657
1 parent 794ed9f commit d9916a3

File tree

2 files changed

+80
-43
lines changed

2 files changed

+80
-43
lines changed

src/com/google/javascript/jscomp/RewriteCallerCodeLocation.java

+45-22
Original file line numberDiff line numberDiff line change
@@ -92,33 +92,56 @@ private class FindCallerLocationFunctions extends AbstractPostOrderCallback {
9292

9393
@Override
9494
public void visit(NodeTraversal t, Node n, Node parent) {
95+
if (isGoogCallerLocationMisused(n, parent)) {
96+
compiler.report(JSError.make(parent.getParent(), JSC_CALLER_LOCATION_MISUSE_ERROR));
97+
}
98+
9599
if (n.isParamList()) {
96100
visitParamListAndAddCallerLocationFunctionNames(n, t);
97101
}
98-
if (n.isCall()) {
99-
// Check for misuse of goog.callerLocation.
100-
Node firstChild = n.getFirstChild();
101-
if (GOOG_CALLER_LOCATION_QUALIFIED_NAME.matches(firstChild)) {
102-
if (!parent.isDefaultValue()) {
103-
// Throw an error when goog.callerLocation() is NOT used as a default value in a
104-
// function's parameter.
105-
compiler.report(JSError.make(firstChild, JSC_CALLER_LOCATION_MISUSE_ERROR));
106-
}
107-
if (parent.getParent().isStringKey()) {
108-
// Throw an error when goog.callerLocation() is used in an object literal.
109-
// E.g:
110-
// function foo({val1, val2, here = goog.callerLocation()}) {}
111-
// The AST for `here = goog.callerLocation()`looks like:
112-
// STRING_KEY here (This node tells us we are in an object literal)
113-
// DEFAULT_VALUE
114-
// NAME here 1:26
115-
// CALL 1:33
116-
// GETPROP callerLocation
117-
// NAME goog 1:33
118-
compiler.report(JSError.make(parent.getParent(), JSC_CALLER_LOCATION_MISUSE_ERROR));
119-
}
102+
}
103+
104+
/**
105+
* Checks if `goog.callerLocation` is misused. `goog.callerLocation` should only be used as a
106+
* default parameter initializer.
107+
*
108+
* @param n node to check
109+
* @param parent parent node
110+
* @return true if goog.callerLocation is misused, false otherwise
111+
*/
112+
private boolean isGoogCallerLocationMisused(Node n, Node parent) {
113+
if (!GOOG_CALLER_LOCATION_QUALIFIED_NAME.matches(n)) {
114+
// not a `goog.callerLocation` node
115+
return false;
116+
}
117+
118+
// Check for misuse of goog.callerLocation.
119+
if (parent.isCall() && parent.getParent().isDefaultValue()) {
120+
if (parent.getParent().getParent().isStringKey()) {
121+
// Throw an error when `goog.callerLocation` is used in an object literal.
122+
// E.g:
123+
// function foo({val1, val2, here = goog.callerLocation()}) {}
124+
// The AST for `here = goog.callerLocation()`looks like:
125+
// STRING_KEY here (This node tells us we are in an object literal)
126+
// DEFAULT_VALUE
127+
// NAME here 1:26
128+
// CALL 1:33
129+
// GETPROP callerLocation (This is the node `n` we are currently at)
130+
// NAME goog 1:33
131+
return true;
120132
}
133+
134+
// `goog.callerLocation` is used correctly as a default value in a function's parameter
135+
// list.
136+
return false;
137+
}
138+
139+
if (n.getSourceFileName().contains("javascript/closure/base.js")) {
140+
return false;
121141
}
142+
143+
// `goog.callerLocation` is NOT used as a default value in a function's parameter list.
144+
return true;
122145
}
123146

124147
/**

test/com/google/javascript/jscomp/RewriteCallerCodeLocationTest.java

+35-21
Original file line numberDiff line numberDiff line change
@@ -196,45 +196,59 @@ public void testNoCallerLocationDefaultParameter() {
196196
}
197197

198198
@Test
199-
public void testCallerLocationIsSpecified() {
199+
public void testGoogCallerLocationDefinitionInBaseJsDoesNotError() {
200+
String baseJsSource =
201+
lines(
202+
"var goog = goog || {};", //
203+
"goog.callerLocation = function() {};");
204+
SourceFile baseJs = SourceFile.fromCode("javascript/closure/base.js", baseJsSource);
205+
// this code is only allowed in base.js
206+
test(srcs(baseJs), expected(baseJs));
207+
}
208+
209+
@Test
210+
public void testNoRewritingIfCallerLocationIsSpecified() {
200211
enableTypeCheck();
201212
enableParseTypeInfo();
213+
214+
String baseJsSource =
215+
lines(
216+
"var goog = goog || {};", //
217+
"goog.callerLocation = function() {};");
218+
SourceFile baseJs = SourceFile.fromCode("javascript/closure/base.js", baseJsSource);
219+
202220
// no rewriting if CodeLocation is provided as an argument
203-
testSame(
221+
String codeLocationProvided =
204222
lines(
205-
"var goog = goog || {};",
206-
"goog.callerLocation = function() {};",
207223
"function signal(here = goog.callerLocation()) {}",
208-
"const mySignal = signal('path/to/file.ts:25');"));
224+
"const mySignal = signal('path/to/file.ts:25');");
225+
SourceFile testJs = SourceFile.fromCode("codeLocationProvided.js", codeLocationProvided);
226+
testSame(srcs(baseJs, testJs));
209227

210-
testSame(
228+
String codeLocationProvided2 =
211229
lines(
212-
"var goog = goog || {};",
213-
"goog.callerLocation = function() {};",
214230
"function signal(val, here = goog.callerLocation()) {}",
215-
"const mySignal = signal(0, xid('path/to/file.ts:25'));"));
231+
"const mySignal = signal(0, xid('path/to/file.ts:25'));");
232+
SourceFile testJs2 = SourceFile.fromCode("codeLocationProvided2.js", codeLocationProvided2);
233+
testSame(srcs(baseJs, testJs2));
216234

217-
testSame(
235+
String codeLocationProvided3 =
218236
lines(
219-
"var goog = goog || {};",
220-
"goog.callerLocation = function() {};",
221237
"function signal(val, here = goog.callerLocation()) {}",
222238
"const mySignal = signal(",
223239
" () => foo() % 2 === 0,",
224240
" xid('path/to/file.ts:25'),",
225-
");"));
226-
}
241+
");");
242+
SourceFile testJs3 = SourceFile.fromCode("codeLocationProvided3.js", codeLocationProvided3);
243+
testSame(srcs(baseJs, testJs3));
227244

228-
@Test
229-
public void testCallerLocationIsSpecified_withNonStringArgument() {
230-
enableTypeCheck();
231245
// no rewriting if CodeLocation is provided as an argument, regardless of the type of the arg
232-
testSame(
246+
String codeLocationProvided4 =
233247
lines(
234-
"var goog = goog || {};",
235-
"goog.callerLocation = function() {};",
236248
"function signal(here = goog.callerLocation()) {}", //
237-
"const mySignal = signal(foo());"));
249+
"const mySignal = signal(foo());");
250+
SourceFile testJs4 = SourceFile.fromCode("codeLocationProvided4.js", codeLocationProvided4);
251+
testSame(srcs(baseJs, testJs4));
238252
}
239253

240254
@Test

0 commit comments

Comments
 (0)