aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevin Moore <devinmoore@google.com>2023-03-20 22:15:44 +0000
committerDevin Moore <devinmoore@google.com>2023-05-05 16:54:15 +0000
commit9ca4195d0f746ec2bc0be39e692105fefb864819 (patch)
tree659e0614b3f4beb2ea6b15194fb2e070a14a1184
parent2c8aff3dc17f1b5a24ec0603214c808cd71e4aaf (diff)
downloadaidl-9ca4195d0f746ec2bc0be39e692105fefb864819.tar.gz
Add AIDL lint check for redundant prefixes
This checks constants and enumerators for this pattern. It's a common pattern to see: enum MyStatus { STATUS_A, STATUS_B, }; Test: atest --host aidl_unittests Bug: 268551304 Change-Id: I7645fbd27ea06efd2607844ed605cc426b9494a9
-rw-r--r--diagnostics.cpp93
-rw-r--r--diagnostics.inc1
-rw-r--r--diagnostics_unittest.cpp52
3 files changed, 142 insertions, 4 deletions
diff --git a/diagnostics.cpp b/diagnostics.cpp
index d3f04b5b..5112173b 100644
--- a/diagnostics.cpp
+++ b/diagnostics.cpp
@@ -50,6 +50,12 @@ std::string to_string(DiagnosticID id) {
return kDiagnosticsNames.at(id);
}
+static std::string ToUpper(const std::string& name) {
+ std::string nameCopy(name);
+ for (auto& c : nameCopy) c = std::toupper(c);
+ return nameCopy;
+}
+
class DiagnosticsContext {
public:
DiagnosticsContext(DiagnosticMapping mapping) : mapping_({std::move(mapping)}) {}
@@ -160,10 +166,6 @@ struct DiagnoseConstName : DiagnosticsVisitor {
<< "Constants should be named in upper case: " << c.GetName();
}
}
- static std::string ToUpper(std::string name) {
- for (auto& c : name) c = std::toupper(c);
- return name;
- }
};
struct DiagnoseExplicitDefault : DiagnosticsVisitor {
@@ -336,6 +338,88 @@ struct DiagnosePermissionAnnotations : DiagnosticsVisitor {
}
};
+struct DiagnoseRedundantNames : DiagnosticsVisitor {
+ DiagnoseRedundantNames(DiagnosticsContext& diag) : DiagnosticsVisitor(diag) {}
+
+ // tokenize the name with either capital letters or '_' being the delimiters
+ static std::vector<std::string> TokenizeName(const std::string& name) {
+ // if a name is all capitals with no '_', then we don't want to tokenize it.
+ if (std::all_of(name.begin(), name.end(), [](unsigned char c) { return isupper(c); })) {
+ return {name};
+ }
+ // if a name has an `_` in it, it will be tokenized based on '_',
+ // otherwise based on capital letters
+ if (name.find('_') != std::string::npos) {
+ return base::Tokenize(name, "_");
+ }
+
+ std::vector<std::string> tokens;
+ size_t size = name.size();
+ std::string tmp{name.front()};
+ // Skip the first character to avoid an empty substring for common cases
+ for (size_t i = 1; i < size; i++) {
+ if (std::isupper(name[i])) {
+ tokens.push_back(tmp);
+ tmp.clear();
+ // This uppercase letter belongs to the next token
+ tmp += name[i];
+ } else {
+ tmp += name[i];
+ }
+ }
+
+ if (!tmp.empty()) tokens.push_back(tmp);
+
+ return tokens;
+ }
+
+ void Visit(const AidlEnumDeclaration& e) override {
+ const std::vector<std::string> parent = TokenizeName(e.GetName());
+ for (const auto& enumerator : e.GetEnumerators()) {
+ const std::vector<std::string> child = TokenizeName(enumerator->GetName());
+ for (const auto& parentSubStr : parent) {
+ for (const auto& childSubStr : child) {
+ if (ToUpper(parentSubStr) == ToUpper(childSubStr)) {
+ diag.Report(e.GetLocation(), DiagnosticID::redundant_name)
+ << "The enumerator '" << enumerator->GetName() << "' has a redundant substring '"
+ << childSubStr << "' being defined in '" << e.GetName() << "'";
+ }
+ }
+ }
+ }
+ }
+
+ void CheckConstantDeclarations(
+ const std::string& name,
+ const std::vector<std::unique_ptr<AidlConstantDeclaration>>& consts) {
+ const std::vector<std::string> parent = TokenizeName(name);
+ for (const auto& member : consts) {
+ const std::vector<std::string> child = TokenizeName(member->GetName());
+ for (const auto& parentSubStr : parent) {
+ for (const auto& childSubStr : child) {
+ if (ToUpper(parentSubStr) == ToUpper(childSubStr)) {
+ diag.Report(member->GetLocation(), DiagnosticID::redundant_name)
+ << "The constant '" << member->GetName() << "' has a redundant substring '"
+ << childSubStr << "' being defined in '" << name << "'";
+ }
+ }
+ }
+ }
+ }
+
+ void Visit(const AidlInterface& t) override {
+ CheckConstantDeclarations(t.GetName(), t.GetConstantDeclarations());
+ }
+
+ void Visit(const AidlUnionDecl& t) override {
+ CheckConstantDeclarations(t.GetName(), t.GetConstantDeclarations());
+ }
+
+ void Visit(const AidlStructuredParcelable& t) override {
+ CheckConstantDeclarations(t.GetName(), t.GetConstantDeclarations());
+ }
+};
+
bool Diagnose(const AidlDocument& doc, const DiagnosticMapping& mapping) {
DiagnosticsContext diag(mapping);
@@ -350,6 +434,7 @@ bool Diagnose(const AidlDocument& doc, const DiagnosticMapping& mapping) {
DiagnoseImports{diag}.Check(doc);
DiagnoseUntypedCollection{diag}.Check(doc);
DiagnosePermissionAnnotations{diag}.Check(doc);
+ DiagnoseRedundantNames{diag}.Check(doc);
return diag.ErrorCount() == 0;
}
diff --git a/diagnostics.inc b/diagnostics.inc
index 6a0570ae..85b52033 100644
--- a/diagnostics.inc
+++ b/diagnostics.inc
@@ -11,3 +11,4 @@ DIAG(out_nullable, "out-nullable", false)
DIAG(unique_import, "unique-import", false)
DIAG(unknown_warning, "unknown-warning", false)
DIAG(untyped_collection, "untyped-collection", false)
+DIAG(redundant_name, "redundant-name", false)
diff --git a/diagnostics_unittest.cpp b/diagnostics_unittest.cpp
index 3a45d09c..dcbb7011 100644
--- a/diagnostics_unittest.cpp
+++ b/diagnostics_unittest.cpp
@@ -281,3 +281,55 @@ TEST_F(DiagnosticsTest, PermissionInterface) {
expect_diagnostic = {};
ParseFiles({{"IFoo.aidl", "@EnforcePermission(\"INTERNET\") interface IFoo { void food(); }"}});
}
+
+TEST_F(DiagnosticsTest, RedundantPrefixConstantInterface) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles({{"SomethingStatus.aidl", "interface SomethingStatus { const int STATUS_ONE = 1; }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantPrefixConstantParcelable) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles(
+ {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int STATUS_ONE = 1; }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantSuffixConstantParcelable) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles(
+ {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int ONE_STATUS = 1; }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantSuffixConstantParcelable2) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles(
+ {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int ONE_SOMETHING = 1; }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantConstantUnion) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles({{"SomethingStatus.aidl",
+ "union SomethingStatus { const int ONE_SOMETHING = 1; int a; int b;}"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantPrefixEnum) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { STATUS_ONE = 1, }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantSuffixEnum) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { ONE_STATUS = 1, }"}});
+}
+
+TEST_F(DiagnosticsTest, RedundantSuffixEnum2) {
+ enable_diagnostic = DiagnosticID::redundant_name;
+ expect_diagnostic = DiagnosticID::redundant_name;
+ ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { ONE_SOMETHING = 1, }"}});
+}