add -W<warning>/-Werror CLI options

-Weverything: enable all diagnotics
-Wfoo: enable "foo"
-Wno-foo: disable "foo"

-Werror: turn diagnostic warnings into errors
-Wno-error=foo: turn "foo" into a warning

For now, only -Winterface-name is available, which checks if interface
names start with "I".

Bug: 168028537
Test: aidl_unittests
Change-Id: I31516b337dd80b9833205bf769af40e49f88d15d
diff --git a/Android.bp b/Android.bp
index ccf3bf8..79b6cca 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,31 +56,32 @@
     host_supported: true,
 
     srcs: [
-        "aidl.cpp",
         "aidl_checkapi.cpp",
         "aidl_const_expressions.cpp",
-        "aidl_language.cpp",
         "aidl_language_l.ll",
         "aidl_language_y.yy",
-        "aidl_typenames.cpp",
+        "aidl_language.cpp",
+        "aidl_to_cpp_common.cpp",
         "aidl_to_cpp.cpp",
         "aidl_to_java.cpp",
         "aidl_to_ndk.cpp",
         "aidl_to_rust.cpp",
+        "aidl_typenames.cpp",
+        "aidl.cpp",
         "ast_cpp.cpp",
         "ast_java.cpp",
         "code_writer.cpp",
-        "generate_cpp.cpp",
-        "aidl_to_cpp_common.cpp",
-        "generate_ndk.cpp",
-        "generate_java.cpp",
-        "generate_java_binder.cpp",
+        "diagnostics.cpp",
         "generate_aidl_mappings.cpp",
+        "generate_cpp.cpp",
+        "generate_java_binder.cpp",
+        "generate_java.cpp",
+        "generate_ndk.cpp",
         "generate_rust.cpp",
         "import_resolver.cpp",
+        "io_delegate.cpp",
         "line_reader.cpp",
         "logging.cpp",
-        "io_delegate.cpp",
         "options.cpp",
         "parser.cpp",
     ],
diff --git a/aidl.cpp b/aidl.cpp
index 5b1295d..d223ac3 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -557,6 +557,28 @@
   // Validation phase
   //////////////////////////////////////////////////////////////////////////
 
+  class DiagnosticsContextImpl : public DiagnosticsContext {
+   public:
+    DiagnosticsContextImpl(const Options& options) : warning_options(options.GetWarningOptions()) {}
+    AidlErrorLog Report(const AidlLocation& loc, DiagnosticID id) override {
+      switch (warning_options.Severity(id)) {
+        case DiagnosticSeverity::DISABLED:
+          return AidlErrorLog(AidlErrorLog::NO_OP, loc);
+        case DiagnosticSeverity::WARNING:
+          return AidlErrorLog(AidlErrorLog::WARNING, loc);
+        case DiagnosticSeverity::ERROR:
+          error_count_++;
+          return AidlErrorLog(AidlErrorLog::ERROR, loc);
+      }
+    }
+    size_t ErrorCount() const { return error_count_; }
+
+   private:
+    const WarningOptions& warning_options;
+    size_t error_count_ = 0;
+  };
+  DiagnosticsContextImpl diag(options);
+
   // For legacy reasons, by default, compiling an unstructured parcelable (which contains no output)
   // is allowed. This must not be returned as an error until the very end of this procedure since
   // this may be considered a success, and we should first check that there are not other, more
@@ -588,7 +610,7 @@
 
       if (!is_check_api) {
         // Ideally, we could do this for check api, but we can't resolve imports
-        if (!defined_type->CheckValid(*typenames)) {
+        if (!defined_type->CheckValid(*typenames, diag)) {
           valid_type = false;
         }
       }
@@ -680,6 +702,10 @@
     }
   }
 
+  if (diag.ErrorCount() > 0) {
+    return AidlError::BAD_TYPE;
+  }
+
   typenames->IterateTypes([&](const AidlDefinedType& type) {
     if (options.IsStructured() && type.AsUnstructuredParcelable() != nullptr &&
         !type.AsUnstructuredParcelable()->IsStableApiParcelable(options.TargetLanguage())) {
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 92f9d9b..52aa085 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -44,6 +44,7 @@
 }
 #endif
 
+using android::aidl::DiagnosticID;
 using android::aidl::IoDelegate;
 using android::base::Join;
 using android::base::Split;
@@ -863,7 +864,7 @@
   }
 }
 
