| // Copyright 2020 The Abseil Authors. | |
| // | |
| // 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 | |
| // | |
| // https://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. | |
| #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ | |
| #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ | |
| #include <algorithm> | |
| #include "absl/base/attributes.h" | |
| #include "absl/strings/internal/str_format/arg.h" | |
| #include "absl/strings/internal/str_format/constexpr_parser.h" | |
| #include "absl/strings/internal/str_format/extension.h" | |
| // Compile time check support for entry points. | |
| #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER | |
| // We disable format checker under vscode intellisense compilation. | |
| // See https://github.com/microsoft/vscode-cpptools/issues/3683 for | |
| // more details. | |
| #if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && \ | |
| !defined(__INTELLISENSE__) | |
| #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 | |
| #endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && | |
| // !defined(__INTELLISENSE__) | |
| #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace str_format_internal { | |
| #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER | |
| template <FormatConversionCharSet... C> | |
| constexpr bool ValidFormatImpl(string_view format) { | |
| int next_arg = 0; | |
| const char* p = format.data(); | |
| const char* const end = p + format.size(); | |
| constexpr FormatConversionCharSet | |
| kAllowedConvs[(std::max)(sizeof...(C), size_t{1})] = {C...}; | |
| bool used[(std::max)(sizeof...(C), size_t{1})]{}; | |
| constexpr int kNumArgs = sizeof...(C); | |
| while (p != end) { | |
| while (p != end && *p != '%') ++p; | |
| if (p == end) { | |
| break; | |
| } | |
| if (p + 1 >= end) return false; | |
| if (p[1] == '%') { | |
| // %% | |
| p += 2; | |
| continue; | |
| } | |
| UnboundConversion conv(absl::kConstInit); | |
| p = ConsumeUnboundConversion(p + 1, end, &conv, &next_arg); | |
| if (p == nullptr) return false; | |
| if (conv.arg_position <= 0 || conv.arg_position > kNumArgs) { | |
| return false; | |
| } | |
| if (!Contains(kAllowedConvs[conv.arg_position - 1], conv.conv)) { | |
| return false; | |
| } | |
| used[conv.arg_position - 1] = true; | |
| for (auto extra : {conv.width, conv.precision}) { | |
| if (extra.is_from_arg()) { | |
| int pos = extra.get_from_arg(); | |
| if (pos <= 0 || pos > kNumArgs) return false; | |
| used[pos - 1] = true; | |
| if (!Contains(kAllowedConvs[pos - 1], '*')) { | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| if (sizeof...(C) != 0) { | |
| for (bool b : used) { | |
| if (!b) return false; | |
| } | |
| } | |
| return true; | |
| } | |
| #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER | |
| } // namespace str_format_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl | |
| #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ |