You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
8.7 KiB
346 lines
8.7 KiB
/**
|
|
* @file rewrite_infinite_loops.cpp
|
|
*
|
|
* @author Alex Henrie
|
|
* @license GPL v2+
|
|
*/
|
|
|
|
#include "rewrite_infinite_loops.h"
|
|
|
|
#include "chunk.h"
|
|
#include "newlines.h"
|
|
#include "uncrustify.h"
|
|
|
|
|
|
using namespace uncrustify;
|
|
|
|
|
|
static bool for_needs_rewrite(Chunk *pc, E_Token desired_type)
|
|
{
|
|
// The 'for' statement needs to be rewritten if `for(;;)` is not the
|
|
// preferred syntax for infinite loops and this 'for' is an infinite loop
|
|
// with no extra tokens (such as inline comments).
|
|
|
|
if (desired_type == CT_FOR)
|
|
{
|
|
return(false);
|
|
}
|
|
pc = pc->GetNext();
|
|
|
|
if (!pc->Is(CT_SPAREN_OPEN))
|
|
{
|
|
return(false);
|
|
}
|
|
pc = pc->GetNext();
|
|
|
|
if (!pc->Is(CT_SEMICOLON))
|
|
{
|
|
return(false);
|
|
}
|
|
pc = pc->GetNext();
|
|
|
|
if (!pc->Is(CT_SEMICOLON))
|
|
{
|
|
return(false);
|
|
}
|
|
pc = pc->GetNext();
|
|
|
|
if (!pc->Is(CT_SPAREN_CLOSE))
|
|
{
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
|
|
static bool while_needs_rewrite(Chunk *keyword, E_Token desired_type, const char *desired_condition)
|
|
{
|
|
// The 'while' statement needs to be rewritten if it has only the tokens that
|
|
// are strictly necessary (keyword, condition, two parentheses, and semicolon
|
|
// if do-while) and either the keyword or the condition needs to be changed.
|
|
|
|
Chunk *oparen = keyword->GetNext();
|
|
Chunk *condition = oparen->GetNext();
|
|
Chunk *cparen = condition->GetNext();
|
|
|
|
if (!oparen->Is(CT_SPAREN_OPEN))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
if ( strcmp(condition->Text(), "true") != 0
|
|
&& strcmp(condition->Text(), "1") != 0)
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
if (!cparen->Is(CT_SPAREN_CLOSE))
|
|
{
|
|
return(false);
|
|
}
|
|
|
|
if (keyword->Is(CT_WHILE_OF_DO))
|
|
{
|
|
Chunk *semicolon = cparen->GetNext();
|
|
|
|
if (!semicolon->Is(CT_SEMICOLON))
|
|
{
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
if (!keyword->Is(desired_type))
|
|
{
|
|
return(true);
|
|
}
|
|
|
|
if ( strcmp(condition->Text(), "true") == 0
|
|
&& strcmp(desired_condition, "true") != 0)
|
|
{
|
|
return(true);
|
|
}
|
|
|
|
if ( strcmp(condition->Text(), "1") == 0
|
|
&& strcmp(desired_condition, "1") != 0)
|
|
{
|
|
return(true);
|
|
}
|
|
return(false);
|
|
} // while_needs_rewrite
|
|
|
|
|
|
void rewrite_loop_keyword(Chunk *keyword, E_Token new_type)
|
|
{
|
|
keyword->SetType(new_type);
|
|
|
|
switch (new_type)
|
|
{
|
|
case CT_DO:
|
|
keyword->SetOrigColEnd(keyword->GetOrigColEnd() + strlen("do") - keyword->Len());
|
|
keyword->Str() = "do";
|
|
break;
|
|
|
|
case CT_WHILE:
|
|
case CT_WHILE_OF_DO:
|
|
keyword->SetOrigColEnd(keyword->GetOrigColEnd() + strlen("while") - keyword->Len());
|
|
keyword->Str() = "while";
|
|
break;
|
|
|
|
case CT_FOR:
|
|
keyword->SetOrigColEnd(keyword->GetOrigColEnd() + strlen("for") - keyword->Len());
|
|
keyword->Str() = "for";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void move_one_token(Chunk * &source, Chunk * &destination, E_Token parent_type)
|
|
{
|
|
Chunk *next_source = source->GetNext();
|
|
|
|
// Place the source token immediately after the destination token, without
|
|
// any whitespace.
|
|
|
|
source->MoveAfter(destination);
|
|
source->SetColumn(destination->GetColumn() + destination->Len());
|
|
source->SetOrigCol(destination->GetOrigCol() + destination->Len());
|
|
source->SetOrigColEnd(source->GetOrigColEnd() + source->Len());
|
|
source->SetOrigPrevSp(0);
|
|
source->SetParentType(parent_type);
|
|
|
|
destination = source;
|
|
source = next_source;
|
|
}
|
|
|
|
|
|
static void rewrite_loop_condition(Chunk * &source, Chunk * &destination,
|
|
E_Token desired_type, const char *desired_condition)
|
|
{
|
|
// Move the opening parenthesis
|
|
move_one_token(source, destination, desired_type);
|
|
|
|
// Move the condition
|
|
if (desired_type == CT_FOR)
|
|
{
|
|
source->SetType(CT_SEMICOLON);
|
|
source->SetParentType(CT_FOR);
|
|
source->Str() = ";";
|
|
move_one_token(source, destination, desired_type);
|
|
destination = (destination)->CopyAndAddAfter(destination);
|
|
}
|
|
else
|
|
{
|
|
source->SetType(CT_WORD);
|
|
source->Str() = desired_condition;
|
|
move_one_token(source, destination, desired_type);
|
|
}
|
|
|
|
// If converting a 'for' to a 'while', delete the second semicolon
|
|
if (source->Is(CT_SEMICOLON))
|
|
{
|
|
Chunk *next_source = source->GetNext();
|
|
Chunk::Delete(source);
|
|
source = next_source;
|
|
}
|
|
// Move the closing parenthesis
|
|
move_one_token(source, destination, desired_type);
|
|
}
|
|
|
|
|
|
void rewrite_loop_in_place(Chunk *keyword, E_Token desired_type, const char *desired_condition)
|
|
{
|
|
Chunk *top = keyword->GetNext();
|
|
Chunk *bottom = keyword;
|
|
|
|
rewrite_loop_keyword(keyword, desired_type);
|
|
rewrite_loop_condition(top, bottom, desired_type, desired_condition);
|
|
}
|
|
|
|
|
|
static Chunk *find_start_brace(Chunk *pc)
|
|
{
|
|
while (!pc->IsBraceOpen())
|
|
{
|
|
pc = pc->GetNextNcNnl();
|
|
}
|
|
return(pc);
|
|
}
|
|
|
|
|
|
void rewrite_infinite_loops()
|
|
{
|
|
LOG_FUNC_ENTRY();
|
|
|
|
E_Token desired_type;
|
|
const char *desired_condition;
|
|
|
|
switch (options::mod_infinite_loop())
|
|
{
|
|
case 1: // for(;;)
|
|
desired_type = CT_FOR;
|
|
desired_condition = nullptr;
|
|
break;
|
|
|
|
case 2: // while(true)
|
|
desired_type = CT_WHILE;
|
|
desired_condition = "true";
|
|
break;
|
|
|
|
case 3: // do...while(true)
|
|
desired_type = CT_WHILE_OF_DO;
|
|
desired_condition = "true";
|
|
break;
|
|
|
|
case 4: // while(1)
|
|
desired_type = CT_WHILE;
|
|
desired_condition = "1";
|
|
break;
|
|
|
|
case 5: // do...while(1)
|
|
desired_type = CT_WHILE_OF_DO;
|
|
desired_condition = "1";
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
for (Chunk *pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl())
|
|
{
|
|
if (pc->Is(CT_DO))
|
|
{
|
|
Chunk *start_brace = find_start_brace(pc);
|
|
Chunk *end_brace = start_brace->GetClosingParen();
|
|
Chunk *while_keyword = end_brace->GetNextNcNnl();
|
|
|
|
if ( !while_keyword->Is(CT_WHILE_OF_DO)
|
|
|| !while_needs_rewrite(while_keyword, desired_type, desired_condition))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (desired_type == CT_WHILE_OF_DO)
|
|
{
|
|
// Change the loop condition
|
|
rewrite_loop_in_place(while_keyword, desired_type, desired_condition);
|
|
|
|
// Update the braces' parent types
|
|
start_brace->SetParentType(CT_DO);
|
|
end_brace->SetParentType(CT_DO);
|
|
}
|
|
else
|
|
{
|
|
Chunk *top = pc;
|
|
Chunk *bottom = while_keyword->GetNext();
|
|
|
|
// Change the 'do' at the top of the loop to a 'for' or a 'while'
|
|
rewrite_loop_keyword(top, desired_type);
|
|
|
|
// Delete the 'while' at the bottom of the loop
|
|
Chunk::Delete(while_keyword);
|
|
|
|
// Move the rest of the tokens from the bottom to the top
|
|
rewrite_loop_condition(bottom, top, desired_type, desired_condition);
|
|
|
|
// Delete the final semicolon
|
|
Chunk::Delete(bottom);
|
|
|
|
// Update the braces' parent types
|
|
start_brace->SetParentType(desired_type);
|
|
end_brace->SetParentType(desired_type);
|
|
}
|
|
}
|
|
else if ( ( pc->Is(CT_WHILE)
|
|
&& while_needs_rewrite(pc, desired_type, desired_condition))
|
|
|| ( pc->Is(CT_FOR)
|
|
&& for_needs_rewrite(pc, desired_type)))
|
|
{
|
|
Chunk *start_brace = find_start_brace(pc);
|
|
Chunk *end_brace = start_brace->GetClosingParen();
|
|
|
|
if (desired_type == CT_WHILE_OF_DO)
|
|
{
|
|
Chunk *top = pc;
|
|
Chunk *bottom = end_brace;
|
|
|
|
if (bottom->Is(CT_VBRACE_CLOSE))
|
|
{
|
|
// Insert a new line before the new 'while' keyword
|
|
newline_add_before(bottom);
|
|
}
|
|
// Add a 'while' at the bottom of the loop
|
|
bottom = top->CopyAndAddAfter(bottom);
|
|
rewrite_loop_keyword(bottom, CT_WHILE_OF_DO);
|
|
|
|
// Change the 'while' at the top of the loop to a 'do'
|
|
rewrite_loop_keyword(top, CT_DO);
|
|
top = top->GetNext();
|
|
|
|
// Move the tokens from the top to the bottom
|
|
rewrite_loop_condition(top, bottom, desired_type, desired_condition);
|
|
|
|
// Add the final semicolon
|
|
bottom = bottom->CopyAndAddAfter(bottom);
|
|
bottom->SetType(CT_SEMICOLON);
|
|
bottom->Str() = ";";
|
|
|
|
// Update the braces' parent types
|
|
start_brace->SetParentType(CT_DO);
|
|
end_brace->SetParentType(CT_DO);
|
|
}
|
|
else
|
|
{
|
|
// Change 'for' to 'while' or vice-versa
|
|
rewrite_loop_in_place(pc, desired_type, desired_condition);
|
|
|
|
// Update the braces' parent types
|
|
start_brace->SetParentType(desired_type);
|
|
end_brace->SetParentType(desired_type);
|
|
}
|
|
}
|
|
}
|
|
} // rewrite_infinite_loops
|