FieldScopeImpl impl  Context context
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Nullable Boolean match = matchesFieldPath.get(impl);
      if (match == null) {
        match = impl.doMatchesFieldPath(context, this);
        matchesFieldPath.put(impl, match);
boolean matchesFieldPath(FieldScopeImpl impl, Context context)
FieldScopeImpl impl  Context context
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Nullable Boolean matchAppliesForAll = matchStateAppliesForAllSubPaths.get(impl);
      if (matchAppliesForAll == null) {
        matchAppliesForAll = impl.doMatchStateAppliesForAllSubPaths(context, this);
        matchStateAppliesForAllSubPaths.put(impl, matchAppliesForAll);
boolean matchStateAppliesForAllSubPaths(FieldScopeImpl impl, Context context)
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
matchesFieldPath.clear();
      matchStateAppliesForAllSubPaths.clear();
void clearMethodCaches()
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return cache.matchesFieldPath(this, context);
boolean matchesFieldPath(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return cache.matchStateAppliesForAllSubPaths(this, context);
boolean matchStateAppliesForAllSubPaths(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return true;
  // Make subclasses provide a decent toString()
  @Override
  public abstract String toString();

        boolean doMatchesFieldPath(Context context, Cache cache) {

        @Override
        public String toString() {
          return "FieldScopes.all()";
boolean doMatchStateAppliesForAllSubPaths(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Override
        public String toString() {
          return "FieldScopes.none()";
boolean doMatchesFieldPath(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Override
    public String toString() {
      return String.format("FieldScopes.fromSetFields(%s)", message);
boolean doMatchesFieldPath(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
final boolean doMatchStateAppliesForAllSubPaths(Context context, Cache cache) {
      // Match is fixed if we match currently, or no sub paths can match.
      if (!matchesFieldPath(context, cache)) {
        for (Message message : context.messageFields()) {
          if (messageHasMatchingField(context, cache, message)) {
            return false;
boolean doMatchesFieldPath(Context context, Cache cache)
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return String.format("FieldScopes.allowingFields(%s)", Joiner.on(", ").join(fieldNumbers));
String toString()
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return String.format(
          "FieldScopes.allowingFieldDescriptors(%s)", Joiner.on(", ").join(fieldDescriptors));
String toString()
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
if (matchesFieldPath(context, cache)) {
        // We are fixed as true only if both operands are fixed.
        return elements.get(0).matchStateAppliesForAllSubPaths(context, cache)
            && elements.get(1).matchStateAppliesForAllSubPaths(context, cache);
boolean doMatchStateAppliesForAllSubPaths(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Override
    public String toString() {
      return String.format("(%s && %s)", elements.get(0), elements.get(1));
boolean doMatchesFieldPath(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
if (matchesFieldPath(context, cache)) {
        // We are fixed as true only if either field is fixed true.
        boolean firstFixedTrue =
            elements.get(0).matchesFieldPath(context, cache)
                && elements.get(0).matchStateAppliesForAllSubPaths(context, cache);
        boolean secondFixedTrue =
            elements.get(1).matchesFieldPath(context, cache)
                && elements.get(1).matchStateAppliesForAllSubPaths(context, cache);
        return firstFixedTrue || secondFixedTrue;
boolean doMatchStateAppliesForAllSubPaths(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Override
    public String toString() {
      return String.format("(%s || %s)", elements.get(0), elements.get(1));
boolean doMatchesFieldPath(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
// We are fixed only if the operand is fixed.
      return elements.get(0).matchStateAppliesForAllSubPaths(context, cache);
boolean doMatchStateAppliesForAllSubPaths(Context context, Cache cache)
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
@Override
    public String toString() {
      return String.format("!(%s)", elements.get(0));
boolean doMatchesFieldPath(Context context, Cache cache)
extensions/proto/src/test/java/com/google/common/truth/extensions/proto/FieldScopesTest.java
// Ignore all 'o_int' fields, in different ways.
    Message message =
        parse(
            "o_int: 1 r_string: \"foo\" o_sub_test_message: { o_int: 2 "
                + "o_sub_sub_test_message: { o_int: 3 r_string: \"bar\"
void testIgnoreFieldsAtDifferentLevels()
Context context  Cache cache
extensions/proto/src/main/java/com/google/common/truth/extensions/proto/FieldScopeImpl.java
return false;
        boolean matchesFieldPath(Context context, Cache cache) {
        boolean matchesFieldPath(Context context, Cache cache) {
    boolean matchesFieldPath(Context context, Cache cache) {
    boolean matchesFieldPath(Context context, Cache cache) {
    boolean matchesContentOfSubMessages(Context context, Cache cache) {
      for (Message message : context.messageFields()) {
        if (messageHasMatchingField(context, cache, message)) {
          return true;
      return false;
    @Override
    final boolean matchesContentOfSubMessages(Context context, Cache cache) {
      // We ignore compound logic for checking sub-messages, since we want to ensure we inspect
      // any sub-message which could match a FieldScopeImpl at a deeper path.
      for (FieldScopeImpl impl : elements) {
        if (impl.matchesContentOfSubMessages(context, cache)) {
          return true;
boolean matchesContentOfSubMessages(Context context, Cache cache)