Add typo detection when lookups on SkylarkModules fail.
Also consolidate code with getattr so getattr now also gets typo detection.
PiperOrigin-RevId: 197612666
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
index 4a1507b..ed45d60 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
@@ -63,19 +63,54 @@
if (result != null) {
return result;
}
+ throw getMissingFieldException(objValue, name, loc, "field");
+ }
+
+ static EvalException getMissingFieldException(
+ Object objValue, String name, Location loc, String accessName) {
String suffix = "";
+ EvalException toSuppress = null;
if (objValue instanceof ClassObject) {
String customErrorMessage = ((ClassObject) objValue).getErrorMessageForUnknownField(name);
if (customErrorMessage != null) {
- throw new EvalException(loc, customErrorMessage);
+ return new EvalException(loc, customErrorMessage);
}
- suffix = SpellChecker.didYouMean(name, ((ClassObject) objValue).getFieldNames());
+ try {
+ suffix = SpellChecker.didYouMean(name, ((ClassObject) objValue).getFieldNames());
+ } catch (EvalException ee) {
+ toSuppress = ee;
+ }
+ } else {
+ suffix =
+ SpellChecker.didYouMean(
+ name,
+ FuncallExpression.getStructFieldNames(
+ objValue instanceof Class ? (Class<?>) objValue : objValue.getClass()));
}
- throw new EvalException(
- loc,
- String.format(
- "object of type '%s' has no field '%s'%s",
- EvalUtils.getDataTypeName(objValue), name, suffix));
+ if (suffix.isEmpty() && hasMethod(objValue, name)) {
+ // If looking up the field failed, then we know that this method must have struct_field=false
+ suffix = ", however, a method of that name exists";
+ }
+ EvalException ee =
+ new EvalException(
+ loc,
+ String.format(
+ "object of type '%s' has no %s '%s'%s",
+ EvalUtils.getDataTypeName(objValue), accessName, name, suffix));
+ if (toSuppress != null) {
+ ee.addSuppressed(toSuppress);
+ }
+ return ee;
+ }
+
+ /** Returns whether the given object has a method with the given name. */
+ static boolean hasMethod(Object obj, String name) {
+ Class<?> cls = obj instanceof Class ? (Class<?>) obj : obj.getClass();
+ if (Runtime.getBuiltinRegistry().getFunctionNames(cls).contains(name)) {
+ return true;
+ }
+
+ return FuncallExpression.getMethodNames(cls).contains(name);
}
/**