rejects circular references

Circular references can't be resolved.

For example,

  enum E { A = B, B }
  parcelable P { const int A = A + 1; }

Bug: 174926968
Bug: 174903521
Test: aidl_unittests
Change-Id: Ief63b3ec2db749d477ad07594cb2797a885ba145
diff --git a/aidl_const_expressions.cpp b/aidl_const_expressions.cpp
index 423bfca..10731ed 100644
--- a/aidl_const_expressions.cpp
+++ b/aidl_const_expressions.cpp
@@ -765,11 +765,17 @@
 
 bool AidlConstantReference::CheckValid() const {
   if (is_evaluated_) return is_valid_;
+  if (is_validating_) {
+    AIDL_ERROR(*this) << "Can't evaluate the circular reference (" << value_ << ")";
+    return false;
+  }
+  is_validating_ = true;
 
   if (!GetRefType() || !GetRefType()->GetDefinedType()) {
     // This can happen when "const reference" is used in an unsupported way,
     // but missed in checks there. It works as a safety net.
     AIDL_ERROR(*this) << "Can't resolve the reference (" << value_ << ")";
+    is_validating_ = false;
     is_valid_ = false;
     return false;
   }
@@ -779,6 +785,7 @@
     for (const auto& e : enum_decl->GetEnumerators()) {
       if (e->GetName() == field_name_) {
         is_valid_ = !e->GetValue() || e->GetValue()->CheckValid();
+        is_validating_ = false;
         return is_valid_;
       }
     }
@@ -786,24 +793,32 @@
     for (const auto& c : defined_type->GetConstantDeclarations()) {
       if (c->GetName() == field_name_) {
         is_valid_ = c->GetValue().CheckValid();
+        is_validating_ = false;
         return is_valid_;
       }
     }
   }
   AIDL_ERROR(*this) << "Can't find " << field_name_ << " in " << ref_type_->GetName();
   is_valid_ = false;
+  is_validating_ = false;
   return false;
 }
 
 bool AidlConstantReference::evaluate(const AidlTypeSpecifier& type) const {
   if (is_evaluated_) return is_valid_;
-  is_evaluated_ = true;
+  if (is_evaluating_) {
+    AIDL_ERROR(*this) << "Can't evaluate the circular reference (" << value_ << ")";
+    return false;
+  }
+  is_evaluating_ = true;
 
   const AidlDefinedType* view_type = type.GetDefinedType();
   if (view_type) {
     auto enum_decl = view_type->AsEnumDeclaration();
     if (!enum_decl) {
       AIDL_ERROR(type) << "Can't refer to a constant expression: " << value_;
+      is_evaluating_ = false;
+      is_evaluated_ = true;
       return false;
     }
   }
@@ -812,7 +827,7 @@
   if (auto enum_decl = defined_type->AsEnumDeclaration(); enum_decl) {
     for (const auto& e : enum_decl->GetEnumerators()) {
       if (e->GetName() == field_name_) {
-        if (e->GetValue()->evaluate(type)) {
+        if (e->GetValue() && e->GetValue()->evaluate(type)) {
           is_valid_ = e->GetValue()->is_valid_;
           if (is_valid_) {
             final_type_ = e->GetValue()->final_type_;
@@ -821,6 +836,8 @@
             } else {
               final_value_ = e->GetValue()->final_value_;
             }
+            is_evaluating_ = false;
+            is_evaluated_ = true;
             return true;
           }
         }
@@ -839,13 +856,16 @@
             } else {
               final_value_ = c->GetValue().final_value_;
             }
+            is_evaluating_ = false;
+            is_evaluated_ = true;
             return true;
           }
         }
       }
     }
   }
-
+  is_evaluating_ = false;
+  is_evaluated_ = true;
   is_valid_ = false;
   return false;
 }
diff --git a/aidl_language.h b/aidl_language.h
index 6638e0e..1df620e 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -601,6 +601,8 @@
   std::unique_ptr<AidlTypeSpecifier> ref_type_;
   std::string field_name_;
   const std::string comments_;
+  mutable bool is_evaluating_ = false;  // to prevent re-entrant CheckValid with circular references
+  mutable bool is_validating_ = false;  // to prevent re-entrant CheckValid with circular references
 };
 
 class AidlUnaryConstExpression : public AidlConstantValue {
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 3f62b15..0c69d39 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -4150,5 +4150,27 @@
                 "std::vector<::aidl::p::Bar> bars = {::aidl::p::Bar::FOO, ::aidl::p::Bar::FOO};"));
 }
 
+TEST_F(AidlTest, RejectsCircularReferencingEnumerators) {
+  io_delegate_.SetFileContents("a/p/Foo.aidl", "package p; enum Foo { A = B, B }");
+  CaptureStderr();
+  auto options = Options::From("aidl -I a --lang ndk -o out -h out a/p/Foo.aidl");
+  EXPECT_EQ(1, aidl::compile_aidl(options, io_delegate_));
+  auto err = GetCapturedStderr();
+  EXPECT_EQ("ERROR: a/p/Foo.aidl:1.26-28: Failed to parse expression as integer: B\n", err);
+}
+
+TEST_F(AidlTest, RejectsCircularReferencingConsts) {
+  io_delegate_.SetFileContents("a/p/Foo.aidl",
+                               "package p; parcelable Foo { const int A = A + 1; }");
+  CaptureStderr();
+  auto options = Options::From("aidl -I a --lang ndk -o out -h out a/p/Foo.aidl");
+  EXPECT_EQ(1, aidl::compile_aidl(options, io_delegate_));
+  auto err = GetCapturedStderr();
+  EXPECT_EQ(
+      "ERROR: a/p/Foo.aidl:1.42-44: Can't evaluate the circular reference (A)\n"
+      "ERROR: a/p/Foo.aidl:1.42-44: Invalid left operand in binary expression: A+1\n",
+      err);
+}
+
 }  // namespace aidl
 }  // namespace android