Skip to content

Commit

Permalink
Port antipattern/duplicate_required_values from JSON BinPack (#201)
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
  • Loading branch information
jviotti committed Sep 9, 2024
1 parent 3105671 commit ff818e2
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/linter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ noa_library(NAMESPACE sourcemeta PROJECT alterschema NAME linter
# Antipattern
antipattern/const_with_type.h
antipattern/duplicate_enum_values.h
antipattern/duplicate_required_values.h
antipattern/enum_with_type.h

# Simplify
Expand Down
31 changes: 31 additions & 0 deletions src/linter/antipattern/duplicate_required_values.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class DuplicateRequiredValues final : public Rule {
public:
DuplicateRequiredValues()
: Rule("duplicate_required_values",
"Setting duplicate values in `required` is considered an "
"anti-pattern") {};

[[nodiscard]] auto
condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &,
const std::set<std::string> &vocabularies,
const sourcemeta::jsontoolkit::Pointer &) const -> bool override {
return contains_any(
vocabularies,
{"https://json-schema.org/draft/2020-12/vocab/validation",
"https://json-schema.org/draft/2019-09/vocab/validation",
"http://json-schema.org/draft-07/schema#",
"http://json-schema.org/draft-06/schema#",
"http://json-schema.org/draft-04/schema#"}) &&
schema.is_object() && schema.defines("required") &&
schema.at("required").is_array() && !schema.at("required").unique();
}

auto transform(Transformer &transformer) const -> void override {
auto collection = transformer.schema().at("required");
std::sort(collection.as_array().begin(), collection.as_array().end());
auto last =
std::unique(collection.as_array().begin(), collection.as_array().end());
collection.erase(last, collection.as_array().end());
transformer.replace({"required"}, std::move(collection));
}
};
2 changes: 2 additions & 0 deletions src/linter/linter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ auto contains_any(const T &container, const T &values) -> bool {
// AntiPattern
#include "antipattern/const_with_type.h"
#include "antipattern/duplicate_enum_values.h"
#include "antipattern/duplicate_required_values.h"
#include "antipattern/enum_with_type.h"
// Simplify
#include "simplify/single_type_array.h"
Expand Down Expand Up @@ -46,6 +47,7 @@ auto add(Bundle &bundle, const LinterCategory category) -> void {
case LinterCategory::AntiPattern:
bundle.add<EnumWithType>();
bundle.add<DuplicateEnumValues>();
bundle.add<DuplicateRequiredValues>();
bundle.add<ConstWithType>();
break;
case LinterCategory::Simplify:
Expand Down
18 changes: 18 additions & 0 deletions test/linter/2019_09_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,21 @@ TEST(Lint_2019_09, duplicate_enum_values_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_2019_09, duplicate_required_values_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"required": [ "foo", "bar", "baz", "foo" ]
})JSON");

LINT_AND_FIX(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"required": [ "bar", "baz", "foo" ]
})JSON");

EXPECT_EQ(document, expected);
}
18 changes: 18 additions & 0 deletions test/linter/2020_12_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -551,3 +551,21 @@ TEST(Lint_2020_12, duplicate_enum_values_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_2020_12, duplicate_required_values_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [ "foo", "bar", "baz", "foo" ]
})JSON");

LINT_AND_FIX(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [ "bar", "baz", "foo" ]
})JSON");

EXPECT_EQ(document, expected);
}
18 changes: 18 additions & 0 deletions test/linter/draft4_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,21 @@ TEST(Lint_draft4, duplicate_enum_values_4) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft4, duplicate_required_values_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"required": [ "foo", "bar", "baz", "foo" ]
})JSON");

LINT_AND_FIX(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"required": [ "bar", "baz", "foo" ]
})JSON");

EXPECT_EQ(document, expected);
}
18 changes: 18 additions & 0 deletions test/linter/draft6_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,21 @@ TEST(Lint_draft6, duplicate_enum_values_6) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft6, duplicate_required_values_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"required": [ "foo", "bar", "baz", "foo" ]
})JSON");

LINT_AND_FIX(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"required": [ "bar", "baz", "foo" ]
})JSON");

EXPECT_EQ(document, expected);
}
18 changes: 18 additions & 0 deletions test/linter/draft7_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,21 @@ TEST(Lint_draft7, duplicate_enum_values_7) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft7, duplicate_required_values_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [ "foo", "bar", "baz", "foo" ]
})JSON");

LINT_AND_FIX(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [ "bar", "baz", "foo" ]
})JSON");

EXPECT_EQ(document, expected);
}

0 comments on commit ff818e2

Please sign in to comment.