diff options
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/tokenize_cleanup.cpp')
-rw-r--r-- | debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/tokenize_cleanup.cpp | 1223 |
1 files changed, 1223 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/tokenize_cleanup.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/tokenize_cleanup.cpp new file mode 100644 index 00000000..831e1aae --- /dev/null +++ b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/tokenize_cleanup.cpp @@ -0,0 +1,1223 @@ +/** + * @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, 2022 + * @license GPL v2+ + */ + +#include "tokenize_cleanup.h" + +#include "check_template.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; + + +/** + * Marks ObjC specific chunks in property declaration, by setting + * parent types and chunk types. + */ +static void cleanup_objc_property(Chunk *start); + + +/** + * Marks ObjC specific chunks in property 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 *open_paren); + + +/** + * Marks ObjC specific chunks in property 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 *open_paren); + + +void split_off_angle_close(Chunk *pc) +{ + const chunk_tag_t *ct = find_punctuator(pc->Text() + 1, cpd.lang_flags); + + if (ct == nullptr) + { + return; + } + Chunk nc = *pc; + + pc->Str().resize(1); + pc->SetOrigColEnd(pc->GetOrigCol() + 1); + pc->SetType(CT_ANGLE_CLOSE); + + nc.SetType(ct->type); + nc.Str().pop_front(); + nc.SetOrigCol(nc.GetOrigCol() + 1); + nc.SetColumn(nc.GetColumn() + 1); + nc.CopyAndAddAfter(pc); +} + + +void tokenize_trailing_return_types() +{ + // 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; + + for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl()) + { + char copy[1000]; + LOG_FMT(LNOTE, "%s(%d): orig line is %zu, orig col is %zu, Text() is '%s'\n", + __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->ElidedText(copy)); + + if ( pc->Is(CT_MEMBER) + && (strcmp(pc->Text(), "->") == 0)) + { + Chunk *tmp = pc->GetPrevNcNnl(); + Chunk *tmp_2; + Chunk *open_paren; + + if (tmp->Is(CT_QUALIFIER)) + { + // auto max(int a, int b) const -> int; + // auto f11() const -> bool; + tmp = tmp->GetPrevNcNnl(); + } + else if (tmp->Is(CT_NOEXCEPT)) + { + // noexcept is present + tmp_2 = tmp->GetPrevNcNnl(); + + if (tmp_2->Is(CT_QUALIFIER)) + { + // auto f12() const noexcept -> bool; + // auto f15() const noexcept -> bool = delete; + tmp = tmp_2->GetPrevNcNnl(); + } + else + { + // auto f02() noexcept -> bool; + // auto f05() noexcept -> bool = delete; + tmp = tmp_2; + } + } + else if (tmp->Is(CT_PAREN_CLOSE)) + { + open_paren = tmp->GetPrevType(CT_PAREN_OPEN, tmp->GetLevel()); + tmp = open_paren->GetPrevNcNnl(); + + if (tmp->Is(CT_NOEXCEPT)) + { + // noexcept is present + tmp_2 = tmp->GetPrevNcNnl(); + + if (tmp_2->Is(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 = tmp_2->GetPrevNcNnl(); + } + 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 (tmp->Is(CT_THROW)) + { + // throw is present + tmp_2 = tmp->GetPrevNcNnl(); + + if (tmp_2->Is(CT_QUALIFIER)) + { + // auto f23() const throw() -> bool; + // auto f24() const throw() -> bool = delete; + tmp = tmp_2->GetPrevNcNnl(); + } + 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 ( tmp->Is(CT_FPAREN_CLOSE) + && ( tmp->GetParentType() == CT_FUNC_PROTO + || tmp->GetParentType() == CT_FUNC_DEF)) + { + pc->SetType(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 *next = pc->GetNextNcNnl(); + + if (next->Is(CT_DECLTYPE)) + { + // TODO + } + else if (next->Is(CT_WORD)) + { + next->SetType(CT_TYPE); // Issue #3222 + next = next->GetNextNcNnl(); + + if (next->Is(CT_ARITH)) + { + if (next->GetStr()[0] == '*') + { + next->SetType(CT_PTR_TYPE); + } + else if (next->GetStr()[0] == '&') // Issue #3407 + { + next->SetType(CT_BYREF); + } + } + } + else + { + // TODO + } + } + } + } +} // tokenize_trailing_return_types + + +void tokenize_cleanup() +{ + LOG_FUNC_ENTRY(); + + Chunk *prev = Chunk::NullChunkPtr; + Chunk *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 *pc; + + for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl()) + { + if (pc->Is(CT_SQUARE_OPEN)) + { + next = pc->GetNextNcNnl(); + + if (next->Is(CT_SQUARE_CLOSE)) + { + // Change '[' + ']' into '[]' + pc->SetType(CT_TSQUARE); + pc->Str() = "[]"; + /* + * bug #664: The original m_origColEnd of CT_SQUARE_CLOSE is + * stored at m_origColEnd of CT_TSQUARE. + * pc->SetOrigColEnd(pc->GetOrigColEnd() + 1); + */ + pc->SetOrigColEnd(next->GetOrigColEnd()); + Chunk::Delete(next); + } + } + + if ( pc->Is(CT_SEMICOLON) + && pc->TestFlags(PCF_IN_PREPROC) + && !pc->GetNextNcNnl(E_Scope::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->GetOrigLine()); + } + } + + // change := to CT_SQL_ASSIGN Issue #527 + for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl()) + { + if (pc->Is(CT_COLON)) + { + next = pc->GetNextNcNnl(); + + if (next->Is(CT_ASSIGN)) + { + // Change ':' + '=' into ':=' + pc->SetType(CT_SQL_ASSIGN); + pc->Str() = ":="; + pc->SetOrigColEnd(next->GetOrigColEnd()); + Chunk::Delete(next); + } + } + } + + // We can handle everything else in the second pass + pc = Chunk::GetHead(); + next = pc->GetNextNcNnl(); + + while ( pc->IsNotNullChunk() + && next->IsNotNullChunk()) + { + if ( pc->Is(CT_DOT) + && language_is_set(LANG_ALLC)) + { + pc->SetType(CT_MEMBER); + } + + if ( pc->Is(CT_NULLCOND) + && language_is_set(LANG_CS)) + { + pc->SetType(CT_MEMBER); + } + + // Determine the version stuff (D only) + if (pc->Is(CT_D_VERSION)) + { + if (next->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_D_VERSION_IF); + } + else + { + if (next->IsNot(CT_ASSIGN)) + { + LOG_FMT(LERR, "%s(%d): %s:%zu: version: Unexpected token %s\n", + __func__, __LINE__, cpd.filename.c_str(), pc->GetOrigLine(), get_token_name(next->GetType())); + exit(EX_SOFTWARE); + } + pc->SetType(CT_WORD); + } + } + + // Determine the scope stuff (D only) + if (pc->Is(CT_D_SCOPE)) + { + if (next->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_D_SCOPE_IF); + } + else + { + pc->SetType(CT_TYPE); + } + } + + /* + * Change CT_BASE before CT_PAREN_OPEN to CT_WORD. + * public myclass() : base() {} + * -or- + * var x = (T)base.y; + */ + if ( pc->Is(CT_BASE) + && ( next->Is(CT_PAREN_OPEN) + || next->Is(CT_DOT))) + { + pc->SetType(CT_WORD); + } + + if ( pc->Is(CT_ENUM) + && ( next->Is(CT_STRUCT) + || next->Is(CT_CLASS))) + { + next->SetType(CT_ENUM_CLASS); + } + Chunk *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 (next_non_attr->Is(CT_WORD)) + { + if (pc->IsClassEnumStructOrUnion()) + { + next_non_attr->SetType(CT_TYPE); + } + + if (pc->Is(CT_WORD)) + { + pc->SetType(CT_TYPE); + } + } + + /* + * change extern to qualifier if extern isn't followed by a string or + * an open parenthesis + */ + if (pc->Is(CT_EXTERN)) + { + if (next->Is(CT_STRING)) + { + // Probably 'extern "C"' + } + else if (next->Is(CT_PAREN_OPEN)) + { + // Probably 'extern (C)' + } + else + { + // Something else followed by a open brace + Chunk *tmp = next->GetNextNcNnl(); + + if ( tmp->IsNullChunk() + || tmp->IsNot(CT_BRACE_OPEN)) + { + pc->SetType(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 ( (next->Is(CT_STAR)) + || ( language_is_set(LANG_CPP) + && (next->Is(CT_CARET))) + || ( language_is_set(LANG_CS | LANG_VALA) + && (next->Is(CT_QUESTION)) + && (strcmp(pc->Text(), "null") != 0))) + { + if ( pc->Is(CT_TYPE) + || pc->Is(CT_QUALIFIER) + || pc->Is(CT_PTR_TYPE)) + { + next->SetType(CT_PTR_TYPE); + } + } + + if ( pc->Is(CT_TYPE_CAST) + && next->Is(CT_ANGLE_OPEN)) + { + next->SetParentType(CT_TYPE_CAST); + in_type_cast = true; + } + + if (pc->Is(CT_DECLTYPE)) + { + flag_cpp_decltype(pc); + } + + // Change angle open/close to CT_COMPARE, if not a template thingy + if ( pc->Is(CT_ANGLE_OPEN) + && pc->GetParentType() != 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 + pc->SetType(CT_COMPARE); + } + } + + if ( pc->Is(CT_ANGLE_CLOSE) + && pc->GetParentType() != CT_TEMPLATE) + { + if (in_type_cast) + { + in_type_cast = false; + pc->SetParentType(CT_TYPE_CAST); + } + else + { + next = handle_double_angle_close(pc); + } + } + + if (language_is_set(LANG_D)) + { + // Check for the D string concat symbol '~' + if ( pc->Is(CT_INV) + && ( prev->Is(CT_STRING) + || prev->Is(CT_WORD) + || next->Is(CT_STRING))) + { + pc->SetType(CT_CONCAT); + } + + // Check for the D template symbol '!' (word + '!' + word or '(') + if ( pc->Is(CT_NOT) + && prev->Is(CT_WORD) + && ( next->Is(CT_PAREN_OPEN) + || next->Is(CT_WORD) + || next->Is(CT_TYPE) + || next->Is(CT_NUMBER) + || next->Is(CT_NUMBER_FP) + || next->Is(CT_STRING) + || next->Is(CT_STRING_MULTI))) + { + pc->SetType(CT_D_TEMPLATE); + } + + // handle "version(unittest) { }" vs "unittest { }" + if ( pc->Is(CT_UNITTEST) + && prev->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_WORD); + } + + // handle 'static if' and merge the tokens + if ( pc->Is(CT_IF) + && prev->IsString("static")) + { + // delete PREV and merge with IF + pc->Str().insert(0, ' '); + pc->Str().insert(0, prev->GetStr()); + pc->SetOrigCol(prev->GetOrigCol()); + pc->SetOrigLine(prev->GetOrigLine()); + Chunk *to_be_deleted = prev; + prev = prev->GetPrevNcNnl(); + + if (prev->IsNotNullChunk()) + { + Chunk::Delete(to_be_deleted); + } + } + } + + if (language_is_set(LANG_CPP)) + { + // Change Word before '::' into a type + if ( pc->Is(CT_WORD) + && next->Is(CT_DC_MEMBER)) + { + prev = pc->GetPrev(); + + if (prev->IsNullChunk()) // Issue #3010 + { + pc->SetType(CT_TYPE); + } + else + { + if (prev->Is(CT_COLON)) + { + // nothing to do + } + else + { + pc->SetType(CT_TYPE); + } + } + } + + // Set parent type for 'if constexpr' + if ( prev->Is(CT_IF) + && pc->Is(CT_QUALIFIER) + && pc->IsString("constexpr")) + { + pc->SetType(CT_CONSTEXPR); + } + } + + // Change get/set to CT_WORD if not followed by a brace open + if ( pc->Is(CT_GETSET) + && next->IsNot(CT_BRACE_OPEN)) + { + if ( next->Is(CT_SEMICOLON) + && ( prev->Is(CT_BRACE_CLOSE) + || prev->Is(CT_BRACE_OPEN) + || prev->Is(CT_SEMICOLON))) + { + pc->SetType(CT_GETSET_EMPTY); + next->SetParentType(CT_GETSET); + } + else + { + pc->SetType(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->Is(CT_CLASS) + && !CharTable::IsKw1(next->GetStr()[0])) + { + if ( next->IsNot(CT_DC_MEMBER) + && next->IsNot(CT_ATTRIBUTE)) // Issue #2570 + { + pc->SetType(CT_WORD); + } + else if ( prev->Is(CT_DC_MEMBER) + || prev->Is(CT_TYPE)) + { + pc->SetType(CT_TYPE); + } + else if (next->Is(CT_DC_MEMBER)) + { + Chunk *next2 = next->GetNextNcNnlNet(); + + if ( next2->Is(CT_INV) // CT_INV hasn't turned into CT_DESTRUCTOR just yet + || ( next2->Is(CT_CLASS) // constructor isn't turned into CT_FUNC* just yet + && !strcmp(pc->Text(), next2->Text()))) + { + pc->SetType(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 (pc->Is(CT_OPERATOR)) + { + Chunk *tmp2 = next->GetNext(); + + // Handle special case of () operator -- [] already handled + if (next->Is(CT_PAREN_OPEN)) + { + Chunk *tmp = next->GetNext(); + + if (tmp->Is(CT_PAREN_CLOSE)) + { + next->Str() = "()"; + next->SetType(CT_OPERATOR_VAL); + Chunk::Delete(tmp); + next->SetOrigColEnd(next->GetOrigColEnd() + 1); + } + } + else if ( next->Is(CT_ANGLE_CLOSE) + && tmp2->Is(CT_ANGLE_CLOSE) + && tmp2->GetOrigCol() == next->GetOrigColEnd()) + { + next->Str().append('>'); + next->SetOrigColEnd(next->GetOrigColEnd() + 1); + next->SetType(CT_OPERATOR_VAL); + Chunk::Delete(tmp2); + } + else if (next->TestFlags(PCF_PUNCTUATOR)) + { + next->SetType(CT_OPERATOR_VAL); + } + else + { + next->SetType(CT_TYPE); + + /* + * Replace next with a collection of all tokens that are part of + * the type. + */ + tmp2 = next; + Chunk *tmp; + + while ((tmp = tmp2->GetNext())->IsNotNullChunk()) + { + if ( tmp->IsNot(CT_WORD) + && tmp->IsNot(CT_TYPE) + && tmp->IsNot(CT_QUALIFIER) + && tmp->IsNot(CT_STAR) + && tmp->IsNot(CT_CARET) + && tmp->IsNot(CT_AMP) + && tmp->IsNot(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->GetStr()); + tmp2 = tmp; + } + + while ((tmp2 = next->GetNext()) != tmp) + { + Chunk::Delete(tmp2); + } + next->SetType(CT_OPERATOR_VAL); + + next->SetOrigColEnd(next->GetOrigCol() + next->Len()); + } + next->SetParentType(CT_OPERATOR); + + LOG_FMT(LOPERATOR, "%s(%d): %zu:%zu operator '%s'\n", + __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), next->Text()); + } + + // Change private, public, protected into either a qualifier or label + if (pc->Is(CT_ACCESS)) + { + // Handle Qt slots - maybe should just check for a CT_WORD? + if ( next->IsString("slots") + || next->IsString("Q_SLOTS")) + { + Chunk *tmp = next->GetNext(); + + if (tmp->Is(CT_COLON)) + { + next = tmp; + } + } + + if (next->Is(CT_COLON)) + { + next->SetType(CT_ACCESS_COLON); + Chunk *tmp; + + if ((tmp = next->GetNextNcNnl())->IsNotNullChunk()) + { + tmp->SetFlagBits(PCF_STMT_START | PCF_EXPR_START); + log_ruleStart("start statementi/ expression", tmp); + } + } + else + { + pc->SetType(( pc->IsString("signals") + || pc->IsString("Q_SIGNALS")) + ? CT_WORD : CT_QUALIFIER); + } + } + + // Look for <newline> 'EXEC' 'SQL' + if ( ( pc->IsString("EXEC", false) + && next->IsString("SQL", false)) + || ( (*pc->GetStr().c_str() == '$') + && pc->IsNot(CT_SQL_WORD) + /* but avoid breaking tokenization for C# 6 interpolated strings. */ + && ( !language_is_set(LANG_CS) + || ( pc->Is(CT_STRING) + && (!pc->GetStr().startswith("$\"")) + && (!pc->GetStr().startswith("$@\"")))))) + { + Chunk *tmp = pc->GetPrev(); + + if (tmp->IsNewline()) + { + if (*pc->GetStr().c_str() == '$') + { + pc->SetType(CT_SQL_EXEC); + + if (pc->Len() > 1) + { + // SPLIT OFF '$' + Chunk nc; + + nc = *pc; + pc->Str().resize(1); + pc->SetOrigColEnd(pc->GetOrigCol() + 1); + + nc.SetType(CT_SQL_WORD); + nc.Str().pop_front(); + nc.SetOrigCol(nc.GetOrigCol() + 1); + nc.SetColumn(nc.GetColumn() + 1); + nc.CopyAndAddAfter(pc); + + next = pc->GetNext(); + } + } + tmp = next->GetNext(); + + if (tmp->IsString("BEGIN", false)) + { + pc->SetType(CT_SQL_BEGIN); + } + else if (tmp->IsString("END", false)) + { + pc->SetType(CT_SQL_END); + } + else + { + pc->SetType(CT_SQL_EXEC); + } + + // Change words into CT_SQL_WORD until CT_SEMICOLON + while (tmp->IsNotNullChunk()) + { + if (tmp->Is(CT_SEMICOLON)) + { + break; + } + + if ( (tmp->Len() > 0) + && ( unc_isalpha(*tmp->GetStr().c_str()) + || (*tmp->GetStr().c_str() == '$'))) + { + tmp->SetType(CT_SQL_WORD); + } + tmp = tmp->GetNextNcNnl(); + } + } + } + + // handle MS abomination 'for each' + if ( pc->Is(CT_FOR) + && next->IsString("each") + && (next == pc->GetNext())) + { + // merge the two with a space between + pc->Str().append(' '); + pc->Str() += next->GetStr(); + pc->SetOrigColEnd(next->GetOrigColEnd()); + Chunk::Delete(next); + next = pc->GetNextNcNnl(); + + // label the 'in' + if (next->Is(CT_PAREN_OPEN)) + { + Chunk *tmp = next->GetNextNcNnl(); + + while ( tmp->IsNotNullChunk() + && tmp->IsNot(CT_PAREN_CLOSE)) + { + if (tmp->IsString("in")) + { + tmp->SetType(CT_IN); + break; + } + tmp = tmp->GetNextNcNnl(); + } + } + } + + /* + * 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 ( ( pc->Is(CT_IF) + || pc->Is(CT_FOR) + || pc->Is(CT_WHILE)) + && !next->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_WORD); + } + + if ( pc->Is(CT_DO) + && ( prev->Is(CT_MINUS) + || next->Is(CT_SQUARE_CLOSE))) + { + pc->SetType(CT_WORD); + } + + // Fix self keyword back to word when mixing c++/objective-c + if ( pc->Is(CT_THIS) + && !strcmp(pc->Text(), "self") + && ( next->Is(CT_COMMA) + || next->Is(CT_PAREN_CLOSE))) + { + pc->SetType(CT_WORD); + } + + // Fix self keyword back to word when mixing c++/objective-c + if ( pc->Is(CT_THIS) + && !strcmp(pc->Text(), "self") + && ( next->Is(CT_COMMA) + || next->Is(CT_PAREN_CLOSE))) + { + pc->SetType(CT_WORD); + } + } + + // Vala allows keywords to be used as identifiers + if (language_is_set(LANG_VALA)) + { + if ( find_keyword_type(pc->Text(), pc->Len()) != CT_WORD + && ( prev->Is(CT_DOT) + || next->Is(CT_DOT) + || prev->Is(CT_MEMBER) + || next->Is(CT_MEMBER) + || prev->Is(CT_TYPE))) + { + pc->SetType(CT_WORD); + } + } + + // Another hack to clean up more keyword abuse + if ( pc->Is(CT_CLASS) + && ( prev->Is(CT_DOT) + || next->Is(CT_DOT) + || prev->Is(CT_MEMBER) // Issue #3031 + || next->Is(CT_MEMBER))) + { + pc->SetType(CT_WORD); + } + + // Detect Objective C class name + if ( pc->Is(CT_OC_IMPL) + || pc->Is(CT_OC_INTF) + || pc->Is(CT_OC_PROTOCOL)) + { + if (next->IsNot(CT_PAREN_OPEN)) + { + next->SetType(CT_OC_CLASS); + } + next->SetParentType(pc->GetType()); + + Chunk *tmp = next->GetNextNcNnl(); + + if (tmp->IsNotNullChunk()) + { + tmp->SetFlagBits(PCF_STMT_START | PCF_EXPR_START); + log_ruleStart("start statementi/ expression", tmp); + } + tmp = pc->GetNextType(CT_OC_END, pc->GetLevel()); + + if (tmp->IsNotNullChunk()) + { + tmp->SetParentType(pc->GetType()); + } + } + + if (pc->Is(CT_OC_INTF)) + { + Chunk *tmp = pc->GetNextNcNnl(E_Scope::PREPROC); + + while ( tmp->IsNotNullChunk() + && tmp->IsNot(CT_OC_END)) + { + if (get_token_pattern_class(tmp->GetType()) != pattern_class_e::NONE) + { + LOG_FMT(LOBJCWORD, "%s(%d): @interface %zu:%zu change '%s' (%s) to CT_WORD\n", + __func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), tmp->Text(), + get_token_name(tmp->GetType())); + tmp->SetType(CT_WORD); + } + tmp = tmp->GetNextNcNnl(E_Scope::PREPROC); + } + } + + /* + * Detect Objective-C categories and class extensions: + * @interface ClassName (CategoryName) + * @implementation ClassName (CategoryName) + * @interface ClassName () + * @implementation ClassName () + */ + if ( ( pc->GetParentType() == CT_OC_IMPL + || pc->GetParentType() == CT_OC_INTF + || pc->Is(CT_OC_CLASS)) + && next->Is(CT_PAREN_OPEN)) + { + next->SetParentType(pc->GetParentType()); + + Chunk *tmp = next->GetNext(); + + if ( tmp->IsNotNullChunk() + && tmp->GetNext()->IsNotNullChunk()) + { + if (tmp->Is(CT_PAREN_CLOSE)) + { + //tmp->SetType(CT_OC_CLASS_EXT); + tmp->SetParentType(pc->GetParentType()); + } + else + { + tmp->SetType(CT_OC_CATEGORY); + tmp->SetParentType(pc->GetParentType()); + } + } + tmp = pc->GetNextType(CT_PAREN_CLOSE, pc->GetLevel()); + + if (tmp->IsNotNullChunk()) + { + tmp->SetParentType(pc->GetParentType()); + } + } + + /* + * Detect Objective C @property: + * @property NSString *stringProperty; + * @property(nonatomic, retain) NSMutableDictionary *shareWith; + */ + if (pc->Is(CT_OC_PROPERTY)) + { + if (next->IsNot(CT_PAREN_OPEN)) + { + next->SetFlagBits(PCF_STMT_START | PCF_EXPR_START); + log_ruleStart("start statement/ expression", next); + } + else + { + cleanup_objc_property(pc); + } + } + + /* + * Detect Objective C @selector: + * @selector(msgNameWithNoArg) + * @selector(msgNameWith1Arg:) + * @selector(msgNameWith2Args:arg2Name:) + */ + if ( pc->Is(CT_OC_SEL) + && next->Is(CT_PAREN_OPEN)) + { + next->SetParentType(pc->GetType()); + + Chunk *tmp = next->GetNext(); + + if (tmp->IsNotNullChunk()) + { + tmp->SetType(CT_OC_SEL_NAME); + tmp->SetParentType(pc->GetType()); + + while ((tmp = tmp->GetNextNcNnl())->IsNotNullChunk()) + { + if (tmp->Is(CT_PAREN_CLOSE)) + { + tmp->SetParentType(CT_OC_SEL); + break; + } + tmp->SetType(CT_OC_SEL_NAME); + tmp->SetParentType(pc->GetType()); + } + } + } + + // Handle special preprocessor junk + if (pc->Is(CT_PREPROC)) + { + pc->SetParentType(next->GetType()); + } + + // Detect "pragma region" and "pragma endregion" + if ( pc->Is(CT_PP_PRAGMA) + && next->Is(CT_PREPROC_BODY)) + { + if ( (strncmp(next->GetStr().c_str(), "region", 6) == 0) + || (strncmp(next->GetStr().c_str(), "endregion", 9) == 0)) + // TODO: probably better use strncmp + { + pc->SetType((*next->GetStr().c_str() == 'r') ? CT_PP_REGION : CT_PP_ENDREGION); + + prev->SetParentType(pc->GetType()); + } + } + + // Change 'default(' into a sizeof-like statement + if ( language_is_set(LANG_CS) + && pc->Is(CT_DEFAULT) + && next->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_SIZEOF); + } + + if ( pc->Is(CT_UNSAFE) + && next->IsNot(CT_BRACE_OPEN)) + { + pc->SetType(CT_QUALIFIER); + } + + if ( ( pc->Is(CT_USING) + || ( pc->Is(CT_TRY) + && language_is_set(LANG_JAVA))) + && next->Is(CT_PAREN_OPEN)) + { + pc->SetType(CT_USING_STMT); + } + + // Add minimal support for C++0x rvalue references + if ( pc->Is(CT_BOOL) + && language_is_set(LANG_CPP) + && pc->IsString("&&")) + { + if (prev->Is(CT_TYPE)) + { + // Issue # 1002 + if (!pc->TestFlags(PCF_IN_TEMPLATE)) + { + pc->SetType(CT_BYREF); + } + } + } + + /* + * HACK: treat try followed by a colon as a qualifier to handle this: + * A::A(int) try : B() { } catch (...) { } + */ + if ( pc->Is(CT_TRY) + && pc->IsString("try") + && next->Is(CT_COLON)) + { + pc->SetType(CT_QUALIFIER); + } + + /* + * If Java's 'synchronized' is in a method declaration, it should be + * a qualifier. + */ + if ( language_is_set(LANG_JAVA) + && pc->Is(CT_SYNCHRONIZED) + && next->IsNot(CT_PAREN_OPEN)) + { + pc->SetType(CT_QUALIFIER); + } + + // change CT_DC_MEMBER + CT_FOR into CT_DC_MEMBER + CT_FUNC_CALL + if ( pc->Is(CT_FOR) + && pc->GetPrev()->Is(CT_DC_MEMBER)) + { + pc->SetType(CT_FUNC_CALL); + } + // TODO: determine other stuff here + + prev = pc; + pc = next; + next = pc->GetNextNcNnl(); + } +} // tokenize_cleanup + + +static void cleanup_objc_property(Chunk *start) +{ + assert(start->Is(CT_OC_PROPERTY)); + + Chunk *open_paren = start->GetNextType(CT_PAREN_OPEN, start->GetLevel()); + + if (open_paren->IsNullChunk()) + { + LOG_FMT(LTEMPL, "%s(%d): Property is not followed by opening paren\n", __func__, __LINE__); + return; + } + open_paren->SetParentType(start->GetType()); + + Chunk *tmp = start->GetNextType(CT_PAREN_CLOSE, start->GetLevel()); + + if (tmp->IsNotNullChunk()) + { + tmp->SetParentType(start->GetType()); + tmp = tmp->GetNextNcNnl(); + + if (tmp->IsNotNullChunk()) + { + tmp->SetFlagBits(PCF_STMT_START | PCF_EXPR_START); + log_ruleStart("start statement/ expression", tmp); + + tmp = tmp->GetNextType(CT_SEMICOLON, start->GetLevel()); + + if (tmp->IsNotNullChunk()) + { + tmp->SetParentType(start->GetType()); + } + } + } + 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 *open_paren) +{ + assert(open_paren->Is(CT_PAREN_OPEN)); + + Chunk *tmp = open_paren; + + while (tmp->IsNot(CT_PAREN_CLOSE)) + { + if ( tmp->Is(CT_WORD) + && ( tmp->IsString("setter") + || tmp->IsString("getter"))) + { + tmp = tmp->GetNext(); + + while ( tmp->IsNotNullChunk() + && tmp->IsNot(CT_COMMA) + && tmp->IsNot(CT_PAREN_CLOSE)) + { + if ( tmp->Is(CT_WORD) + || tmp->IsString(":")) + { + tmp->SetType(CT_OC_SEL_NAME); + } + tmp = tmp->GetNext(); + } + } + else + { + tmp = tmp->GetNext(); + } + } +} + + +static void mark_attributes_in_property_with_open_paren(Chunk *open_paren) +{ + assert(open_paren->Is(CT_PAREN_OPEN)); + + Chunk *tmp = open_paren; + + while (tmp->IsNot(CT_PAREN_CLOSE)) + { + Chunk *next = tmp->GetNext(); + + if ( ( tmp->Is(CT_COMMA) + || tmp->Is(CT_PAREN_OPEN)) + && ( next->Is(CT_WORD) + || next->Is(CT_TYPE))) + { + next->SetType(CT_OC_PROPERTY_ATTR); + } + tmp = next; + } +} |