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/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