Refactor `OutputSuggestions` to make it testable. Bug: 500845363 Change-Id: I11df1e31cc58e125b372b0bbef6137886a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/22720 Commit-Queue: Matt Stark <msta@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/command_suggest.cc b/src/gn/command_suggest.cc index 0427e49..54110fe 100644 --- a/src/gn/command_suggest.cc +++ b/src/gn/command_suggest.cc
@@ -4,6 +4,7 @@ #include <stddef.h> +#include <functional> #include <vector> #include "base/files/file_util.h" @@ -100,38 +101,6 @@ } return SourceFile(); } - -constexpr auto kLabelLike = TextDecoration::DECORATION_GREEN; - -void OutputSuggestion(std::string_view message) { - OutputString("Suggestion: ", TextDecoration::DECORATION_BLUE); - OutputString(message); -} - -void OutputWarning(std::string_view message = "") { - OutputString("Warning: ", TextDecoration::DECORATION_YELLOW); - OutputString(message); -} - -void OutputError(std::string_view message = "") { - OutputString("Error: ", TextDecoration::DECORATION_RED); - OutputString(message); -} - -void OutputQuoted(std::string_view message) { - OutputString("\"", kLabelLike); - OutputString(message, kLabelLike); - OutputString("\"", kLabelLike); -} - -void OutputDefinition(const Target* target) { - OutputString(":", kLabelLike); - OutputString(target->label().name(), kLabelLike); - OutputString(" (defined at "); - OutputString(target->user_friendly_location().Describe(false), kLabelLike); - OutputString(")"); -} - } // namespace // Resolves an input to a list of targets, and whether each are private. @@ -206,24 +175,58 @@ } bool OutputSuggestions(const std::vector<const Target*>& all_targets, - Setup* setup, + const BuildSettings* build_settings, + const Label& default_toolchain, std::string_view includer_name, - std::string_view included_name) { - Label current_toolchain = setup->loader()->default_toolchain_label(); - auto OutputTarget = [¤t_toolchain](const Target* target) { + std::string_view included_name, + OutputStringFunc output_fn) { + auto OutputString = + [&](std::string_view str, TextDecoration dec = DECORATION_NONE, + HtmlEscaping esc = DEFAULT_ESCAPING) { output_fn(str, dec, esc); }; + + constexpr auto kLabelLike = TextDecoration::DECORATION_GREEN; + + auto StartSuggestion = [&]() { + OutputString("Suggestion: ", TextDecoration::DECORATION_BLUE); + }; + auto StartWarning = [&]() { + OutputString("Warning: ", TextDecoration::DECORATION_YELLOW); + }; + auto StartError = [&]() { + OutputString("Error: ", TextDecoration::DECORATION_RED); + }; + + auto OutputQuoted = [&](std::string_view message) { + OutputString("\"", kLabelLike); + OutputString(message, kLabelLike); + OutputString("\"", kLabelLike); + }; + + auto OutputDefinition = [&](const Target* target) { + OutputString(":", kLabelLike); + OutputString(target->label().name(), kLabelLike); + OutputString(" (defined at "); + OutputString(target->user_friendly_location().Describe(false), kLabelLike); + OutputString(")"); + }; + + Label current_toolchain = default_toolchain; + auto OutputTarget = [¤t_toolchain, + &OutputString](const Target* target) { OutputString(target->label().GetUserVisibleName(current_toolchain), kLabelLike); }; auto OutputInsertionHint = [&](std::string_view key, std::string_view value, const Target* target) { - OutputSuggestion("Add "); + StartSuggestion(); + OutputString("Add "); OutputString(key); OutputString(" = [ "); OutputQuoted(value); OutputString(" ] to "); OutputDefinition(target); - if (current_toolchain != setup->loader()->default_toolchain_label()) { + if (current_toolchain != default_toolchain) { OutputString(" for toolchain "); OutputString( target->label().GetToolchainLabel().GetUserVisibleName(false), @@ -234,9 +237,9 @@ auto ResolveSuggestion = [&](std::string_view value) { const auto& [targets, ok] = ResolveSuggestionToTarget( - &setup->build_settings(), all_targets, current_toolchain, value); + build_settings, all_targets, current_toolchain, value); if (!ok) { - OutputError(); + StartError(); if (value.starts_with("//")) { OutputString("Could not find target or file "); OutputQuoted(value); @@ -255,12 +258,12 @@ return false; if (includer_targets.empty()) { - OutputError(); + StartError(); OutputQuoted(includer_name); OutputString(" did not resolve to any targets\n"); return false; } else if (includer_targets.size() > 1) { - OutputError(); + StartError(); OutputQuoted(includer_name); OutputString(" resolved to multiple targets\n"); for (const auto& [target, is_private] : includer_targets) { @@ -287,7 +290,8 @@ if (targets.empty()) { OutputQuoted(included_name); OutputString(" is not in the headers of any targets.\n"); - OutputSuggestion("Add "); + StartSuggestion(); + OutputString("Add "); OutputQuoted(included_name); OutputString(" to a target's public headers"); return true; @@ -316,7 +320,7 @@ } if (targets.size() > 1) { - OutputWarning(); + StartWarning(); OutputQuoted(included_name); OutputString(" is ambiguous because it belongs to multiple targets:\n"); for (const auto& [target, _] : targets) { @@ -324,7 +328,8 @@ OutputTarget(target); OutputString("\n"); } - OutputSuggestion( + StartSuggestion(); + OutputString( "Create a source_set target for the common headers and sources and " "have all of the above targets depend on that."); OutputInsertionHint(dep_field, "$NEW_SOURCE_SET", includer); @@ -333,11 +338,12 @@ const auto& [included, included_dep_kind] = targets.front(); if (included_dep_kind == commands::ApiScope::kPrivate) { - OutputWarning(); + StartWarning(); OutputQuoted(included_name); OutputString(" is in the private API of "); OutputTarget(included); - OutputSuggestion("Move "); + StartSuggestion(); + OutputString("Move "); OutputQuoted(included_name); OutputString(" from `sources` to `public` in "); OutputDefinition(included); @@ -366,6 +372,19 @@ } int RunSuggest(const std::vector<std::string>& args) { + constexpr auto kLabelLike = TextDecoration::DECORATION_GREEN; + + auto OutputError = [](std::string_view message) { + OutputString("Error: ", TextDecoration::DECORATION_RED); + OutputString(message); + }; + + auto OutputQuoted = [](std::string_view message) { + OutputString("\"", kLabelLike); + OutputString(message, kLabelLike); + OutputString("\"", kLabelLike); + }; + if (args.size() <= 1) { OutputError("gn suggest requires arguments. See \"gn help suggest\"\n"); return 1; @@ -404,7 +423,12 @@ OutputString(":\n"); } - success &= OutputSuggestions(all_targets, setup, includer, included); + success &= OutputSuggestions( + all_targets, &setup->build_settings(), + setup->loader()->default_toolchain_label(), includer, included, + [](std::string_view str, TextDecoration dec, HtmlEscaping esc) { + ::OutputString(str, dec, esc); + }); } return success ? 0 : 1;
diff --git a/src/gn/commands.h b/src/gn/commands.h index fcc5bd8..62d0ffb 100644 --- a/src/gn/commands.h +++ b/src/gn/commands.h
@@ -5,6 +5,7 @@ #ifndef TOOLS_GN_COMMANDS_H_ #define TOOLS_GN_COMMANDS_H_ +#include <functional> #include <map> #include <set> #include <string> @@ -12,6 +13,7 @@ #include <vector> #include "base/values.h" +#include "gn/standard_out.h" #include "gn/target.h" #include "gn/unique_vector.h" @@ -103,6 +105,15 @@ extern const char kSuggest_Help[]; int RunSuggest(const std::vector<std::string>& args); +using OutputStringFunc = + std::function<void(std::string_view, TextDecoration, HtmlEscaping)>; +bool OutputSuggestions(const std::vector<const Target*>& all_targets, + const BuildSettings* build_settings, + const Label& default_toolchain, + std::string_view includer_name, + std::string_view included_name, + OutputStringFunc output_fn); + extern const char kCleanStale[]; extern const char kCleanStale_HelpShort[]; extern const char kCleanStale_Help[];