blob: 8dd72dc6dfe23d7243422fe69c5bb49d8105db23 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_UTILS_H_
#define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_UTILS_H_
#include <errno.h>
#include <clocale>
#include <cstring>
#include <regex>
#include <set>
#include <string>
#include <string_view>
#include <fidl/findings.h>
#include <fidl/reporter.h>
namespace fidl {
namespace utils {
// An enumeration of supported syntaxes for the purposes of the ongoing
// old-to-new migration. The kOld represents the syntax as of Jan 1, 2021 and
// the kNew is the new syntax per FTP-050 and its amendments.
// TODO(fxbug.dev/70247): Remove this when old syntax support is removed
enum struct Syntax {
kOld,
kNew,
};
template <class>
inline constexpr bool always_false_v = false;
// Helper object for creating a callable argument to std::visit by passing in
// lambdas for handling each variant (code comes from
// https://en.cppreference.com/w/cpp/utility/variant/visit)
template <class... Ts>
struct matchers : Ts... {
using Ts::operator()...;
};
template <class... Ts>
matchers(Ts...) -> matchers<Ts...>;
using reporter::Reporter;
constexpr char kWhitespaceChars[] = " \t\n\v\f\r";
constexpr char kWhitespaceNoNewlineChars[] = " \t\v\f\r";
inline bool IsWhitespace(char ch) { return ch != '\0' && strchr(kWhitespaceChars, ch) != nullptr; }
inline bool IsWhitespaceNoNewline(char ch) {
return ch != '\0' && strchr(kWhitespaceNoNewlineChars, ch) != nullptr;
}
// Returns true if the view has anything other than whitespace
inline bool IsBlank(std::string_view view) {
return view.find_first_not_of(kWhitespaceChars) == std::string::npos;
}
// IsValidLibraryComponent validates individual components of a library
// identifier.
//
// See https://fuchsia.dev/fuchsia-src/reference/fidl/language/language#identifiers
bool IsValidLibraryComponent(const std::string& component);
// IsValidIdentifierComponent validates individual components of an identifier
// (other than a library identifier).
//
// See https://fuchsia.dev/fuchsia-src/reference/fidl/language/language#identifiers
bool IsValidIdentifierComponent(const std::string& component);
// IsValidFullyQualifiedMethodIdentifier validates fully qualified method
// identifiers, i.e. a library identifier, followed by a slash, followed by a
// protocol identifier, a dot, and lastly the method name.
bool IsValidFullyQualifiedMethodIdentifier(const std::string& fq_identifier);
inline bool LineFromOffsetIsBlank(const std::string& str, size_t offset) {
for (size_t i = offset; i < str.size() && str[i] != '\n'; i++) {
if (!IsWhitespaceNoNewline(str[i])) {
return false;
}
}
return true;
}
inline bool FirstLineIsBlank(const std::string& str) { return LineFromOffsetIsBlank(str, 0); }
inline bool LineFromOffsetIsRegularComment(std::string_view view, size_t offset) {
size_t i = offset;
if ((i + 1 < view.size()) && view[i] == '/' && view[i + 1] == '/') {
// Doc comments, which start with three slashes, should not
// be treated as comments since they get internally converted
// to attributes. But comments that start with more than three
// slashes are not converted to doc comment attributes.
if (view.size() == 2) {
return true;
} else {
return (i + 2 == view.size()) || (view[i + 2] != '/') ||
((i + 3 < view.size()) && (view[i + 3] == '/'));
}
} else {
return false;
}
}
inline bool FirstLineIsRegularComment(std::string_view view) {
return LineFromOffsetIsRegularComment(view, 0);
}
enum class ParseNumericResult {
kSuccess,
kOutOfBounds,
kMalformed,
};
template <typename NumericType>
ParseNumericResult ParseNumeric(const std::string& input, NumericType* out_value, int base = 0) {
assert(out_value != nullptr);
// Set locale to "C" for numeric types, since all strtox() functions are locale-dependent
setlocale(LC_NUMERIC, "C");
const char* startptr = input.data();
if (base == 0 && 2 < input.size() && input[0] == '0' && (input[1] == 'b' || input[1] == 'B')) {
startptr += 2;
base = 2;
}
char* endptr;
if constexpr (std::is_unsigned<NumericType>::value) {
if (input[0] == '-')
return ParseNumericResult::kOutOfBounds;
errno = 0;
unsigned long long value = strtoull(startptr, &endptr, base);
if (errno != 0)
return ParseNumericResult::kMalformed;
if (value > std::numeric_limits<NumericType>::max())
return ParseNumericResult::kOutOfBounds;
*out_value = static_cast<NumericType>(value);
} else if constexpr (std::is_floating_point<NumericType>::value) {
errno = 0;
long double value = strtold(startptr, &endptr);
if (errno != 0)
return ParseNumericResult::kMalformed;
if (value > std::numeric_limits<NumericType>::max())
return ParseNumericResult::kOutOfBounds;
if (value < std::numeric_limits<NumericType>::lowest())
return ParseNumericResult::kOutOfBounds;
*out_value = static_cast<NumericType>(value);
} else {
errno = 0;
long long value = strtoll(startptr, &endptr, base);
if (errno != 0)
return ParseNumericResult::kMalformed;
if (value > std::numeric_limits<NumericType>::max())
return ParseNumericResult::kOutOfBounds;
if (value < std::numeric_limits<NumericType>::lowest())
return ParseNumericResult::kOutOfBounds;
*out_value = static_cast<NumericType>(value);
}
if (endptr != (input.c_str() + input.size()))
return ParseNumericResult::kMalformed;
return ParseNumericResult::kSuccess;
}
bool ends_with_underscore(const std::string& str);
bool has_adjacent_underscores(const std::string& str);
std::vector<std::string> id_to_words(const std::string& str);
// Split the identifier into words, excluding words in the |stop_words| set.
std::vector<std::string> id_to_words(const std::string& str,
const std::set<std::string> stop_words);
bool is_konstant_case(const std::string& str);
bool is_lower_no_separator_case(const std::string& str);
bool is_lower_snake_case(const std::string& str);
bool is_upper_snake_case(const std::string& str);
bool is_lower_camel_case(const std::string& str);
bool is_upper_camel_case(const std::string& str);
std::string strip_konstant_k(const std::string& str);
std::string to_konstant_case(const std::string& str);
std::string to_lower_no_separator_case(const std::string& str);
std::string to_lower_snake_case(const std::string& str);
std::string to_upper_snake_case(const std::string& str);
std::string to_lower_camel_case(const std::string& str);
std::string to_upper_camel_case(const std::string& str);
// Returns the canonical form of an identifier, used to detect name collisions
// in FIDL libraries. For example, the identifers "FooBar" and "FOO_BAR" collide
// because canonicalize returns "foo_bar" for both.
std::string canonicalize(std::string_view identifier);
// Used by fidl-lint FormatFindings, and for testing,
// this generates the linter error message string in the format
// required for the fidl::Reporter.
void PrintFinding(std::ostream& os, const Finding& finding);
// Used by fidl-lint main() and for testing, this generates the linter error
// messages for a list of findings.
std::vector<std::string> FormatFindings(const Findings& findings, bool enable_color);
// Gets a string with the original file contents, and a string with the
// formatted file, and makes sure that the only difference is in the whitespace.
// Used by the formatter to make sure that formatting was not destructive.
bool OnlyWhitespaceChanged(const std::string& unformatted_input,
const std::string& formatted_output);
} // namespace utils
} // namespace fidl
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_UTILS_H_