summaryrefslogtreecommitdiffstats
path: root/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp')
-rw-r--r--debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp1756
1 files changed, 1756 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp
new file mode 100644
index 00000000..23a87479
--- /dev/null
+++ b/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/tokenize_cleanup.cpp
@@ -0,0 +1,1756 @@
+/**
+ * @file tokenize_cleanup.cpp
+ * Looks at simple sequences to refine the chunk types.
+ * Examples:
+ * - change '[' + ']' into '[]'/
+ * - detect "version = 10;" vs "version (xxx) {"
+ *
+ * @author Ben Gardner
+ * @author Guy Maurel 2015, 2021
+ * @license GPL v2+
+ */
+
+#include "tokenize_cleanup.h"
+
+#include "combine.h"
+#include "combine_skip.h"
+#include "flag_braced_init_list.h"
+#include "flag_decltype.h"
+#include "keywords.h"
+#include "prototypes.h"
+#include "punctuators.h"
+#include "space.h"
+#include "unc_ctype.h"
+
+
+using namespace uncrustify;
+
+
+/**
+ * Mark types in a single template argument.
+ *
+ * @param start chunk to start check at
+ * @param end chunk to end check at
+ */
+static void check_template_arg(chunk_t *start, chunk_t *end);
+
+
+/**
+ * Mark types in template argument(s).
+ *
+ * @param start chunk to start check at
+ * @param end chunk to end check at
+ */
+static void check_template_args(chunk_t *start, chunk_t *end);
+
+
+/**
+ * If there is nothing but CT_WORD and CT_MEMBER, then it's probably a
+ * template thingy. Otherwise, it's likely a comparison.
+ *
+ * @param start chunk to start check at
+ */
+static void check_template(chunk_t *start, bool in_type_cast);
+
+
+/**
+ * Convert '>' + '>' into '>>'
+ * If we only have a single '>', then change it to CT_COMPARE.
+ *
+ * @param pc chunk to start at
+ */
+static chunk_t *handle_double_angle_close(chunk_t *pc);
+
+
+/**
+ * Marks ObjC specific chunks in propery declaration, by setting
+ * parent types and chunk types.
+ */
+static void cleanup_objc_property(chunk_t *start);
+
+
+/**
+ * Marks ObjC specific chunks in propery declaration (getter/setter attribute)
+ * Will mark 'test4Setter'and ':' in '@property (setter=test4Setter:, strong) int test4;' as CT_OC_SEL_NAME
+ */
+static void mark_selectors_in_property_with_open_paren(chunk_t *open_paren);
+
+
+/**
+ * Marks ObjC specific chunks in propery declaration ( attributes)
+ * Changes all the CT_WORD and CT_TYPE to CT_OC_PROPERTY_ATTR
+ */
+static void mark_attributes_in_property_with_open_paren(chunk_t *open_paren);
+
+
+static chunk_t *handle_double_angle_close(chunk_t *pc)
+{
+ chunk_t *next = chunk_get_next(pc);
+
+ if (next != nullptr)
+ {
+ if ( chunk_is_token(pc, CT_ANGLE_CLOSE)
+ && chunk_is_token(next, CT_ANGLE_CLOSE)
+ && get_chunk_parent_type(pc) == CT_NONE
+ && (pc->orig_col_end + 1) == next->orig_col
+ && get_chunk_parent_type(next) == CT_NONE)
+ {
+ pc->str.append('>');
+ set_chunk_type(pc, CT_SHIFT);
+ pc->orig_col_end = next->orig_col_end;
+
+ chunk_t *tmp = chunk_get_next_ncnnl(next);
+ chunk_del(next);
+ next = tmp;
+ }
+ else
+ {
+ // bug #663
+ set_chunk_type(pc, CT_COMPARE);
+ }
+ }
+ return(next);
+}
+
+
+void split_off_angle_close(chunk_t *pc)
+{
+ const chunk_tag_t *ct = find_punctuator(pc->text() + 1, cpd.lang_flags);
+
+ if (ct == nullptr)
+ {
+ return;
+ }
+ chunk_t nc = *pc;
+
+ pc->str.resize(1);
+ pc->orig_col_end = pc->orig_col + 1;
+ set_chunk_type(pc, CT_ANGLE_CLOSE);
+
+ set_chunk_type(&nc, ct->type);
+ nc.str.pop_front();
+ nc.orig_col++;
+ nc.column++;
+ chunk_add_after(&nc, pc);
+}
+
+
+void tokenize_trailing_return_types(void)
+{
+ // Issue #2330
+ // auto max(int a, int b) -> int;
+ // Issue #2460
+ // auto f01() -> bool;
+ // auto f02() noexcept -> bool;
+ // auto f03() noexcept(true) -> bool;
+ // auto f04() noexcept(false) -> bool;
+ // auto f05() noexcept -> bool = delete;
+ // auto f06() noexcept(true) -> bool = delete;
+ // auto f07() noexcept(false) -> bool = delete;
+ // auto f11() const -> bool;
+ // auto f12() const noexcept -> bool;
+ // auto f13() const noexcept(true) -> bool;
+ // auto f14() const noexcept(false) -> bool;
+ // auto f15() const noexcept -> bool = delete;
+ // auto f16() const noexcept(true) -> bool = delete;
+ // auto f17() const noexcept(false) -> bool = delete;
+ // auto f21() throw() -> bool;
+ // auto f22() throw() -> bool = delete;
+ // auto f23() const throw() -> bool;
+ // auto f24() const throw() -> bool = delete;
+ chunk_t *pc;
+
+ for (pc = chunk_get_head(); pc != nullptr; pc = chunk_get_next_ncnnl(pc))
+ {
+ char copy[1000];
+ LOG_FMT(LNOTE, "%s(%d): orig_line is %zu, orig_col is %zu, text() is '%s'\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col, pc->elided_text(copy));
+
+ if ( chunk_is_token(pc, CT_MEMBER)
+ && (strcmp(pc->text(), "->") == 0))
+ {
+ chunk_t *tmp = chunk_get_prev_ncnnl(pc);
+ chunk_t *tmp_2;
+ chunk_t *open_paren;
+
+ if (chunk_is_token(tmp, CT_QUALIFIER))
+ {
+ // auto max(int a, int b) const -> int;
+ // auto f11() const -> bool;
+ tmp = chunk_get_prev_ncnnl(tmp);
+ }
+ else if (chunk_is_token(tmp, CT_NOEXCEPT))
+ {
+ // noexcept is present
+ tmp_2 = chunk_get_prev_ncnnl(tmp);
+
+ if (chunk_is_token(tmp_2, CT_QUALIFIER))
+ {
+ // auto f12() const noexcept -> bool;
+ // auto f15() const noexcept -> bool = delete;
+ tmp = chunk_get_prev_ncnnl(tmp_2);
+ }
+ else
+ {
+ // auto f02() noexcept -> bool;
+ // auto f05() noexcept -> bool = delete;
+ tmp = tmp_2;
+ }
+ }
+ else if (chunk_is_token(tmp, CT_PAREN_CLOSE))
+ {
+ open_paren = chunk_get_prev_type(tmp, CT_PAREN_OPEN, tmp->level);
+ tmp = chunk_get_prev_ncnnl(open_paren);
+
+ if (chunk_is_token(tmp, CT_NOEXCEPT))
+ {
+ // noexcept is present
+ tmp_2 = chunk_get_prev_ncnnl(tmp);
+
+ if (chunk_is_token(tmp_2, CT_QUALIFIER))
+ {
+ // auto f13() const noexcept(true) -> bool;
+ // auto f14() const noexcept(false) -> bool;
+ // auto f16() const noexcept(true) -> bool = delete;
+ // auto f17() const noexcept(false) -> bool = delete;
+ tmp = chunk_get_prev_ncnnl(tmp_2);
+ }
+ else
+ {
+ // auto f03() noexcept(true) -> bool;
+ // auto f04() noexcept(false) -> bool;
+ // auto f06() noexcept(true) -> bool = delete;
+ // auto f07() noexcept(false) -> bool = delete;
+ tmp = tmp_2;
+ }
+ }
+ else if (chunk_is_token(tmp, CT_THROW))
+ {
+ // throw is present
+ tmp_2 = chunk_get_prev_ncnnl(tmp);
+
+ if (chunk_is_token(tmp_2, CT_QUALIFIER))
+ {
+ // auto f23() const throw() -> bool;
+ // auto f24() const throw() -> bool = delete;
+ tmp = chunk_get_prev_ncnnl(tmp_2);
+ }
+ else
+ {
+ // auto f21() throw() -> bool;
+ // auto f22() throw() -> bool = delete;
+ tmp = tmp_2;
+ }
+ }
+ else
+ {
+ LOG_FMT(LNOTE, "%s(%d): NOT COVERED\n", __func__, __LINE__);
+ }
+ }
+ else
+ {
+ LOG_FMT(LNOTE, "%s(%d): NOT COVERED\n", __func__, __LINE__);
+ }
+
+ if ( chunk_is_token(tmp, CT_FPAREN_CLOSE)
+ && ( get_chunk_parent_type(tmp) == CT_FUNC_PROTO
+ || get_chunk_parent_type(tmp) == CT_FUNC_DEF))
+ {
+ set_chunk_type(pc, CT_TRAILING_RET);
+ LOG_FMT(LNOTE, "%s(%d): set trailing return type for text() is '%s'\n",
+ __func__, __LINE__, pc->text()); // Issue #3222
+ // TODO
+ // https://en.cppreference.com/w/cpp/language/function
+ // noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) -> trailing
+ chunk_t *next = chunk_get_next_ncnnl(pc);
+
+ if (chunk_is_token(next, CT_DECLTYPE))
+ {
+ // TODO
+ }
+ else if (chunk_is_token(next, CT_WORD))
+ {
+ set_chunk_type(next, CT_TYPE); // Issue #3222
+ next = chunk_get_next_ncnnl(next);
+
+ if ( chunk_is_token(next, CT_ARITH)
+ && (next->str[0] == '*'))
+ {
+ set_chunk_type(next, CT_PTR_TYPE);
+ }
+ }
+ else
+ {
+ // TODO
+ }
+ }
+ }
+ }
+} // tokenize_trailing_return_types
+
+
+void tokenize_cleanup(void)
+{
+ LOG_FUNC_ENTRY();
+
+ chunk_t *prev = nullptr;
+ chunk_t *next;
+ bool in_type_cast = false;
+
+ cpd.unc_stage = unc_stage_e::TOKENIZE_CLEANUP;
+
+ /*
+ * Since [] is expected to be TSQUARE for the 'operator', we need to make
+ * this change in the first pass.
+ */
+ chunk_t *pc;
+
+ for (pc = chunk_get_head(); pc != nullptr; pc = chunk_get_next_ncnnl(pc))
+ {
+ if (chunk_is_token(pc, CT_SQUARE_OPEN))
+ {
+ next = chunk_get_next_ncnnl(pc);
+
+ if (chunk_is_token(next, CT_SQUARE_CLOSE))
+ {
+ // Change '[' + ']' into '[]'
+ set_chunk_type(pc, CT_TSQUARE);
+ pc->str = "[]";
+ /*
+ * bug #664: The original orig_col_end of CT_SQUARE_CLOSE is
+ * stored at orig_col_end of CT_TSQUARE.
+ * pc->orig_col_end += 1;
+ */
+ pc->orig_col_end = next->orig_col_end;
+ chunk_del(next);
+ }
+ }
+
+ if ( chunk_is_token(pc, CT_SEMICOLON)
+ && pc->flags.test(PCF_IN_PREPROC)
+ && !chunk_get_next_ncnnl(pc, scope_e::PREPROC))
+ {
+ LOG_FMT(LNOTE, "%s(%d): %s:%zu Detected a macro that ends with a semicolon. Possible failures if used.\n",
+ __func__, __LINE__, cpd.filename.c_str(), pc->orig_line);
+ }
+ }
+
+ // change := to CT_SQL_ASSIGN Issue #527
+ for (pc = chunk_get_head(); pc != nullptr; pc = chunk_get_next_ncnnl(pc))
+ {
+ if (chunk_is_token(pc, CT_COLON))
+ {
+ next = chunk_get_next_ncnnl(pc);
+
+ if (chunk_is_token(next, CT_ASSIGN))
+ {
+ // Change ':' + '=' into ':='
+ set_chunk_type(pc, CT_SQL_ASSIGN);
+ pc->str = ":=";
+ pc->orig_col_end = next->orig_col_end;
+ chunk_del(next);
+ }
+ }
+ }
+
+ // We can handle everything else in the second pass
+ pc = chunk_get_head();
+ next = chunk_get_next_ncnnl(pc);
+
+ while ( pc != nullptr
+ && next != nullptr)
+ {
+ if ( chunk_is_token(pc, CT_DOT)
+ && language_is_set(LANG_ALLC))
+ {
+ set_chunk_type(pc, CT_MEMBER);
+ }
+
+ if ( chunk_is_token(pc, CT_NULLCOND)
+ && language_is_set(LANG_CS))
+ {
+ set_chunk_type(pc, CT_MEMBER);
+ }
+
+ // Determine the version stuff (D only)
+ if (chunk_is_token(pc, CT_D_VERSION))
+ {
+ if (chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_D_VERSION_IF);
+ }
+ else
+ {
+ if (next->type != CT_ASSIGN)
+ {
+ LOG_FMT(LERR, "%s(%d): %s:%zu: version: Unexpected token %s\n",
+ __func__, __LINE__, cpd.filename.c_str(), pc->orig_line, get_token_name(next->type));
+ cpd.error_count++;
+ }
+ set_chunk_type(pc, CT_WORD);
+ }
+ }
+
+ // Determine the scope stuff (D only)
+ if (chunk_is_token(pc, CT_D_SCOPE))
+ {
+ if (chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_D_SCOPE_IF);
+ }
+ else
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ }
+
+ /*
+ * Change CT_BASE before CT_PAREN_OPEN to CT_WORD.
+ * public myclass() : base() {}
+ * -or-
+ * var x = (T)base.y;
+ */
+ if ( chunk_is_token(pc, CT_BASE)
+ && ( chunk_is_token(next, CT_PAREN_OPEN)
+ || chunk_is_token(next, CT_DOT)))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ if ( chunk_is_token(pc, CT_ENUM)
+ && ( chunk_is_token(next, CT_STRUCT)
+ || chunk_is_token(next, CT_CLASS)))
+ {
+ set_chunk_type(next, CT_ENUM_CLASS);
+ }
+ chunk_t *next_non_attr = language_is_set(LANG_CPP) ? skip_attribute_next(next) : next;
+
+ /*
+ * Change CT_WORD after CT_ENUM, CT_UNION, CT_STRUCT, or CT_CLASS to CT_TYPE
+ * Change CT_WORD before CT_WORD to CT_TYPE
+ */
+ if (chunk_is_token(next_non_attr, CT_WORD))
+ {
+ if ( chunk_is_token(pc, CT_ENUM)
+ || chunk_is_token(pc, CT_ENUM_CLASS)
+ || chunk_is_token(pc, CT_UNION)
+ || chunk_is_token(pc, CT_STRUCT)
+ || chunk_is_token(pc, CT_CLASS))
+ {
+ set_chunk_type(next_non_attr, CT_TYPE);
+ }
+
+ if (chunk_is_token(pc, CT_WORD))
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ }
+
+ /*
+ * change extern to qualifier if extern isn't followed by a string or
+ * an open parenthesis
+ */
+ if (chunk_is_token(pc, CT_EXTERN))
+ {
+ if (chunk_is_token(next, CT_STRING))
+ {
+ // Probably 'extern "C"'
+ }
+ else if (chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ // Probably 'extern (C)'
+ }
+ else
+ {
+ // Something else followed by a open brace
+ chunk_t *tmp = chunk_get_next_ncnnl(next);
+
+ if ( tmp == nullptr
+ || tmp->type != CT_BRACE_OPEN)
+ {
+ set_chunk_type(pc, CT_QUALIFIER);
+ }
+ }
+ }
+
+ /*
+ * Change CT_STAR to CT_PTR_TYPE if preceded by
+ * CT_TYPE, CT_QUALIFIER, or CT_PTR_TYPE
+ * or by a
+ * CT_WORD which is preceded by CT_DC_MEMBER: '::aaa *b'
+ */
+ if ( (chunk_is_token(next, CT_STAR))
+ || ( language_is_set(LANG_CPP)
+ && (chunk_is_token(next, CT_CARET)))
+ || ( language_is_set(LANG_CS)
+ && (chunk_is_token(next, CT_QUESTION))
+ && (strcmp(pc->text(), "null") != 0)))
+ {
+ if ( chunk_is_token(pc, CT_TYPE)
+ || chunk_is_token(pc, CT_QUALIFIER)
+ || chunk_is_token(pc, CT_PTR_TYPE))
+ {
+ set_chunk_type(next, CT_PTR_TYPE);
+ }
+ }
+
+ if ( chunk_is_token(pc, CT_TYPE_CAST)
+ && chunk_is_token(next, CT_ANGLE_OPEN))
+ {
+ set_chunk_parent(next, CT_TYPE_CAST);
+ in_type_cast = true;
+ }
+
+ if (chunk_is_token(pc, CT_DECLTYPE))
+ {
+ flag_cpp_decltype(pc);
+ }
+
+ // Change angle open/close to CT_COMPARE, if not a template thingy
+ if ( chunk_is_token(pc, CT_ANGLE_OPEN)
+ && pc->parent_type != CT_TYPE_CAST)
+ {
+ /*
+ * pretty much all languages except C use <> for something other than
+ * comparisons. "#include<xxx>" is handled elsewhere.
+ */
+ if (language_is_set(LANG_OC | LANG_CPP | LANG_CS | LANG_JAVA | LANG_VALA))
+ {
+ // bug #663
+ check_template(pc, in_type_cast);
+ }
+ else
+ {
+ // convert CT_ANGLE_OPEN to CT_COMPARE
+ set_chunk_type(pc, CT_COMPARE);
+ }
+ }
+
+ if ( chunk_is_token(pc, CT_ANGLE_CLOSE)
+ && pc->parent_type != CT_TEMPLATE)
+ {
+ if (in_type_cast)
+ {
+ in_type_cast = false;
+ set_chunk_parent(pc, CT_TYPE_CAST);
+ }
+ else
+ {
+ next = handle_double_angle_close(pc);
+ }
+ }
+
+ if (language_is_set(LANG_D))
+ {
+ // Check for the D string concat symbol '~'
+ if ( chunk_is_token(pc, CT_INV)
+ && ( chunk_is_token(prev, CT_STRING)
+ || chunk_is_token(prev, CT_WORD)
+ || chunk_is_token(next, CT_STRING)))
+ {
+ set_chunk_type(pc, CT_CONCAT);
+ }
+
+ // Check for the D template symbol '!' (word + '!' + word or '(')
+ if ( chunk_is_token(pc, CT_NOT)
+ && chunk_is_token(prev, CT_WORD)
+ && ( chunk_is_token(next, CT_PAREN_OPEN)
+ || chunk_is_token(next, CT_WORD)
+ || chunk_is_token(next, CT_TYPE)
+ || chunk_is_token(next, CT_NUMBER)
+ || chunk_is_token(next, CT_NUMBER_FP)
+ || chunk_is_token(next, CT_STRING)
+ || chunk_is_token(next, CT_STRING_MULTI)))
+ {
+ set_chunk_type(pc, CT_D_TEMPLATE);
+ }
+
+ // handle "version(unittest) { }" vs "unittest { }"
+ if ( chunk_is_token(pc, CT_UNITTEST)
+ && chunk_is_token(prev, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ // handle 'static if' and merge the tokens
+ if ( chunk_is_token(pc, CT_IF)
+ && chunk_is_str(prev, "static", 6))
+ {
+ // delete PREV and merge with IF
+ pc->str.insert(0, ' ');
+ pc->str.insert(0, prev->str);
+ pc->orig_col = prev->orig_col;
+ pc->orig_line = prev->orig_line;
+ chunk_t *to_be_deleted = prev;
+ prev = chunk_get_prev_ncnnl(prev);
+
+ if (prev != nullptr)
+ {
+ chunk_del(to_be_deleted);
+ }
+ }
+ }
+
+ if (language_is_set(LANG_CPP))
+ {
+ // Change Word before '::' into a type
+ if ( chunk_is_token(pc, CT_WORD)
+ && chunk_is_token(next, CT_DC_MEMBER))
+ {
+ prev = chunk_get_prev(pc);
+
+ if (prev == nullptr) // Issue #3010
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ else
+ {
+ if (chunk_is_token(prev, CT_COLON))
+ {
+ // nothing to do
+ }
+ else
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ }
+ }
+
+ // Set parent type for 'if constexpr'
+ if ( chunk_is_token(prev, CT_IF)
+ && chunk_is_token(pc, CT_QUALIFIER)
+ && chunk_is_str(pc, "constexpr", 9))
+ {
+ set_chunk_type(pc, CT_CONSTEXPR);
+ }
+ }
+
+ // Change get/set to CT_WORD if not followed by a brace open
+ if ( chunk_is_token(pc, CT_GETSET)
+ && next->type != CT_BRACE_OPEN)
+ {
+ if ( chunk_is_token(next, CT_SEMICOLON)
+ && ( chunk_is_token(prev, CT_BRACE_CLOSE)
+ || chunk_is_token(prev, CT_BRACE_OPEN)
+ || chunk_is_token(prev, CT_SEMICOLON)))
+ {
+ set_chunk_type(pc, CT_GETSET_EMPTY);
+ set_chunk_parent(next, CT_GETSET);
+ }
+ else
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+ }
+
+ /*
+ * Interface is only a keyword in MS land if followed by 'class' or 'struct'
+ * likewise, 'class' may be a member name in Java.
+ */
+ if ( chunk_is_token(pc, CT_CLASS)
+ && !CharTable::IsKw1(next->str[0]))
+ {
+ if ( chunk_is_not_token(next, CT_DC_MEMBER)
+ && chunk_is_not_token(next, CT_ATTRIBUTE)) // Issue #2570
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+ else if ( chunk_is_token(prev, CT_DC_MEMBER)
+ || chunk_is_token(prev, CT_TYPE))
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ else if (chunk_is_token(next, CT_DC_MEMBER))
+ {
+ chunk_t *next2 = chunk_get_next_nblank(next);
+
+ if ( chunk_is_token(next2, CT_INV) // CT_INV hasn't turned into CT_DESTRUCTOR just yet
+ || ( chunk_is_token(next2, CT_CLASS) // constructor isn't turned into CT_FUNC* just yet
+ && !strcmp(pc->text(), next2->text())))
+ {
+ set_chunk_type(pc, CT_TYPE);
+ }
+ }
+ }
+
+ /*
+ * Change item after operator (>=, ==, etc) to a CT_OPERATOR_VAL
+ * Usually the next item is part of the operator.
+ * In a few cases the next few tokens are part of it:
+ * operator + - common case
+ * operator >> - need to combine '>' and '>'
+ * operator ()
+ * operator [] - already converted to TSQUARE
+ * operator new []
+ * operator delete []
+ * operator const char *
+ * operator const B&
+ * operator std::allocator<U>
+ *
+ * In all cases except the last, this will put the entire operator value
+ * in one chunk.
+ */
+ if (chunk_is_token(pc, CT_OPERATOR))
+ {
+ chunk_t *tmp2 = chunk_get_next(next);
+
+ // Handle special case of () operator -- [] already handled
+ if (chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ chunk_t *tmp = chunk_get_next(next);
+
+ if (chunk_is_token(tmp, CT_PAREN_CLOSE))
+ {
+ next->str = "()";
+ set_chunk_type(next, CT_OPERATOR_VAL);
+ chunk_del(tmp);
+ next->orig_col_end += 1;
+ }
+ }
+ else if ( chunk_is_token(next, CT_ANGLE_CLOSE)
+ && chunk_is_token(tmp2, CT_ANGLE_CLOSE)
+ && tmp2->orig_col == next->orig_col_end)
+ {
+ next->str.append('>');
+ next->orig_col_end++;
+ set_chunk_type(next, CT_OPERATOR_VAL);
+ chunk_del(tmp2);
+ }
+ else if (next->flags.test(PCF_PUNCTUATOR))
+ {
+ set_chunk_type(next, CT_OPERATOR_VAL);
+ }
+ else
+ {
+ set_chunk_type(next, CT_TYPE);
+
+ /*
+ * Replace next with a collection of all tokens that are part of
+ * the type.
+ */
+ tmp2 = next;
+ chunk_t *tmp;
+
+ while ((tmp = chunk_get_next(tmp2)) != nullptr)
+ {
+ if ( tmp->type != CT_WORD
+ && tmp->type != CT_TYPE
+ && tmp->type != CT_QUALIFIER
+ && tmp->type != CT_STAR
+ && tmp->type != CT_CARET
+ && tmp->type != CT_AMP
+ && tmp->type != CT_TSQUARE)
+ {
+ break;
+ }
+ // Change tmp into a type so that space_needed() works right
+ make_type(tmp);
+ size_t num_sp = space_needed(tmp2, tmp);
+
+ while (num_sp-- > 0)
+ {
+ next->str.append(" ");
+ }
+ next->str.append(tmp->str);
+ tmp2 = tmp;
+ }
+
+ while ((tmp2 = chunk_get_next(next)) != tmp)
+ {
+ chunk_del(tmp2);
+ }
+ set_chunk_type(next, CT_OPERATOR_VAL);
+
+ next->orig_col_end = next->orig_col + next->len();
+ }
+ set_chunk_parent(next, CT_OPERATOR);
+
+ LOG_FMT(LOPERATOR, "%s(%d): %zu:%zu operator '%s'\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col, next->text());
+ }
+
+ // Change private, public, protected into either a qualifier or label
+ if (chunk_is_token(pc, CT_ACCESS))
+ {
+ // Handle Qt slots - maybe should just check for a CT_WORD?
+ if ( chunk_is_str(next, "slots", 5)
+ || chunk_is_str(next, "Q_SLOTS", 7))
+ {
+ chunk_t *tmp = chunk_get_next(next);
+
+ if (chunk_is_token(tmp, CT_COLON))
+ {
+ next = tmp;
+ }
+ }
+
+ if (chunk_is_token(next, CT_COLON))
+ {
+ set_chunk_type(next, CT_ACCESS_COLON);
+ chunk_t *tmp;
+
+ if ((tmp = chunk_get_next_ncnnl(next)) != nullptr)
+ {
+ chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
+ }
+ }
+ else
+ {
+ set_chunk_type(pc, ( chunk_is_str(pc, "signals", 7)
+ || chunk_is_str(pc, "Q_SIGNALS", 9))
+ ? CT_WORD : CT_QUALIFIER);
+ }
+ }
+
+ // Look for <newline> 'EXEC' 'SQL'
+ if ( ( chunk_is_str_case(pc, "EXEC", 4)
+ && chunk_is_str_case(next, "SQL", 3))
+ || ( (*pc->str.c_str() == '$')
+ && pc->type != CT_SQL_WORD
+ /* but avoid breaking tokenization for C# 6 interpolated strings. */
+ && ( !language_is_set(LANG_CS)
+ || ( chunk_is_token(pc, CT_STRING)
+ && (!pc->str.startswith("$\""))
+ && (!pc->str.startswith("$@\""))))))
+ {
+ chunk_t *tmp = chunk_get_prev(pc);
+
+ if (chunk_is_newline(tmp))
+ {
+ if (*pc->str.c_str() == '$')
+ {
+ set_chunk_type(pc, CT_SQL_EXEC);
+
+ if (pc->len() > 1)
+ {
+ // SPLIT OFF '$'
+ chunk_t nc;
+
+ nc = *pc;
+ pc->str.resize(1);
+ pc->orig_col_end = pc->orig_col + 1;
+
+ set_chunk_type(&nc, CT_SQL_WORD);
+ nc.str.pop_front();
+ nc.orig_col++;
+ nc.column++;
+ chunk_add_after(&nc, pc);
+
+ next = chunk_get_next(pc);
+ }
+ }
+ tmp = chunk_get_next(next);
+
+ if (chunk_is_str_case(tmp, "BEGIN", 5))
+ {
+ set_chunk_type(pc, CT_SQL_BEGIN);
+ }
+ else if (chunk_is_str_case(tmp, "END", 3))
+ {
+ set_chunk_type(pc, CT_SQL_END);
+ }
+ else
+ {
+ set_chunk_type(pc, CT_SQL_EXEC);
+ }
+
+ // Change words into CT_SQL_WORD until CT_SEMICOLON
+ while (tmp != nullptr)
+ {
+ if (chunk_is_token(tmp, CT_SEMICOLON))
+ {
+ break;
+ }
+
+ if ( (tmp->len() > 0)
+ && ( unc_isalpha(*tmp->str.c_str())
+ || (*tmp->str.c_str() == '$')))
+ {
+ set_chunk_type(tmp, CT_SQL_WORD);
+ }
+ tmp = chunk_get_next_ncnnl(tmp);
+ }
+ }
+ }
+
+ // handle MS abomination 'for each'
+ if ( chunk_is_token(pc, CT_FOR)
+ && chunk_is_str(next, "each", 4)
+ && (next == chunk_get_next(pc)))
+ {
+ // merge the two with a space between
+ pc->str.append(' ');
+ pc->str += next->str;
+ pc->orig_col_end = next->orig_col_end;
+ chunk_del(next);
+ next = chunk_get_next_ncnnl(pc);
+
+ // label the 'in'
+ if (chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ chunk_t *tmp = chunk_get_next_ncnnl(next);
+
+ while ( tmp != nullptr
+ && tmp->type != CT_PAREN_CLOSE)
+ {
+ if (chunk_is_str(tmp, "in", 2))
+ {
+ set_chunk_type(tmp, CT_IN);
+ break;
+ }
+ tmp = chunk_get_next_ncnnl(tmp);
+ }
+ }
+ }
+
+ /*
+ * ObjectiveC allows keywords to be used as identifiers in some situations
+ * This is a dirty hack to allow some of the more common situations.
+ */
+ if (language_is_set(LANG_OC))
+ {
+ if ( ( chunk_is_token(pc, CT_IF)
+ || chunk_is_token(pc, CT_FOR)
+ || chunk_is_token(pc, CT_WHILE))
+ && !chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ if ( chunk_is_token(pc, CT_DO)
+ && ( chunk_is_token(prev, CT_MINUS)
+ || chunk_is_token(next, CT_SQUARE_CLOSE)))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ // Fix self keyword back to word when mixing c++/objective-c
+ if ( chunk_is_token(pc, CT_THIS)
+ && !strcmp(pc->text(), "self")
+ && ( chunk_is_token(next, CT_COMMA)
+ || chunk_is_token(next, CT_PAREN_CLOSE)))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ // Fix self keyword back to word when mixing c++/objective-c
+ if ( chunk_is_token(pc, CT_THIS)
+ && !strcmp(pc->text(), "self")
+ && ( chunk_is_token(next, CT_COMMA)
+ || chunk_is_token(next, CT_PAREN_CLOSE)))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+ }
+
+ // Another hack to clean up more keyword abuse
+ if ( chunk_is_token(pc, CT_CLASS)
+ && ( chunk_is_token(prev, CT_DOT)
+ || chunk_is_token(next, CT_DOT)
+ || chunk_is_token(prev, CT_MEMBER) // Issue #3031
+ || chunk_is_token(next, CT_MEMBER)))
+ {
+ set_chunk_type(pc, CT_WORD);
+ }
+
+ // Detect Objective C class name
+ if ( chunk_is_token(pc, CT_OC_IMPL)
+ || chunk_is_token(pc, CT_OC_INTF)
+ || chunk_is_token(pc, CT_OC_PROTOCOL))
+ {
+ if (next->type != CT_PAREN_OPEN)
+ {
+ set_chunk_type(next, CT_OC_CLASS);
+ }
+ set_chunk_parent(next, pc->type);
+
+ chunk_t *tmp = chunk_get_next_ncnnl(next);
+
+ if (tmp != nullptr)
+ {
+ chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
+ }
+ tmp = chunk_get_next_type(pc, CT_OC_END, pc->level);
+
+ if (tmp != nullptr)
+ {
+ set_chunk_parent(tmp, pc->type);
+ }
+ }
+
+ if (chunk_is_token(pc, CT_OC_INTF))
+ {
+ chunk_t *tmp = chunk_get_next_ncnnl(pc, scope_e::PREPROC);
+
+ while ( tmp != nullptr
+ && tmp->type != CT_OC_END)
+ {
+ if (get_token_pattern_class(tmp->type) != pattern_class_e::NONE)
+ {
+ LOG_FMT(LOBJCWORD, "%s(%d): @interface %zu:%zu change '%s' (%s) to CT_WORD\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col, tmp->text(),
+ get_token_name(tmp->type));
+ set_chunk_type(tmp, CT_WORD);
+ }
+ tmp = chunk_get_next_ncnnl(tmp, scope_e::PREPROC);
+ }
+ }
+
+ /*
+ * Detect Objective-C categories and class extensions:
+ * @interface ClassName (CategoryName)
+ * @implementation ClassName (CategoryName)
+ * @interface ClassName ()
+ * @implementation ClassName ()
+ */
+ if ( ( get_chunk_parent_type(pc) == CT_OC_IMPL
+ || get_chunk_parent_type(pc) == CT_OC_INTF
+ || chunk_is_token(pc, CT_OC_CLASS))
+ && chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_parent(next, get_chunk_parent_type(pc));
+
+ chunk_t *tmp = chunk_get_next(next);
+
+ if ( tmp != nullptr
+ && tmp->next != nullptr)
+ {
+ if (chunk_is_token(tmp, CT_PAREN_CLOSE))
+ {
+ //set_chunk_type(tmp, CT_OC_CLASS_EXT);
+ set_chunk_parent(tmp, get_chunk_parent_type(pc));
+ }
+ else
+ {
+ set_chunk_type(tmp, CT_OC_CATEGORY);
+ set_chunk_parent(tmp, get_chunk_parent_type(pc));
+ }
+ }
+ tmp = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level);
+
+ if (tmp != nullptr)
+ {
+ set_chunk_parent(tmp, get_chunk_parent_type(pc));
+ }
+ }
+
+ /*
+ * Detect Objective C @property:
+ * @property NSString *stringProperty;
+ * @property(nonatomic, retain) NSMutableDictionary *shareWith;
+ */
+ if (chunk_is_token(pc, CT_OC_PROPERTY))
+ {
+ if (next->type != CT_PAREN_OPEN)
+ {
+ chunk_flags_set(next, PCF_STMT_START | PCF_EXPR_START);
+ }
+ else
+ {
+ cleanup_objc_property(pc);
+ }
+ }
+
+ /*
+ * Detect Objective C @selector:
+ * @selector(msgNameWithNoArg)
+ * @selector(msgNameWith1Arg:)
+ * @selector(msgNameWith2Args:arg2Name:)
+ */
+ if ( chunk_is_token(pc, CT_OC_SEL)
+ && chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_parent(next, pc->type);
+
+ chunk_t *tmp = chunk_get_next(next);
+
+ if (tmp != nullptr)
+ {
+ set_chunk_type(tmp, CT_OC_SEL_NAME);
+ set_chunk_parent(tmp, pc->type);
+
+ while ((tmp = chunk_get_next_ncnnl(tmp)) != nullptr)
+ {
+ if (chunk_is_token(tmp, CT_PAREN_CLOSE))
+ {
+ set_chunk_parent(tmp, CT_OC_SEL);
+ break;
+ }
+ set_chunk_type(tmp, CT_OC_SEL_NAME);
+ set_chunk_parent(tmp, pc->type);
+ }
+ }
+ }
+
+ // Handle special preprocessor junk
+ if (chunk_is_token(pc, CT_PREPROC))
+ {
+ set_chunk_parent(pc, next->type);
+ }
+
+ // Detect "pragma region" and "pragma endregion"
+ if ( chunk_is_token(pc, CT_PP_PRAGMA)
+ && chunk_is_token(next, CT_PREPROC_BODY))
+ {
+ if ( (strncmp(next->str.c_str(), "region", 6) == 0)
+ || (strncmp(next->str.c_str(), "endregion", 9) == 0))
+ // TODO: probably better use strncmp
+ {
+ set_chunk_type(pc, (*next->str.c_str() == 'r') ? CT_PP_REGION : CT_PP_ENDREGION);
+
+ set_chunk_parent(prev, pc->type);
+ }
+ }
+
+ // Change 'default(' into a sizeof-like statement
+ if ( language_is_set(LANG_CS)
+ && chunk_is_token(pc, CT_DEFAULT)
+ && chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_SIZEOF);
+ }
+
+ if ( chunk_is_token(pc, CT_UNSAFE)
+ && next->type != CT_BRACE_OPEN)
+ {
+ set_chunk_type(pc, CT_QUALIFIER);
+ }
+
+ if ( ( chunk_is_token(pc, CT_USING)
+ || ( chunk_is_token(pc, CT_TRY)
+ && language_is_set(LANG_JAVA)))
+ && chunk_is_token(next, CT_PAREN_OPEN))
+ {
+ set_chunk_type(pc, CT_USING_STMT);
+ }
+
+ // Add minimal support for C++0x rvalue references
+ if ( chunk_is_token(pc, CT_BOOL)
+ && language_is_set(LANG_CPP)
+ && chunk_is_str(pc, "&&", 2))
+ {
+ if (chunk_is_token(prev, CT_TYPE))
+ {
+ // Issue # 1002
+ if (!pc->flags.test(PCF_IN_TEMPLATE))
+ {
+ set_chunk_type(pc, CT_BYREF);
+ }
+ }
+ }
+
+ /*
+ * HACK: treat try followed by a colon as a qualifier to handle this:
+ * A::A(int) try : B() { } catch (...) { }
+ */
+ if ( chunk_is_token(pc, CT_TRY)
+ && chunk_is_str(pc, "try", 3)
+ && chunk_is_token(next, CT_COLON))
+ {
+ set_chunk_type(pc, CT_QUALIFIER);
+ }
+
+ /*
+ * If Java's 'synchronized' is in a method declaration, it should be
+ * a qualifier.
+ */
+ if ( language_is_set(LANG_JAVA)
+ && chunk_is_token(pc, CT_SYNCHRONIZED)
+ && next->type != CT_PAREN_OPEN)
+ {
+ set_chunk_type(pc, CT_QUALIFIER);
+ }
+
+ // change CT_DC_MEMBER + CT_FOR into CT_DC_MEMBER + CT_FUNC_CALL
+ if ( chunk_is_token(pc, CT_FOR)
+ && chunk_is_token(pc->prev, CT_DC_MEMBER))
+ {
+ set_chunk_type(pc, CT_FUNC_CALL);
+ }
+ // TODO: determine other stuff here
+
+ prev = pc;
+ pc = next;
+ next = chunk_get_next_ncnnl(pc);
+ }
+} // tokenize_cleanup
+
+
+bool invalid_open_angle_template(chunk_t *prev)
+{
+ if (prev == nullptr)
+ {
+ return(false);
+ }
+ // A template requires a word/type right before the open angle
+ return( prev->type != CT_WORD
+ && prev->type != CT_TYPE
+ && prev->type != CT_COMMA
+ && prev->type != CT_QUALIFIER
+ && prev->type != CT_OPERATOR_VAL
+ && get_chunk_parent_type(prev) != CT_OPERATOR);
+}
+
+
+static void check_template(chunk_t *start, bool in_type_cast)
+{
+ LOG_FMT(LTEMPL, "%s(%d): orig_line %zu, orig_col %zu:\n",
+ __func__, __LINE__, start->orig_line, start->orig_col);
+
+ chunk_t *prev = chunk_get_prev_ncnnl(start, scope_e::PREPROC);
+
+ if (prev == nullptr)
+ {
+ return;
+ }
+ chunk_t *end;
+ chunk_t *pc;
+
+ if (chunk_is_token(prev, CT_TEMPLATE))
+ {
+ LOG_FMT(LTEMPL, "%s(%d): CT_TEMPLATE:\n", __func__, __LINE__);
+
+ // We have: "template< ... >", which is a template declaration
+ size_t level = 1;
+ size_t parens = 0;
+
+ for (pc = chunk_get_next_ncnnl(start, scope_e::PREPROC);
+ pc != nullptr;
+ pc = chunk_get_next_ncnnl(pc, scope_e::PREPROC))
+ {
+ LOG_FMT(LTEMPL, "%s(%d): type is %s, level is %zu\n",
+ __func__, __LINE__, get_token_name(pc->type), level);
+
+ if ( (pc->str[0] == '>')
+ && (pc->len() > 1))
+ {
+ if (pc->str[1] == '=') // Issue #1462 and #2565
+ {
+ LOG_FMT(LTEMPL, "%s(%d): do not split '%s' at orig_line %zu, orig_col %zu\n",
+ __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+ }
+ else
+ {
+ LOG_FMT(LTEMPL, "%s(%d): {split '%s' at orig_line %zu, orig_col %zu}\n",
+ __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+ split_off_angle_close(pc);
+ }
+ }
+
+ if (chunk_is_token(pc, CT_DECLTYPE))
+ {
+ flag_cpp_decltype(pc);
+ }
+ else if (chunk_is_token(pc, CT_PAREN_OPEN))
+ {
+ ++parens;
+ }
+ else if (chunk_is_token(pc, CT_PAREN_CLOSE))
+ {
+ --parens;
+ }
+
+ if (parens == 0)
+ {
+ if (chunk_is_str(pc, "<", 1))
+ {
+ level++;
+ }
+ else if (chunk_is_str(pc, ">", 1))
+ {
+ if (level == 0)
+ {
+ fprintf(stderr, "%s(%d): level is ZERO, cannot be decremented, at line %zu, column %zu\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col);
+ log_flush(true);
+ exit(EX_SOFTWARE);
+ }
+ level--;
+
+ if (level == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ end = pc;
+ }
+ else
+ {
+ /*
+ * We may have something like "a< ... >", which is a template where
+ * '...' may consist of anything except a semicolon, unbalanced
+ * parens, or braces (with one exception being braced initializers
+ * embedded within decltypes).
+ *
+ * For example, braces may be encountered as such in the following
+ * snippet of valid C++ code:
+ *
+ * template<typename T,
+ * typename = enable_if_t<is_same<typename decay<T>::type,
+ * decltype (make_index_sequence<5> { })>::value>>
+ * void foo(T &&arg)
+ * {
+ *
+ * }
+ *
+ * Finally, if we are inside an 'if' statement and hit a CT_BOOL,
+ * then it isn't a template.
+ */
+
+ if (invalid_open_angle_template(prev))
+ {
+ LOG_FMT(LTEMPL, "%s(%d): - after type %s + ( - Not a template\n",
+ __func__, __LINE__, get_token_name(prev->type));
+ set_chunk_type(start, CT_COMPARE);
+ return;
+ }
+ LOG_FMT(LTEMPL, "%s(%d): - prev->type is %s -\n",
+ __func__, __LINE__, get_token_name(prev->type));
+
+ // Scan back and make sure we aren't inside square parenthesis
+ bool in_if = false;
+ bool hit_semicolon = false;
+ pc = start;
+
+ while ((pc = chunk_get_prev_ncnnl(pc, scope_e::PREPROC)) != nullptr)
+ {
+ if ( ( chunk_is_token(pc, CT_SEMICOLON)
+ && hit_semicolon)
+ || chunk_is_token(pc, CT_SQUARE_CLOSE))
+ {
+ break;
+ }
+
+ if (chunk_is_token(pc, CT_DECLTYPE))
+ {
+ flag_cpp_decltype(pc);
+ }
+
+ if (chunk_is_token(pc, CT_BRACE_OPEN))
+ {
+ if ( !pc->flags.test(PCF_IN_DECLTYPE)
+ || !detect_cpp_braced_init_list(pc->prev, pc))
+ {
+ break;
+ }
+ flag_cpp_braced_init_list(pc->prev, pc);
+ }
+
+ if ( chunk_is_token(pc, CT_BRACE_CLOSE)
+ && get_chunk_parent_type(pc) != CT_BRACED_INIT_LIST
+ && !pc->flags.test(PCF_IN_DECLTYPE))
+ {
+ break;
+ }
+
+ if ( chunk_is_token(pc, CT_SEMICOLON)
+ && !hit_semicolon)
+ {
+ hit_semicolon = true;
+ }
+
+ if ( ( ( chunk_is_token(pc, CT_IF)
+ || chunk_is_token(pc, CT_RETURN)
+ || chunk_is_token(pc, CT_WHILE)
+ || chunk_is_token(pc, CT_WHILE_OF_DO))
+ && !hit_semicolon)
+ || ( chunk_is_token(pc, CT_FOR)
+ && hit_semicolon))
+ {
+ in_if = true;
+ break;
+ }
+ }
+ /*
+ * Scan forward to the angle close
+ * If we have a comparison in there, then it can't be a template.
+ */
+ const int max_token_count = 1024;
+ c_token_t tokens[max_token_count];
+ size_t num_tokens = 1;
+
+ tokens[0] = CT_ANGLE_OPEN;
+
+ for (pc = chunk_get_next_ncnnl(start, scope_e::PREPROC);
+ pc != nullptr;
+ pc = chunk_get_next_ncnnl(pc, scope_e::PREPROC))
+ {
+ constexpr static auto LCURRENT = LTEMPL;
+
+ LOG_FMT(LTEMPL, "%s(%d): pc->orig_line is %zu, pc->orig_col is %zu, type is %s, num_tokens is %zu\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col, get_token_name(pc->type), num_tokens);
+
+ log_rule_B("tok_split_gte");
+
+ if (chunk_is_token(pc, CT_BRACE_OPEN)) // Issue #2886
+ {
+ // look for the closing brace
+ chunk_t *A = chunk_skip_to_match(pc);
+ LOG_FMT(LTEMPL, "%s(%d): A->orig_line is %zu, A->orig_col is %zu, type is %s\n",
+ __func__, __LINE__, A->orig_line, A->orig_col, get_token_name(A->type));
+ pc = chunk_get_next(A);
+ }
+
+ if ( (tokens[num_tokens - 1] == CT_ANGLE_OPEN)
+ && (pc->str[0] == '>')
+ && (pc->len() > 1)
+ && ( options::tok_split_gte()
+ || ( ( chunk_is_str(pc, ">>", 2)
+ || chunk_is_str(pc, ">>>", 3))
+ && ( num_tokens >= 2
+ || ( num_tokens >= 1
+ && in_type_cast)))))
+ {
+ LOG_FMT(LTEMPL, "%s(%d): {split '%s' at orig_line %zu, orig_col %zu}\n",
+ __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+
+ split_off_angle_close(pc);
+ }
+
+ if (chunk_is_str(pc, "<", 1))
+ {
+ if ( num_tokens > 0 && (tokens[num_tokens - 1] == CT_PAREN_OPEN)
+ && invalid_open_angle_template(pc->prev))
+ {
+ set_chunk_type(pc, CT_COMPARE); // Issue #3127
+ }
+ else
+ {
+ tokens[num_tokens] = CT_ANGLE_OPEN;
+ num_tokens++;
+ }
+ }
+ else if (chunk_is_str(pc, ">", 1))
+ {
+ if (num_tokens > 0 && (tokens[num_tokens - 1] == CT_PAREN_OPEN))
+ {
+ handle_double_angle_close(pc);
+ }
+ else if (--num_tokens <= 0)
+ {
+ break;
+ }
+ else if (tokens[num_tokens] != CT_ANGLE_OPEN)
+ {
+ break; // unbalanced parentheses
+ }
+ }
+ else if ( in_if
+ && ( chunk_is_token(pc, CT_BOOL)
+ || chunk_is_token(pc, CT_COMPARE)))
+ {
+ break;
+ }
+ else if (chunk_is_token(pc, CT_BRACE_OPEN))
+ {
+ if ( !pc->flags.test(PCF_IN_DECLTYPE)
+ || !detect_cpp_braced_init_list(pc->prev, pc))
+ {
+ break;
+ }
+ auto brace_open = chunk_get_next_ncnnl(pc);
+ auto brace_close = chunk_skip_to_match(brace_open);
+
+ set_chunk_parent(brace_open, CT_BRACED_INIT_LIST);
+ set_chunk_parent(brace_close, CT_BRACED_INIT_LIST);
+ }
+ else if ( chunk_is_token(pc, CT_BRACE_CLOSE)
+ && get_chunk_parent_type(pc) != CT_BRACED_INIT_LIST
+ && !pc->flags.test(PCF_IN_DECLTYPE))
+ {
+ break;
+ }
+ else if (chunk_is_token(pc, CT_SEMICOLON))
+ {
+ break;
+ }
+ else if (chunk_is_token(pc, CT_PAREN_OPEN))
+ {
+ if (num_tokens >= max_token_count - 1)
+ {
+ break;
+ }
+ tokens[num_tokens] = CT_PAREN_OPEN;
+ num_tokens++;
+ }
+ else if ( chunk_is_token(pc, CT_QUESTION) // Issue #2949
+ && language_is_set(LANG_CPP))
+ {
+ break;
+ }
+ else if (chunk_is_token(pc, CT_PAREN_CLOSE))
+ {
+ if (num_tokens == 0)
+ {
+ fprintf(stderr, "%s(%d): num_tokens is ZERO, cannot be decremented, at line %zu, column %zu\n",
+ __func__, __LINE__, pc->orig_line, pc->orig_col);
+ log_flush(true);
+ exit(EX_SOFTWARE);
+ }
+ num_tokens--;
+
+ if (tokens[num_tokens] != CT_PAREN_OPEN)
+ {
+ break; // unbalanced parentheses
+ }
+ }
+ }
+
+ end = pc;
+ }
+
+ if (chunk_is_token(end, CT_ANGLE_CLOSE))
+ {
+ pc = chunk_get_next_ncnnl(end, scope_e::PREPROC);
+
+ if ( pc == nullptr
+ || pc->type != CT_NUMBER)
+ {
+ LOG_FMT(LTEMPL, "%s(%d): Template detected\n", __func__, __LINE__);
+ LOG_FMT(LTEMPL, "%s(%d): from orig_line %zu, orig_col %zu\n",
+ __func__, __LINE__, start->orig_line, start->orig_col);
+ LOG_FMT(LTEMPL, "%s(%d): to orig_line %zu, orig_col %zu\n",
+ __func__, __LINE__, end->orig_line, end->orig_col);
+ set_chunk_parent(start, CT_TEMPLATE);
+
+ check_template_args(start, end);
+
+ set_chunk_parent(end, CT_TEMPLATE);
+ chunk_flags_set(end, PCF_IN_TEMPLATE);
+ return;
+ }
+ }
+ LOG_FMT(LTEMPL, "%s(%d): - Not a template: end = %s\n",
+ __func__, __LINE__, (end != nullptr) ? get_token_name(end->type) : "<null>");
+ set_chunk_type(start, CT_COMPARE);
+} // check_template
+
+
+static void check_template_arg(chunk_t *start, chunk_t *end)
+{
+ LOG_FMT(LTEMPL, "%s(%d): Template argument detected\n", __func__, __LINE__);
+ LOG_FMT(LTEMPL, "%s(%d): from orig_line %zu, orig_col %zu\n",
+ __func__, __LINE__, start->orig_line, start->orig_col);
+ LOG_FMT(LTEMPL, "%s(%d): to orig_line %zu, orig_col %zu\n",
+ __func__, __LINE__, end->orig_line, end->orig_col);
+
+ // Issue #1127
+ // MyFoo<mySize * 2> foo1;
+ // MyFoo<2*mySize * 2> foo1;
+ // Issue #1346
+ // use it as ONE line:
+ // typename std::enable_if<!std::is_void<T>::value,
+ // QVector<T> >::type dummy(const std::function<T*(const S&)>&
+ // pFunc, const QVector<S>& pItems)
+ // we need two runs
+ // 1. run to test if expression is numeric
+ bool expressionIsNumeric = false;
+ chunk_t *pc = start;
+
+ while (pc != end)
+ {
+ chunk_t *next = chunk_get_next_ncnnl(pc, scope_e::PREPROC);
+ // a test "if (next == nullptr)" is not necessary
+ chunk_flags_set(pc, PCF_IN_TEMPLATE);
+
+ if ( chunk_is_token(pc, CT_DECLTYPE)
+ || chunk_is_token(pc, CT_SIZEOF))
+ {
+ expressionIsNumeric = true;
+ break;
+ }
+
+ if (next->type != CT_PAREN_OPEN)
+ {
+ if ( chunk_is_token(pc, CT_NUMBER)
+ || chunk_is_token(pc, CT_ARITH)
+ || chunk_is_token(pc, CT_SHIFT))
+ {
+ expressionIsNumeric = true;
+ break;
+ }
+ }
+ pc = next;
+ }
+ LOG_FMT(LTEMPL, "%s(%d): expressionIsNumeric is %s\n",
+ __func__, __LINE__, expressionIsNumeric ? "TRUE" : "FALSE");
+
+ // 2. run to do the work
+ if (!expressionIsNumeric)
+ {
+ pc = start;
+
+ while (pc != end)
+ {
+ chunk_t *next = chunk_get_next_ncnnl(pc, scope_e::PREPROC);
+ // a test "if (next == nullptr)" is not necessary
+ chunk_flags_set(pc, PCF_IN_TEMPLATE);
+
+ chunk_t *prev = chunk_get_prev_ncnnl(pc, scope_e::PREPROC);
+ chunk_t *prev2 = chunk_get_prev_ncnnl(prev, scope_e::PREPROC);
+
+ if ( chunk_is_token(prev, CT_ELLIPSIS) // Issue #3309
+ && chunk_is_token(prev2, CT_TYPENAME))
+ {
+ set_chunk_type(pc, CT_PARAMETER_PACK);
+ }
+ else
+ {
+ make_type(pc);
+ }
+ pc = next;
+ }
+ }
+} // check_template_arg
+
+
+static void check_template_args(chunk_t *start, chunk_t *end)
+{
+ std::vector<c_token_t> tokens;
+
+ // Scan for commas
+ chunk_t *pc;
+
+ for (pc = chunk_get_next_ncnnl(start, scope_e::PREPROC);
+ pc != nullptr && pc != end;
+ pc = chunk_get_next_ncnnl(pc, scope_e::PREPROC))
+ {
+ switch (pc->type)
+ {
+ case CT_COMMA:
+
+ if (tokens.empty())
+ {
+ // Check current argument
+ check_template_args(start, pc);
+ start = pc;
+ }
+ break;
+
+ case CT_ANGLE_OPEN:
+ case CT_PAREN_OPEN:
+ tokens.push_back(pc->type);
+ break;
+
+ case CT_ANGLE_CLOSE:
+
+ if ( !tokens.empty()
+ && tokens.back() == CT_ANGLE_OPEN)
+ {
+ tokens.pop_back();
+ }
+ break;
+
+ case CT_PAREN_CLOSE:
+
+ if ( !tokens.empty()
+ && tokens.back() == CT_PAREN_OPEN)
+ {
+ tokens.pop_back();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Check whatever is left
+ check_template_arg(start, end);
+} // check_template_args
+
+
+static void cleanup_objc_property(chunk_t *start)
+{
+ assert(chunk_is_token(start, CT_OC_PROPERTY));
+
+ chunk_t *open_paren = chunk_get_next_type(start, CT_PAREN_OPEN, start->level);
+
+ if (open_paren == nullptr)
+ {
+ LOG_FMT(LTEMPL, "%s(%d): Property is not followed by openning paren\n", __func__, __LINE__);
+ return;
+ }
+ set_chunk_parent(open_paren, start->type);
+
+ chunk_t *tmp = chunk_get_next_type(start, CT_PAREN_CLOSE, start->level);
+
+ if (tmp != nullptr)
+ {
+ set_chunk_parent(tmp, start->type);
+ tmp = chunk_get_next_ncnnl(tmp);
+
+ if (tmp != nullptr)
+ {
+ chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
+
+ tmp = chunk_get_next_type(tmp, CT_SEMICOLON, start->level);
+
+ if (tmp != nullptr)
+ {
+ set_chunk_parent(tmp, start->type);
+ }
+ }
+ }
+ mark_selectors_in_property_with_open_paren(open_paren);
+ mark_attributes_in_property_with_open_paren(open_paren);
+}
+
+
+static void mark_selectors_in_property_with_open_paren(chunk_t *open_paren)
+{
+ assert(chunk_is_token(open_paren, CT_PAREN_OPEN));
+
+ chunk_t *tmp = open_paren;
+
+ while ( tmp != nullptr
+ && tmp->type != CT_PAREN_CLOSE)
+ {
+ if ( chunk_is_token(tmp, CT_WORD)
+ && ( chunk_is_str(tmp, "setter", 6)
+ || chunk_is_str(tmp, "getter", 6)))
+ {
+ tmp = tmp->next;
+
+ while ( tmp != nullptr
+ && tmp->type != CT_COMMA
+ && tmp->type != CT_PAREN_CLOSE)
+ {
+ if ( chunk_is_token(tmp, CT_WORD)
+ || chunk_is_str(tmp, ":", 1))
+ {
+ set_chunk_type(tmp, CT_OC_SEL_NAME);
+ }
+ tmp = tmp->next;
+ }
+ }
+ else
+ {
+ tmp = tmp->next;
+ }
+ }
+}
+
+
+static void mark_attributes_in_property_with_open_paren(chunk_t *open_paren)
+{
+ assert(chunk_is_token(open_paren, CT_PAREN_OPEN));
+
+ chunk_t *tmp = open_paren;
+
+ while ( tmp != nullptr
+ && tmp->type != CT_PAREN_CLOSE)
+ {
+ if ( ( chunk_is_token(tmp, CT_COMMA)
+ || chunk_is_token(tmp, CT_PAREN_OPEN))
+ && ( chunk_is_token(tmp->next, CT_WORD)
+ || chunk_is_token(tmp->next, CT_TYPE)))
+ {
+ set_chunk_type(tmp->next, CT_OC_PROPERTY_ATTR);
+ }
+ tmp = tmp->next;
+ }
+}