-bool AidlDefinedType::CheckValid(const AidlTypenames& typenames) const {
+bool AidlDefinedType::CheckValid(const AidlTypenames& typenames, DiagnosticsContext&) const {
   if (!AidlAnnotatable::CheckValid(typenames)) {
     return false;
   }
@@ -991,8 +992,8 @@
           AidlAnnotation::Type::JAVA_PASSTHROUGH,       AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE};
 }
 
-bool AidlParcelable::CheckValid(const AidlTypenames& typenames) const {
-  if (!AidlDefinedType::CheckValid(typenames)) {
+bool AidlParcelable::CheckValid(const AidlTypenames& typenames, DiagnosticsContext& diag) const {
+  if (!AidlDefinedType::CheckValid(typenames, diag)) {
     return false;
   }
   if (!AidlParameterizable<std::string>::CheckValid()) {
@@ -1044,8 +1045,9 @@
           AidlAnnotation::Type::RUST_DERIVE};
 }
 
-bool AidlStructuredParcelable::CheckValid(const AidlTypenames& typenames) const {
-  if (!AidlParcelable::CheckValid(typenames)) {
+bool AidlStructuredParcelable::CheckValid(const AidlTypenames& typenames,
+                                          DiagnosticsContext& diag) const {
+  if (!AidlParcelable::CheckValid(typenames, diag)) {
     return false;
   }
 
@@ -1240,8 +1242,9 @@
           AidlAnnotation::Type::HIDE, AidlAnnotation::Type::JAVA_PASSTHROUGH};
 }
 
-bool AidlEnumDeclaration::CheckValid(const AidlTypenames& typenames) const {
-  if (!AidlDefinedType::CheckValid(typenames)) {
+bool AidlEnumDeclaration::CheckValid(const AidlTypenames& typenames,
+                                     DiagnosticsContext& diag) const {
+  if (!AidlDefinedType::CheckValid(typenames, diag)) {
     return false;
   }
   if (!GetMembers().empty()) {
@@ -1303,9 +1306,9 @@
   writer->Write("}\n");
 }
 
-bool AidlUnionDecl::CheckValid(const AidlTypenames& typenames) const {
+bool AidlUnionDecl::CheckValid(const AidlTypenames& typenames, DiagnosticsContext& diag) const {
   // visit parents
-  if (!AidlParcelable::CheckValid(typenames)) {
+  if (!AidlParcelable::CheckValid(typenames, diag)) {
     return false;
   }
 
@@ -1421,8 +1424,8 @@
           AidlAnnotation::Type::JAVA_PASSTHROUGH,      AidlAnnotation::Type::DESCRIPTOR};
 }
 
-bool AidlInterface::CheckValid(const AidlTypenames& typenames) const {
-  if (!AidlDefinedType::CheckValid(typenames)) {
+bool AidlInterface::CheckValid(const AidlTypenames& typenames, DiagnosticsContext& diag) const {
+  if (!AidlDefinedType::CheckValid(typenames, diag)) {
     return false;
   }
   // Has to be a pointer due to deleting copy constructor. No idea why.
@@ -1522,6 +1525,11 @@
     success = success && constant->CheckValid(typenames);
   }
 
+  if (auto name = GetName(); name.size() < 1 || name[0] != 'I') {
+    diag.Report(GetLocation(), DiagnosticID::interface_name)
+        << "Interface names should start with I.";
+  }
+
   return success;
 }
 
diff --git a/aidl_language.h b/aidl_language.h
index 4ef6973..d8bb9d8 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -16,11 +16,6 @@
 
 #pragma once
 
-#include "aidl_typenames.h"
-#include "code_writer.h"
-#include "io_delegate.h"
-#include "options.h"
-
 #include <memory>
 #include <regex>
 #include <string>
@@ -29,8 +24,15 @@
 
 #include <android-base/strings.h>
 
+#include "aidl_typenames.h"
+#include "code_writer.h"
+#include "diagnostics.h"
+#include "io_delegate.h"
+#include "options.h"
+
 using android::aidl::AidlTypenames;
 using android::aidl::CodeWriter;
+using android::aidl::DiagnosticsContext;
 using android::aidl::Options;
 using std::shared_ptr;
 using std::string;
@@ -114,14 +116,6 @@
   const AidlLocation location_;
 };
 
-namespace android {
-namespace aidl {
-
-class AidlTypenames;
-
-}  // namespace aidl
-}  // namespace android
-
 // unique_ptr<AidlTypeSpecifier> for type arugment,
 // std::string for type parameter(T, U, and so on).
 template <typename T>
@@ -268,7 +262,7 @@
   std::string ToString() const;
 
   const vector<AidlAnnotation>& GetAnnotations() const { return annotations_; }
-  virtual bool CheckValid(const AidlTypenames&) const;
+  bool CheckValid(const AidlTypenames&) const;
 
  protected:
   virtual std::set<AidlAnnotation::Type> GetSupportedAnnotations() const = 0;
@@ -339,7 +333,7 @@
   bool Resolve(const AidlTypenames& typenames);
 
   std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames) const;
   bool LanguageSpecificCheckValid(const AidlTypenames& typenames, Options::Language lang) const;
   const AidlNode& AsAidlNode() const override { return *this; }
 
@@ -811,7 +805,7 @@
   virtual const AidlUnionDecl* AsUnionDeclaration() const { return nullptr; }
   virtual const AidlInterface* AsInterface() const { return nullptr; }
   virtual const AidlParameterizable<std::string>* AsParameterizable() const { return nullptr; }
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  virtual bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const;
   virtual bool LanguageSpecificCheckValid(const AidlTypenames& typenames,
                                           Options::Language lang) const = 0;
   AidlStructuredParcelable* AsStructuredParcelable() {
@@ -895,7 +889,7 @@
   std::string GetCppHeader() const { return cpp_header_; }
 
   std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const override;
   bool LanguageSpecificCheckValid(const AidlTypenames& typenames,
                                   Options::Language lang) const override;
   const AidlParcelable* AsParcelable() const override { return this; }
@@ -929,7 +923,7 @@
   void Dump(CodeWriter* writer) const override;
 
   std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const override;
   bool LanguageSpecificCheckValid(const AidlTypenames& typenames,
                                   Options::Language lang) const override;
 };
@@ -983,7 +977,7 @@
     return enumerators_;
   }
   std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const override;
   bool LanguageSpecificCheckValid(const AidlTypenames& /*typenames*/,
                                   Options::Language) const override {
     return true;
@@ -1018,7 +1012,7 @@
 
   const AidlNode& AsAidlNode() const override { return *this; }
 
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const override;
   bool LanguageSpecificCheckValid(const AidlTypenames& typenames,
                                   Options::Language lang) const override;
   std::string GetPreprocessDeclarationName() const override { return "union"; }
@@ -1046,7 +1040,7 @@
   void Dump(CodeWriter* writer) const override;
 
   std::set<AidlAnnotation::Type> GetSupportedAnnotations() const override;
-  bool CheckValid(const AidlTypenames& typenames) const override;
+  bool CheckValid(const AidlTypenames& typenames, DiagnosticsContext& context) const override;
   bool LanguageSpecificCheckValid(const AidlTypenames& typenames,
                                   Options::Language lang) const override;
 
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 30b6abc..b829f6b 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -4312,6 +4312,26 @@
   EXPECT_NE(nullptr, Parse("IFoo.aidl", contents, typenames_, GetLanguage()));
 }
 
+TEST_P(AidlTest, WarningInterfaceName) {
+  io_delegate_.SetFileContents("p/Foo.aidl", "interface Foo {}");
+  auto options = Options::From("aidl --lang " + Options::LanguageToString(GetLanguage()) +
+                               " -Weverything -o out -h out p/Foo.aidl");
+  CaptureStderr();
+  EXPECT_EQ(0, aidl::compile_aidl(options, io_delegate_));
+  EXPECT_EQ("WARNING: p/Foo.aidl:1.1-10: Interface names should start with I.\n",
+            GetCapturedStderr());
+}
+
+TEST_P(AidlTest, ErrorInterfaceName) {
+  io_delegate_.SetFileContents("p/Foo.aidl", "interface Foo {}");
+  auto options = Options::From("aidl --lang " + Options::LanguageToString(GetLanguage()) +
+                               " -Weverything -Werror -o out -h out p/Foo.aidl");
+  CaptureStderr();
+  EXPECT_EQ(1, aidl::compile_aidl(options, io_delegate_));
+  EXPECT_EQ("ERROR: p/Foo.aidl:1.1-10: Interface names should start with I.\n",
+            GetCapturedStderr());
+}
+
 struct TypeParam {
   string kind;
   string literal;
diff --git a/diagnostics.cpp b/diagnostics.cpp
new file mode 100644
index 0000000..b205296
--- /dev/null
+++ b/diagnostics.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "diagnostics.h"
+
+namespace android {
+namespace aidl {
+
+const std::map<std::string, DiagnosticOption> kAllDiagnostics = {
+#define DIAG(ENUM, NAME, ENABLED) {NAME, DiagnosticOption{DiagnosticID::ENUM, NAME, ENABLED}},
+#include "diagnostics.inc"
+#undef DIAG
+};
+
+}  // namespace aidl
+}  // namespace android
\ No newline at end of file
diff --git a/diagnostics.h b/diagnostics.h
new file mode 100644
index 0000000..cb63642
--- /dev/null
+++ b/diagnostics.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+class AidlLocation;
+class AidlErrorLog;
+
+namespace android {
+namespace aidl {
+
+enum class DiagnosticSeverity {
+  DISABLED,
+  WARNING,
+  ERROR,
+};
+
+enum class DiagnosticID {
+#define DIAG(ENUM, NAME, ENABLED) ENUM,
+
+#include "diagnostics.inc"
+#undef DIAG
+};
+
+class DiagnosticsContext {
+ public:
+  virtual ~DiagnosticsContext() {}
+  // Returns true if it's okay to proceed after reporting diagnostics.
+  virtual AidlErrorLog Report(const AidlLocation& loc, DiagnosticID id) = 0;
+};
+
+struct DiagnosticOption {
+  DiagnosticID id;
+  const std::string name;
+  bool default_enabled;
+};
+
+extern const std::map<std::string, DiagnosticOption> kAllDiagnostics;
+
+}  // namespace aidl
+}  // namespace android
\ No newline at end of file
diff --git a/diagnostics.inc b/diagnostics.inc
new file mode 100644
index 0000000..ddb0201
--- /dev/null
+++ b/diagnostics.inc
@@ -0,0 +1,2 @@
+// DIAG(enum, name, enable-by-default)
+DIAG(interface_name, "interface-name", false)
\ No newline at end of file
diff --git a/import_resolver.cpp b/import_resolver.cpp
index edde65b..1bf2794 100644
--- a/import_resolver.cpp
+++ b/import_resolver.cpp
@@ -79,8 +79,7 @@
   } else if (num_found == 1) {
     return found_paths.front();
   } else {
-    AIDL_ERROR(input_file_name_) << "Duplicate files found for " << canonical_name
-                                 << " from:" << std::endl
+    AIDL_ERROR(input_file_name_) << "Duplicate files found for " << canonical_name << " from:\n"
                                  << android::base::Join(found_paths, "\n");
     return "";
   }
diff --git a/logging.h b/logging.h
index f6d6da1..5f7685b 100644
--- a/logging.h
+++ b/logging.h
@@ -23,43 +23,56 @@
 // Generic point for printing any error in the AIDL compiler.
 class AidlErrorLog {
  public:
-  AidlErrorLog(bool fatal, const AidlLocation& location)
-      : os_(std::cerr), fatal_(fatal), location_(location) {
-    sHadError = true;
+  enum Severity { NO_OP, WARNING, ERROR, FATAL };
 
-    os_ << "ERROR: ";
-    os_ << location << ": ";
+  AidlErrorLog(Severity severity, const AidlLocation& location)
+      : os_(&std::cerr), severity_(severity), location_(location) {
+    sHadError |= severity_ >= ERROR;
+    if (severity_ != NO_OP) {
+      (*os_) << (severity_ == WARNING ? "WARNING: " : "ERROR: ");
+      (*os_) << location << ": ";
+    }
   }
-  AidlErrorLog(bool fatal, const std::string& filename)
-      : AidlErrorLog(fatal, AidlLocation(filename, AidlLocation::Source::EXTERNAL)) {}
-  AidlErrorLog(bool fatal, const AidlNode& node) : AidlErrorLog(fatal, node.location_) {}
-  AidlErrorLog(bool fatal, const AidlNode* node) : AidlErrorLog(fatal, *node) {}
+  AidlErrorLog(Severity severity, const std::string& filename)
+      : AidlErrorLog(severity, AidlLocation(filename, AidlLocation::Source::EXTERNAL)) {}
+  AidlErrorLog(Severity severity, const AidlNode& node) : AidlErrorLog(severity, node.location_) {}
+  AidlErrorLog(Severity severity, const AidlNode* node) : AidlErrorLog(severity, *node) {}
 
   template <typename T>
-  AidlErrorLog(bool fatal, const std::unique_ptr<T>& node) : AidlErrorLog(fatal, *node) {}
+  AidlErrorLog(Severity severity, const std::unique_ptr<T>& node) : AidlErrorLog(severity, *node) {}
   ~AidlErrorLog() {
-    os_ << std::endl;
-    if (fatal_) abort();
+    if (severity_ == NO_OP) return;
+    (*os_) << std::endl;
+    if (severity_ == FATAL) abort();
     if (location_.IsInternal()) {
-      os_ << "Logging an internal location should not happen. Offending location: " << location_
-          << std::endl;
+      (*os_) << "Logging an internal location should not happen. Offending location: " << location_
+             << std::endl;
       abort();
     }
   }
 
-  // AidlErrorLog is a single use object. No need to copy or move
+  // AidlErrorLog is a single use object. No need to copy
   AidlErrorLog(const AidlErrorLog&) = delete;
-  AidlErrorLog(AidlErrorLog&&) = delete;
   AidlErrorLog& operator=(const AidlErrorLog&) = delete;
-  AidlErrorLog& operator=(AidlErrorLog&&) = delete;
 
-  std::ostream& os_;
+  // btw, making it movable so that functions can return it.
+  AidlErrorLog(AidlErrorLog&&) = default;
+  AidlErrorLog& operator=(AidlErrorLog&&) = default;
+
+  template <typename T>
+  AidlErrorLog& operator<<(T&& arg) {
+    if (severity_ != NO_OP) {
+      (*os_) << std::forward<T>(arg);
+    }
+    return *this;
+  }
 
   static void clearError() { sHadError = false; }
   static bool hadError() { return sHadError; }
 
  private:
-  bool fatal_;
+  std::ostream* os_;
+  Severity severity_;
   const AidlLocation location_;
   static bool sHadError;
 };
@@ -72,8 +85,8 @@
   __attribute__((noreturn)) ~AidlAbortOnDestruction() { abort(); }
 };
 
-#define AIDL_ERROR(CONTEXT) ::AidlErrorLog(false /*fatal*/, (CONTEXT)).os_
+#define AIDL_ERROR(CONTEXT) ::AidlErrorLog(AidlErrorLog::ERROR, (CONTEXT))
 #define AIDL_FATAL(CONTEXT) \
-  (::AidlAbortOnDestruction(), ::AidlErrorLog(true /*fatal*/, (CONTEXT)).os_)
+  (::AidlAbortOnDestruction(), ::AidlErrorLog(AidlErrorLog::FATAL, (CONTEXT)))
 #define AIDL_FATAL_IF(CONDITION, CONTEXT) \
   if (CONDITION) AIDL_FATAL(CONTEXT) << "Bad internal state: " << #CONDITION << ": "
diff --git a/options.cpp b/options.cpp
index 601a06b..8ffdb04 100644
--- a/options.cpp
+++ b/options.cpp
@@ -124,6 +124,19 @@
        << "  --log" << endl
        << "          Information about the transaction, e.g., method name, argument" << endl
        << "          values, execution time, etc., is provided via callback." << endl
+       << "  -Werror" << endl
+       << "          Turn warnings into errors." << endl
+       << "  -Wno-error=<warning>" << endl
+       << "          Turn the specified warning into a warning even if -Werror is specified."
+       << endl
+       << "  -W<warning>" << endl
+       << "          Enable the specified warning." << endl
+       << "  -Wno-<warning>" << endl
+       << "          Disable the specified warning." << endl
+       << "  -w" << endl
+       << "          Disable all diagnostics. -w wins -Weverything" << endl
+       << "  -Weverything" << endl
+       << "          Enable all diagnostics." << endl
        << "  --help" << endl
        << "          Show this help." << endl
        << endl
@@ -188,8 +201,12 @@
   return Options(argc, argv, lang);
 }
 
-Options::Options(int argc, const char* const argv[], Options::Language default_lang)
-    : myname_(argv[0]), language_(default_lang) {
+Options::Options(int argc, const char* const raw_argv[], Options::Language default_lang)
+    : myname_(raw_argv[0]), language_(default_lang) {
+  std::vector<const char*> argv = warning_options_.Parse(argc, raw_argv, error_message_);
+  if (!Ok()) return;
+  argc = argv.size();
+
   bool lang_option_found = false;
   optind = 0;
   while (true) {
@@ -218,7 +235,7 @@
         {"help", no_argument, 0, 'e'},
         {0, 0, 0, 0},
     };
-    const int c = getopt_long(argc, const_cast<char* const*>(argv),
+    const int c = getopt_long(argc, const_cast<char* const*>(argv.data()),
                               "I:m:p:d:o:h:abtv:", long_options, nullptr);
     if (c == -1) {
       // no more options
@@ -505,5 +522,62 @@
                 output_header_dir_);
 }
 
+std::vector<const char*> WarningOptions::Parse(int argc, const char* const raw_argv[],
+                                               ErrorMessage& error_message) {
+  std::vector<const char*> remains;
+  for (int i = 0; i < argc; i++) {
+    auto arg = raw_argv[i];
+    if (strcmp(arg, "-Weverything") == 0) {
+      enable_all_ = true;
+    } else if (strcmp(arg, "-Werror") == 0) {
+      as_errors_ = true;
+    } else if (strcmp(arg, "-w") == 0) {
+      disable_all_ = true;
+    } else if (base::StartsWith(arg, "-Wno-error=")) {
+      no_errors_.insert(arg + strlen("-Wno-error="));
+    } else if (base::StartsWith(arg, "-Wno-")) {
+      disabled_.insert(arg + strlen("-Wno-"));
+    } else if (base::StartsWith(arg, "-W")) {
+      enabled_.insert(arg + strlen("-W"));
+    } else {
+      remains.push_back(arg);
+    }
+  }
+
+  for (const auto& names : {no_errors_, disabled_, enabled_}) {
+    for (const auto& name : names) {
+      if (kAllDiagnostics.count(name) == 0) {
+        error_message << "unknown warning: " << name << "\n";
+        return {};
+      }
+    }
+  }
+
+  for (const auto& [_, d] : kAllDiagnostics) {
+    bool enabled = d.default_enabled;
+    if (enable_all_ || enabled_.find(d.name) != enabled_.end()) {
+      enabled = true;
+    }
+    if (disable_all_ || disabled_.find(d.name) != disabled_.end()) {
+      enabled = false;
+    }
+
+    DiagnosticSeverity severity = DiagnosticSeverity::DISABLED;
+    if (enabled) {
+      severity = DiagnosticSeverity::WARNING;
+      if (as_errors_ && no_errors_.find(d.name) == no_errors_.end()) {
+        severity = DiagnosticSeverity::ERROR;
+      }
+    }
+    mapping_.emplace(d.id, Mapping{d.name, severity});
+  }
+
+  return remains;
+}
+
+DiagnosticSeverity WarningOptions::Severity(DiagnosticID id) const {
+  return mapping_.at(id).severity;
+}
+
 }  // namespace aidl
 }  // namespace android
diff --git a/options.h b/options.h
index 554d1cd..08f0fdf 100644
--- a/options.h
+++ b/options.h
@@ -15,11 +15,14 @@
 
 #pragma once
 
+#include <map>
 #include <set>
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "diagnostics.h"
+
 namespace android {
 namespace aidl {
 
@@ -57,6 +60,26 @@
   }
 };
 
+class WarningOptions {
+ public:
+  std::vector<const char*> Parse(int argc, const char* const argv[], ErrorMessage& error_message);
+  DiagnosticSeverity Severity(DiagnosticID id) const;
+
+ private:
+  bool as_errors_ = false;           // -Werror
+  bool enable_all_ = false;          // -Weverything
+  bool disable_all_ = false;         // -w
+  std::set<std::string> enabled_;    // -Wfoo
+  std::set<std::string> disabled_;   // -Wno-foo
+  std::set<std::string> no_errors_;  // -Wno-error=foo
+
+  struct Mapping {
+    std::string name;
+    DiagnosticSeverity severity;
+  };
+  std::map<DiagnosticID, Mapping> mapping_;
+};
+
 class Options final {
  public:
   enum class Language { UNSPECIFIED, JAVA, CPP, NDK, RUST };
@@ -135,6 +158,8 @@
 
   static const string LanguageToString(Language language);
 
+  const WarningOptions& GetWarningOptions() const { return warning_options_; }
+
   // The following are for testability, but cannot be influenced on the command line.
   // Threshold of interface methods to enable outlining of onTransact cases.
   size_t onTransact_outline_threshold_{275u};
@@ -166,6 +191,7 @@
   string hash_ = "";
   bool gen_log_ = false;
   ErrorMessage error_message_;
+  WarningOptions warning_options_;
 };
 
 }  // namespace aidl
diff --git a/options_unittest.cpp b/options_unittest.cpp
index 377861b..83ab32a 100644
--- a/options_unittest.cpp
+++ b/options_unittest.cpp
@@ -24,6 +24,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "diagnostics.h"
+
+using android::aidl::DiagnosticID;
+using android::aidl::DiagnosticSeverity;
 using std::cerr;
 using std::endl;
 using std::string;
@@ -344,5 +348,56 @@
   EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
 }
 
+TEST(OptionsTests, ParsesWarningEnableAll) {
+  const char* args[] = {
+      "aidl", "--lang=java", "-Weverything", "--out=out", "input.aidl", nullptr,
+  };
+  auto options = GetOptions(args);
+  EXPECT_TRUE(options->Ok());
+  auto warning_options = options->GetWarningOptions();
+  EXPECT_EQ(DiagnosticSeverity::WARNING, warning_options.Severity(DiagnosticID::interface_name));
+}
+
+TEST(OptionsTests, ParsesWarningEnableSpecificWarning) {
+  const char* args[] = {
+      "aidl", "--lang=java", "-Winterface-name", "--out=out", "input.aidl", nullptr,
+  };
+  auto options = GetOptions(args);
+  EXPECT_TRUE(options->Ok());
+  auto warning_options = options->GetWarningOptions();
+  EXPECT_EQ(DiagnosticSeverity::WARNING, warning_options.Severity(DiagnosticID::interface_name));
+}
+
+TEST(OptionsTests, ParsesWarningDisableSpecificWarning) {
+  const char* args[] = {
+      "aidl",      "--lang=java", "-Weverything", "-Wno-interface-name",
+      "--out=out", "input.aidl",  nullptr,
+  };
+  auto options = GetOptions(args);
+  EXPECT_TRUE(options->Ok());
+  auto warning_options = options->GetWarningOptions();
+  EXPECT_EQ(DiagnosticSeverity::DISABLED, warning_options.Severity(DiagnosticID::interface_name));
+}
+
+TEST(OptionsTests, ParsesWarningAsErrors) {
+  const char* args[] = {
+      "aidl", "--lang=java", "-Werror", "-Weverything", "--out=out", "input.aidl", nullptr,
+  };
+  auto options = GetOptions(args);
+  EXPECT_TRUE(options->Ok());
+  auto warning_options = options->GetWarningOptions();
+  EXPECT_EQ(DiagnosticSeverity::ERROR, warning_options.Severity(DiagnosticID::interface_name));
+}
+
+TEST(OptionsTests, RejectsUnknownWarning) {
+  const char* args[] = {
+      "aidl", "--lang=java", "-Wfoobar", "--out=out", "input.aidl", nullptr,
+  };
+  CaptureStderr();
+  auto options = GetOptions(args);
+  EXPECT_FALSE(options->Ok());
+  EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr("unknown warning: foobar"));
+}
+
 }  // namespace aidl
 }  // namespace android