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