| /** |
| * @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 |
| * @license GPL v2+ |
| */ |
| #include "uncrustify_types.h" |
| #include "prototypes.h" |
| #include "chunk_list.h" |
| #include "char_table.h" |
| #include "unc_ctype.h" |
| #include <cstring> |
| |
| static void check_template(chunk_t *start); |
| |
| |
| /** |
| * Convert '>' + '>' into '>>' |
| * If we only have a single '>', then change it to CT_COMPARE. |
| */ |
| static chunk_t *handle_double_angle_close(chunk_t *pc) |
| { |
| chunk_t *next = chunk_get_next(pc); |
| |
| if (next) |
| { |
| if ((pc->type == CT_ANGLE_CLOSE) && |
| (next->type == CT_ANGLE_CLOSE) && |
| (pc->parent_type == CT_NONE) && |
| ((pc->orig_col_end + 1) == next->orig_col) && |
| (next->parent_type == CT_NONE)) |
| { |
| pc->str.append('>'); |
| set_chunk_type(pc, CT_ARITH); |
| pc->orig_col_end = next->orig_col_end; |
| |
| chunk_t *tmp = chunk_get_next_ncnl(next); |
| chunk_del(next); |
| next = tmp; |
| } |
| else |
| { |
| set_chunk_type(pc, CT_COMPARE); |
| } |
| } |
| return(next); |
| } |
| |
| |
| static void split_off_angle_close(chunk_t *pc) |
| { |
| chunk_t nc; |
| |
| nc = *pc; |
| |
| const chunk_tag_t *ct; |
| |
| ct = find_punctuator(pc->text() + 1, cpd.lang_flags); |
| if (ct == NULL) |
| { |
| return; |
| } |
| |
| pc->str.resize(1); |
| pc->orig_col_end = pc->orig_col + 1; |
| set_chunk_type(pc, CT_ANGLE_CLOSE); |
| |
| nc.type = ct->type; |
| nc.str.pop_front(); |
| nc.orig_col++; |
| nc.column++; |
| chunk_add_after(&nc, pc); |
| } |
| |
| |
| void tokenize_cleanup(void) |
| { |
| chunk_t *pc = chunk_get_head(); |
| chunk_t *prev = NULL; |
| chunk_t *next; |
| chunk_t *tmp; |
| chunk_t *tmp2; |
| bool in_type_cast = false; |
| |
| /* Since [] is expected to be TSQUARE for the 'operator', we need to make |
| * this change in the first pass. |
| */ |
| for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnl(pc)) |
| { |
| if (pc->type == CT_SQUARE_OPEN) |
| { |
| next = chunk_get_next_ncnl(pc); |
| if (chunk_is_token(next, CT_SQUARE_CLOSE)) |
| { |
| /* Change '[' + ']' into '[]' */ |
| set_chunk_type(pc, CT_TSQUARE); |
| pc->str = "[]"; |
| chunk_del(next); |
| pc->orig_col_end += 1; |
| } |
| } |
| if ((pc->type == CT_SEMICOLON) && |
| (pc->flags & PCF_IN_PREPROC) && |
| !chunk_get_next_ncnl(pc, CNAV_PREPROC)) |
| { |
| LOG_FMT(LNOTE, "%s:%d Detected a macro that ends with a semicolon. Possible failures if used.\n", |
| cpd.filename, pc->orig_line); |
| } |
| } |
| |
| /* We can handle everything else in the second pass */ |
| pc = chunk_get_head(); |
| next = chunk_get_next_ncnl(pc); |
| while ((pc != NULL) && (next != NULL)) |
| { |
| if ((pc->type == CT_DOT) && ((cpd.lang_flags & LANG_ALLC) != 0)) |
| { |
| set_chunk_type(pc, CT_MEMBER); |
| } |
| |
| /* Determine the version stuff (D only) */ |
| if (pc->type == CT_D_VERSION) |
| { |
| if (next->type == CT_PAREN_OPEN) |
| { |
| set_chunk_type(pc, CT_D_VERSION_IF); |
| } |
| else |
| { |
| if (next->type != CT_ASSIGN) |
| { |
| LOG_FMT(LERR, "%s:%d %s: version: Unexpected token %s\n", |
| cpd.filename, pc->orig_line, __func__, get_token_name(next->type)); |
| cpd.error_count++; |
| } |
| set_chunk_type(pc, CT_WORD); |
| } |
| } |
| |
| /* Determine the scope stuff (D only) */ |
| if (pc->type == CT_D_SCOPE) |
| { |
| if (next->type == 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() { |
| * } |
| */ |
| if ((pc->type == CT_BASE) && (next->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| |
| if ((pc->type == CT_ENUM) && (next->type == CT_CLASS)) |
| { |
| set_chunk_type(next, CT_ENUM_CLASS); |
| } |
| |
| /** |
| * Change CT_WORD after CT_ENUM, CT_UNION, or CT_STRUCT to CT_TYPE |
| * Change CT_WORD before CT_WORD to CT_TYPE |
| */ |
| if (next->type == CT_WORD) |
| { |
| if ((pc->type == CT_ENUM) || |
| (pc->type == CT_ENUM_CLASS) || |
| (pc->type == CT_UNION) || |
| (pc->type == CT_STRUCT)) |
| { |
| set_chunk_type(next, CT_TYPE); |
| } |
| if (pc->type == CT_WORD) |
| { |
| set_chunk_type(pc, CT_TYPE); |
| } |
| } |
| |
| /* change extern to qualifier if extern isn't followed by a string or |
| * an open paren |
| */ |
| if (pc->type == CT_EXTERN) |
| { |
| if (next->type == CT_STRING) |
| { |
| /* Probably 'extern "C"' */ |
| } |
| else if (next->type == CT_PAREN_OPEN) |
| { |
| /* Probably 'extern (C)' */ |
| } |
| else |
| { |
| /* Something else followed by a open brace */ |
| tmp = chunk_get_next_ncnl(next); |
| if ((tmp == NULL) || (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. |
| */ |
| if ((next->type == CT_STAR) && |
| ((pc->type == CT_TYPE) || |
| (pc->type == CT_QUALIFIER) || |
| (pc->type == CT_PTR_TYPE))) |
| { |
| set_chunk_type(next, CT_PTR_TYPE); |
| } |
| |
| if ((pc->type == CT_TYPE_CAST) && |
| (next->type == CT_ANGLE_OPEN)) |
| { |
| set_chunk_parent(next, CT_TYPE_CAST); |
| in_type_cast = true; |
| } |
| |
| /** |
| * Change angle open/close to CT_COMPARE, if not a template thingy |
| */ |
| if ((pc->type == 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 (cpd.lang_flags & (LANG_CPP | LANG_CS | LANG_JAVA | LANG_VALA | LANG_OC)) |
| { |
| check_template(pc); |
| } |
| else |
| { |
| /* convert CT_ANGLE_OPEN to CT_COMPARE */ |
| set_chunk_type(pc, CT_COMPARE); |
| } |
| } |
| if ((pc->type == 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 ((cpd.lang_flags & LANG_D) != 0) |
| { |
| /* Check for the D string concat symbol '~' */ |
| if ((pc->type == CT_INV) && |
| ((prev->type == CT_STRING) || |
| (prev->type == CT_WORD) || |
| (next->type == CT_STRING))) |
| { |
| set_chunk_type(pc, CT_CONCAT); |
| } |
| |
| /* Check for the D template symbol '!' (word + '!' + word or '(') */ |
| if ((pc->type == CT_NOT) && |
| (prev->type == CT_WORD) && |
| ((next->type == CT_PAREN_OPEN) || |
| (next->type == CT_WORD) || |
| (next->type == CT_TYPE))) |
| { |
| set_chunk_type(pc, CT_D_TEMPLATE); |
| } |
| |
| /* handle "version(unittest) { }" vs "unittest { }" */ |
| if (prev && (pc->type == CT_UNITTEST) && (prev->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| |
| /* handle 'static if' and merge the tokens */ |
| if (prev && (pc->type == 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 *tmp = prev; |
| prev = chunk_get_prev_ncnl(prev); |
| chunk_del(tmp); |
| } |
| } |
| |
| if ((cpd.lang_flags & LANG_CPP) != 0) |
| { |
| /* Change Word before '::' into a type */ |
| if ((pc->type == CT_WORD) && (next->type == CT_DC_MEMBER)) |
| { |
| set_chunk_type(pc, CT_TYPE); |
| } |
| } |
| |
| /* Change get/set to CT_WORD if not followed by a brace open */ |
| if ((pc->type == CT_GETSET) && (next->type != CT_BRACE_OPEN)) |
| { |
| if ((next->type == CT_SEMICOLON) && |
| ((prev->type == CT_BRACE_CLOSE) || |
| (prev->type == CT_BRACE_OPEN) || |
| (prev->type == 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 ((pc->type == CT_CLASS) && !CharTable::IsKw1(next->str[0])) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| |
| /* 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 (pc->type == CT_OPERATOR) |
| { |
| tmp2 = chunk_get_next(next); |
| /* Handle special case of () operator -- [] already handled */ |
| if (next->type == CT_PAREN_OPEN) |
| { |
| tmp = chunk_get_next(next); |
| if ((tmp != NULL) && (tmp->type == CT_PAREN_CLOSE)) |
| { |
| next->str = "()"; |
| set_chunk_type(next, CT_OPERATOR_VAL); |
| chunk_del(tmp); |
| next->orig_col_end += 1; |
| } |
| } |
| else if ((next->type == CT_ANGLE_CLOSE) && |
| tmp2 && (tmp2->type == 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 & 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; |
| while ((tmp = chunk_get_next(tmp2)) != NULL) |
| { |
| if ((tmp->type != CT_WORD) && |
| (tmp->type != CT_TYPE) && |
| (tmp->type != CT_QUALIFIER) && |
| (tmp->type != CT_STAR) && |
| (tmp->type != CT_AMP) && |
| (tmp->type != CT_TSQUARE)) |
| { |
| break; |
| } |
| /* Change tmp into a type so that space_needed() works right */ |
| make_type(tmp); |
| int 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:%d operator '%s'\n", |
| __func__, pc->orig_line, pc->orig_col, next->str.c_str()); |
| } |
| |
| /* Change private, public, protected into either a qualifier or label */ |
| if (pc->type == CT_PRIVATE) |
| { |
| /* 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)) |
| { |
| tmp = chunk_get_next(next); |
| if ((tmp != NULL) && (tmp->type == CT_COLON)) |
| { |
| next = tmp; |
| } |
| } |
| if (next->type == CT_COLON) |
| { |
| set_chunk_type(next, CT_PRIVATE_COLON); |
| if ((tmp = chunk_get_next_ncnl(next)) != NULL) |
| { |
| tmp->flags |= 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(pc, "EXEC", 4) && chunk_is_str(next, "SQL", 3)) |
| { |
| tmp = chunk_get_prev(pc); |
| if (chunk_is_newline(tmp)) |
| { |
| 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 != NULL) |
| { |
| if (tmp->type == CT_SEMICOLON) |
| { |
| break; |
| } |
| if ((tmp->len() > 0) && unc_isalpha(*tmp->str)) |
| { |
| set_chunk_type(tmp, CT_SQL_WORD); |
| } |
| tmp = chunk_get_next_ncnl(tmp); |
| } |
| } |
| } |
| |
| /* handle MS abomination 'for each' */ |
| if ((pc->type == 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_ncnl(pc); |
| /* label the 'in' */ |
| if (next && (next->type == CT_PAREN_OPEN)) |
| { |
| tmp = chunk_get_next_ncnl(next); |
| while (tmp && (tmp->type != CT_PAREN_CLOSE)) |
| { |
| if (chunk_is_str(tmp, "in", 2)) |
| { |
| set_chunk_type(tmp, CT_IN); |
| break; |
| } |
| tmp = chunk_get_next_ncnl(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 (cpd.lang_flags & LANG_OC) |
| { |
| if (((pc->type == CT_IF) || |
| (pc->type == CT_FOR) || |
| (pc->type == CT_WHILE)) && |
| !chunk_is_token(next, CT_PAREN_OPEN)) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| if ((pc->type == CT_DO) && |
| (chunk_is_token(prev, CT_MINUS) || |
| chunk_is_token(next, CT_SQUARE_CLOSE))) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| } |
| |
| /* Another hack to clean up more keyword abuse */ |
| if ((pc->type == CT_CLASS) && |
| (chunk_is_token(prev, CT_DOT) || |
| chunk_is_token(next, CT_DOT))) |
| { |
| set_chunk_type(pc, CT_WORD); |
| } |
| |
| /* Detect Objective C class name */ |
| if ((pc->type == CT_OC_IMPL) || |
| (pc->type == CT_OC_INTF) || |
| (pc->type == CT_OC_PROTOCOL)) |
| { |
| if (next->type != CT_PAREN_OPEN) |
| { |
| set_chunk_type(next, CT_OC_CLASS); |
| } |
| set_chunk_parent(next, pc->type); |
| |
| tmp = chunk_get_next_ncnl(next); |
| if (tmp != NULL) |
| { |
| tmp->flags |= PCF_STMT_START | PCF_EXPR_START; |
| } |
| |
| tmp = chunk_get_next_type(pc, CT_OC_END, pc->level); |
| if (tmp != NULL) |
| { |
| set_chunk_parent(tmp, pc->type); |
| } |
| } |
| |
| if (pc->type == CT_OC_INTF) |
| { |
| tmp = chunk_get_next_ncnl(pc, CNAV_PREPROC); |
| while ((tmp != NULL) && (tmp->type != CT_OC_END)) |
| { |
| if (get_token_pattern_class(tmp->type) != PATCLS_NONE) |
| { |
| LOG_FMT(LOBJCWORD, "@interface %d:%d change '%s' (%s) to CT_WORD\n", |
| pc->orig_line, pc->orig_col, tmp->str.c_str(), |
| get_token_name(tmp->type)); |
| set_chunk_type(tmp, CT_WORD); |
| } |
| tmp = chunk_get_next_ncnl(tmp, CNAV_PREPROC); |
| } |
| } |
| |
| /* Detect Objective-C categories and class extensions */ |
| /* @interface ClassName (CategoryName) */ |
| /* @implementation ClassName (CategoryName) */ |
| /* @interface ClassName () */ |
| /* @implementation ClassName () */ |
| if (((pc->parent_type == CT_OC_IMPL) || |
| (pc->parent_type == CT_OC_INTF) || |
| (pc->type == CT_OC_CLASS)) && |
| (next->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_parent(next, pc->parent_type); |
| |
| tmp = chunk_get_next(next); |
| if ((tmp != NULL) && (tmp->next != NULL)) |
| { |
| if (tmp->type == CT_PAREN_CLOSE) |
| { |
| //set_chunk_type(tmp, CT_OC_CLASS_EXT); |
| set_chunk_parent(tmp, pc->parent_type); |
| } |
| else |
| { |
| set_chunk_type(tmp, CT_OC_CATEGORY); |
| set_chunk_parent(tmp, pc->parent_type); |
| } |
| } |
| |
| tmp = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level); |
| if (tmp != NULL) |
| { |
| set_chunk_parent(tmp, pc->parent_type); |
| } |
| } |
| |
| /* Detect Objective C @property |
| * @property NSString *stringProperty; |
| * @property(nonatomic, retain) NSMutableDictionary *shareWith; |
| */ |
| if (pc->type == CT_OC_PROPERTY) |
| { |
| if (next->type != CT_PAREN_OPEN) |
| { |
| next->flags |= PCF_STMT_START | PCF_EXPR_START; |
| } |
| else |
| { |
| set_chunk_parent(next, pc->type); |
| |
| tmp = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level); |
| if (tmp != NULL) |
| { |
| set_chunk_parent(tmp, pc->type); |
| tmp = chunk_get_next_ncnl(tmp); |
| if (tmp != NULL) |
| { |
| tmp->flags |= PCF_STMT_START | PCF_EXPR_START; |
| |
| tmp = chunk_get_next_type(tmp, CT_SEMICOLON, pc->level); |
| if (tmp != NULL) |
| { |
| set_chunk_parent(tmp, pc->type); |
| } |
| } |
| } |
| } |
| } |
| |
| /* Detect Objective C @selector |
| * @selector(msgNameWithNoArg) |
| * @selector(msgNameWith1Arg:) |
| * @selector(msgNameWith2Args:arg2Name:) |
| */ |
| if ((pc->type == CT_OC_SEL) && (next->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_parent(next, pc->type); |
| |
| tmp = chunk_get_next(next); |
| if (tmp != NULL) |
| { |
| set_chunk_type(tmp, CT_OC_SEL_NAME); |
| set_chunk_parent(tmp, pc->type); |
| |
| while ((tmp = chunk_get_next_ncnl(tmp)) != NULL) |
| { |
| if (tmp->type == 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 (pc->type == CT_PREPROC) |
| { |
| set_chunk_parent(pc, next->type); |
| } |
| |
| /* Detect "pragma region" and "pragma endregion" */ |
| if ((pc->type == CT_PP_PRAGMA) && (next->type == CT_PREPROC_BODY)) |
| { |
| if ((memcmp(next->str, "region", 6) == 0) || |
| (memcmp(next->str, "endregion", 9) == 0)) |
| { |
| set_chunk_type(pc, (*next->str == 'r') ? CT_PP_REGION : CT_PP_ENDREGION); |
| |
| set_chunk_parent(prev, pc->type); |
| } |
| } |
| |
| /* Check for C# nullable types '?' is in next */ |
| if ((cpd.lang_flags & LANG_CS) && |
| (next->type == CT_QUESTION) && |
| (next->orig_col == (pc->orig_col + pc->len()))) |
| { |
| tmp = chunk_get_next_ncnl(next); |
| if (tmp != NULL) |
| { |
| bool doit = ((tmp->type == CT_PAREN_CLOSE) || |
| (tmp->type == CT_ANGLE_CLOSE)); |
| |
| if (tmp->type == CT_WORD) |
| { |
| tmp2 = chunk_get_next_ncnl(tmp); |
| if ((tmp2 != NULL) && |
| ((tmp2->type == CT_SEMICOLON) || |
| (tmp2->type == CT_ASSIGN) || |
| (tmp2->type == CT_COMMA))) |
| { |
| doit = true; |
| } |
| } |
| |
| if (doit) |
| { |
| pc->str += next->str; |
| pc->orig_col_end = next->orig_col_end; |
| chunk_del(next); |
| next = tmp; |
| } |
| } |
| } |
| |
| /* Change 'default(' into a sizeof-like statement */ |
| if ((cpd.lang_flags & LANG_CS) && |
| (pc->type == CT_DEFAULT) && |
| (next->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_type(pc, CT_SIZEOF); |
| } |
| |
| if ((pc->type == CT_UNSAFE) && (next->type != CT_BRACE_OPEN)) |
| { |
| set_chunk_type(pc, CT_QUALIFIER); |
| } |
| |
| if (((pc->type == CT_USING) || |
| ((pc->type == CT_TRY) && (cpd.lang_flags & LANG_JAVA))) && |
| (next->type == CT_PAREN_OPEN)) |
| { |
| set_chunk_type(pc, CT_USING_STMT); |
| } |
| |
| /* Add minimal support for C++0x rvalue references */ |
| if ((pc->type == CT_BOOL) && chunk_is_str(pc, "&&", 2)) |
| { |
| if (prev->type == CT_TYPE) |
| { |
| 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 ((pc->type == CT_TRY) && chunk_is_str(pc, "try", 3) && |
| (next != NULL) && (next->type == CT_COLON)) |
| { |
| set_chunk_type(pc, CT_QUALIFIER); |
| } |
| |
| /* TODO: determine other stuff here */ |
| |
| prev = pc; |
| pc = next; |
| next = chunk_get_next_ncnl(pc); |
| } |
| } |
| |
| |
| /** |
| * If there is nothing but CT_WORD and CT_MEMBER, then it's probably a |
| * template thingy. Otherwise, it's likely a comparison. |
| */ |
| static void check_template(chunk_t *start) |
| { |
| chunk_t *pc; |
| chunk_t *end; |
| chunk_t *prev; |
| chunk_t *next; |
| bool in_if = false; |
| |
| LOG_FMT(LTEMPL, "%s: Line %d, col %d:", __func__, start->orig_line, start->orig_col); |
| |
| prev = chunk_get_prev_ncnl(start, CNAV_PREPROC); |
| if (prev == NULL) |
| { |
| return; |
| } |
| |
| if (prev->type == CT_TEMPLATE) |
| { |
| LOG_FMT(LTEMPL, " CT_TEMPLATE:"); |
| |
| /* We have: "template< ... >", which is a template declaration */ |
| int level = 1; |
| for (pc = chunk_get_next_ncnl(start, CNAV_PREPROC); |
| pc != NULL; |
| pc = chunk_get_next_ncnl(pc, CNAV_PREPROC)) |
| { |
| LOG_FMT(LTEMPL, " [%s,%d]", get_token_name(pc->type), level); |
| |
| if ((pc->str[0] == '>') && (pc->len() > 1)) |
| { |
| LOG_FMT(LTEMPL, " {split '%s' at %d:%d}", |
| pc->str.c_str(), pc->orig_line, pc->orig_col); |
| split_off_angle_close(pc); |
| } |
| |
| if (chunk_is_str(pc, "<", 1)) |
| { |
| level++; |
| } |
| else if (chunk_is_str(pc, ">", 1)) |
| { |
| level--; |
| if (level == 0) |
| { |
| break; |
| } |
| } |
| } |
| end = pc; |
| } |
| else |
| { |
| /* We may have something like "a< ... >", which is a template where |
| * '...' may consist of anything except braces {}, a semicolon, and |
| * unbalanced parens. |
| * if we are inside an 'if' statement and hit a CT_BOOL, then it isn't a |
| * template. |
| */ |
| |
| /* A template requires a word/type right before the open angle */ |
| if ((prev->type != CT_WORD) && |
| (prev->type != CT_TYPE) && |
| (prev->type != CT_COMMA) && |
| (prev->type != CT_OPERATOR_VAL) && |
| (prev->parent_type != CT_OPERATOR)) |
| { |
| LOG_FMT(LTEMPL, " - after %s + ( - Not a template\n", get_token_name(prev->type)); |
| set_chunk_type(start, CT_COMPARE); |
| return; |
| } |
| |
| LOG_FMT(LTEMPL, " - prev %s -", get_token_name(prev->type)); |
| |
| /* Scan back and make sure we aren't inside square parens */ |
| pc = start; |
| while ((pc = chunk_get_prev_ncnl(pc, CNAV_PREPROC)) != NULL) |
| { |
| if ((pc->type == CT_SEMICOLON) || |
| (pc->type == CT_BRACE_OPEN) || |
| (pc->type == CT_BRACE_CLOSE) || |
| (pc->type == CT_SQUARE_CLOSE)) |
| { |
| break; |
| } |
| if ((pc->type == CT_IF) || (pc->type == CT_RETURN)) |
| { |
| in_if = true; |
| break; |
| } |
| if (pc->type == CT_SQUARE_OPEN) |
| { |
| LOG_FMT(LTEMPL, " - Not a template: after a square open\n"); |
| set_chunk_type(start, CT_COMPARE); |
| return; |
| } |
| } |
| |
| /* Scan forward to the angle close |
| * If we have a comparison in there, then it can't be a template. |
| */ |
| c_token_t tokens[1024]; |
| int num_tokens = 1; |
| |
| tokens[0] = CT_ANGLE_OPEN; |
| for (pc = chunk_get_next_ncnl(start, CNAV_PREPROC); |
| pc != NULL; |
| pc = chunk_get_next_ncnl(pc, CNAV_PREPROC)) |
| { |
| LOG_FMT(LTEMPL, " [%s,%d]", get_token_name(pc->type), num_tokens); |
| |
| if ((tokens[num_tokens - 1] == CT_ANGLE_OPEN) && |
| (pc->str[0] == '>') && (pc->len() > 1) && |
| (cpd.settings[UO_tok_split_gte].b || chunk_is_str(pc, ">>", 2))) |
| { |
| LOG_FMT(LTEMPL, " {split '%s' at %d:%d}", |
| pc->str.c_str(), pc->orig_line, pc->orig_col); |
| split_off_angle_close(pc); |
| } |
| |
| if (chunk_is_str(pc, "<", 1)) |
| { |
| tokens[num_tokens++] = CT_ANGLE_OPEN; |
| } |
| 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) |
| { |
| /* unbalanced parens */ |
| break; |
| } |
| } |
| else if (in_if && |
| ((pc->type == CT_BOOL) || |
| (pc->type == CT_COMPARE))) |
| { |
| break; |
| } |
| else if ((pc->type == CT_BRACE_OPEN) || |
| (pc->type == CT_BRACE_CLOSE) || |
| (pc->type == CT_SEMICOLON)) |
| { |
| break; |
| } |
| else if (pc->type == CT_PAREN_OPEN) |
| { |
| if (num_tokens >= (int)(ARRAY_SIZE(tokens) - 1)) |
| { |
| break; |
| } |
| tokens[num_tokens++] = pc->type; |
| } |
| else if (pc->type == CT_PAREN_CLOSE) |
| { |
| num_tokens--; |
| if (tokens[num_tokens] != (pc->type - 1)) |
| { |
| /* unbalanced parens */ |
| break; |
| } |
| } |
| } |
| end = pc; |
| } |
| |
| if ((end != NULL) && (end->type == CT_ANGLE_CLOSE)) |
| { |
| pc = chunk_get_next_ncnl(end, CNAV_PREPROC); |
| if ((pc != NULL) && (pc->type != CT_NUMBER)) |
| { |
| LOG_FMT(LTEMPL, " - Template Detected\n"); |
| |
| set_chunk_parent(start, CT_TEMPLATE); |
| |
| pc = start; |
| while (pc != end) |
| { |
| next = chunk_get_next_ncnl(pc, CNAV_PREPROC); |
| pc->flags |= PCF_IN_TEMPLATE; |
| if (next->type != CT_PAREN_OPEN) |
| { |
| make_type(pc); |
| } |
| pc = next; |
| } |
| set_chunk_parent(end, CT_TEMPLATE); |
| end->flags |= PCF_IN_TEMPLATE; |
| return; |
| } |
| } |
| |
| LOG_FMT(LTEMPL, " - Not a template: end = %s\n", |
| (end != NULL) ? get_token_name(end->type) : "<null>"); |
| set_chunk_type(start, CT_COMPARE); |
| } |