Skip to content

Commit

Permalink
CXX: Improve parsing of function-try-blocks and try/catch in general
Browse files Browse the repository at this point in the history
  • Loading branch information
pragmaware committed May 1, 2018
1 parent 576c750 commit 6ce94e0
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 78 deletions.
3 changes: 3 additions & 0 deletions Units/parser-cxx.r/function_try_block.d/args.ctags
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
--c++-kinds=+pflz
--fields=+SsKe
--fields-c++=+{captures}+{properties}
--sort=no
18 changes: 14 additions & 4 deletions Units/parser-cxx.r/function_try_block.d/expected.tags
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
S input.cxx /^struct S {$/;" s file:
m input.cxx /^ std::string m;$/;" m struct:S typeref:typename:std::string file:
S input.cxx /^ S(const std::string& arg) try : m(arg, 100) {$/;" f struct:S file:
f input.cxx /^int f(int n = 2) try {$/;" f typeref:typename:int
S input.cxx /^struct S {$/;" struct file: end:11
m input.cxx /^ std::string m;$/;" member struct:S typeref:typename:std::string file:
S input.cxx /^ S(const std::string& arg) try : m(arg, 100) {$/;" function struct:S file: signature:(const std::string & arg) end:10 properties:fntryblock
arg input.cxx /^ S(const std::string& arg) try : m(arg, 100) {$/;" parameter function:S::S typeref:typename:const std::string & file:
e input.cxx /^ } catch(const std::exception& e) {$/;" local function:S::S typeref:typename:const std::exception & file:
f input.cxx /^int f(int n = 2) try {$/;" function typeref:typename:int signature:(int n=2) end:20 properties:fntryblock
n input.cxx /^int f(int n = 2) try {$/;" parameter function:f typeref:typename:int file:
f2 input.cxx /^int f2() try {$/;" function typeref:typename:int signature:() end:28 properties:fntryblock
f2v1 input.cxx /^} catch(SomeKindOfException &f2v1)$/;" local function:f2 typeref:typename:SomeKindOfException & file:
f2v2 input.cxx /^} catch(SomeOtherKindOfException &f2v2)$/;" local function:f2 typeref:typename:SomeOtherKindOfException & file:
f3 input.cxx /^int f3()$/;" function typeref:typename:int signature:() end:38
f2v1 input.cxx /^ int f2v1 = 10;$/;" local function:f3 typeref:typename:int file:
f2v2 input.cxx /^ } catch(std::exception & f2v2)$/;" local function:f3 typeref:typename:std::exception & file:
f4 input.cxx /^auto f4() -> void try {$/;" function typeref:typename:void try signature:() end:43 properties:fntryblock
23 changes: 23 additions & 0 deletions Units/parser-cxx.r/function_try_block.d/input.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,26 @@ int f(int n = 2) try {
assert(n == 4);
return n;
}

int f2() try {
} catch(SomeKindOfException &f2v1)
{
} catch(SomeOtherKindOfException &f2v2)
{
} catch(...) {
}

int f3()
{
// This is NOT a function-try-block
try {
int f2v1 = 10;
} catch(std::exception & f2v2)
{
}
}

auto f4() -> void try {
} catch(...)
{
}
149 changes: 97 additions & 52 deletions parsers/cxx/cxx_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,22 +404,27 @@ bool cxxParserParseToEndOfQualifedName(void)
return true;
}


//
// Attach the current position of input file as "end" field of
// the specified tag in the cork queue
//
void cxxParserMarkEndLineForTagInCorkQueue(int iCorkQueueIndex)
void cxxParserSetEndLineForTagInCorkQueue(int iCorkQueueIndex,unsigned long lEndLine)
{
CXX_DEBUG_ASSERT(iCorkQueueIndex > CORK_NIL,"The cork queue index is not valid");

tagEntryInfo * tag = getEntryInCorkQueue (iCorkQueueIndex);

CXX_DEBUG_ASSERT(tag,"No tag entry in the cork queue");

tag->extensionFields.endLine = getInputLineNumber();
tag->extensionFields.endLine = lEndLine;
}

//
// Attach the current position of input file as "end" field of
// the specified tag in the cork queue
//
void cxxParserMarkEndLineForTagInCorkQueue(int iCorkQueueIndex)
{
cxxParserSetEndLineForTagInCorkQueue(iCorkQueueIndex,getInputLineNumber());
}


// Make sure that the token chain contains only the specified keyword and eventually
// the "const" or "volatile" type modifiers.
static void cxxParserCleanupEnumStructClassOrUnionPrefixChain(CXXKeyword eKeyword,CXXToken * pLastToken)
Expand Down Expand Up @@ -1617,76 +1622,114 @@ bool cxxParserParseAccessSpecifier(void)
return true;
}

bool cxxParserParseIfForWhileSwitch(void)
bool cxxParserParseIfForWhileSwitchCatchParenthesis(void)
{
CXX_DEBUG_ENTER();


CXX_DEBUG_ASSERT(
cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeKeyword),
"This function should be called only after encountering one of the keywords"
);

CXXKeyword eKeyword = g_cxx.pToken->eKeyword;

if(!cxxParserParseUpToOneOf(
CXXTokenTypeParenthesisChain | CXXTokenTypeSemicolon |
CXXTokenTypeOpeningBracket | CXXTokenTypeEOF,
false
))
{
CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch up to parenthesis");
CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch/catch up to parenthesis");
return false;
}

if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeEOF | CXXTokenTypeSemicolon))
if(cxxTokenTypeIsOneOf(
g_cxx.pToken,
CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket
))
{
CXX_DEBUG_LEAVE_TEXT("Found EOF/semicolon while parsing if/for/while/switch");
CXX_DEBUG_LEAVE_TEXT(
"Found EOF/semicolon/opening bracket while parsing if/for/while/switch/catch"
);
return true;
}

if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain))
{
// Extract variables from the parenthesis chain
// We handle only simple cases.
CXXTokenChain * pChain = g_cxx.pToken->pChain;
CXX_DEBUG_ASSERT(
cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain),
"Expected a parenthesis chain here"
);

CXX_DEBUG_PRINT("Found if/for/while/switch/catch parenthesis chain");

CXX_DEBUG_ASSERT(
pChain->iCount >= 2,
"The parenthesis chain must have initial and final parenthesis"
);
// Extract variables from the parenthesis chain

// Simple check for cases like if(a & b), if(a * b).
// If there is &, && or * then we expect there to be also a = or a ;.
if(
// & && * not present
!cxxTokenChainFirstTokenOfType(
CXXTokenChain * pChain = g_cxx.pToken->pChain;

CXX_DEBUG_ASSERT(
pChain->iCount >= 2,
"The parenthesis chain must have initial and final parenthesis"
);

// There are several constructs that can fool the parser here.
// The most notable ones are:
// if(a & b)
// if(a * b)
// if(a && b)
// Too bad that these constructs are also used to declare variables.

// catch() always contains variable declarations
bool bOkToExtractVariables = eKeyword == CXXKeywordCATCH;

if(!bOkToExtractVariables)
{
// Parenthesis contents start with a keyword. Things like:
// if(const std::exception & e)
// if(int i ...
bOkToExtractVariables = cxxTokenTypeIs(
cxxTokenChainAt(pChain,1),
CXXTokenTypeKeyword
);

if(!bOkToExtractVariables)
{
// If there is &, && or * then we expect there to be also a = or
// a semicolon that comes after it.
// This is not 100% foolproof but works most of the times.

CXXToken * pAndOrStar = cxxTokenChainFirstTokenOfType(
pChain,
CXXTokenTypeAnd | CXXTokenTypeMultipleAnds |
CXXTokenTypeStar
) ||
// or [=;] present
cxxTokenChainFirstTokenOfType(
pChain,
);

if(!pAndOrStar)
{
bOkToExtractVariables = true;
} else {
bOkToExtractVariables = cxxTokenChainNextTokenOfType(
pAndOrStar,
CXXTokenTypeAssignment | CXXTokenTypeSemicolon
)
)
{
// Kill the initial parenthesis
cxxTokenChainDestroyFirst(pChain);
// Fake the final semicolon
CXXToken * t = cxxTokenChainLast(pChain);
t->eType = CXXTokenTypeSemicolon;
vStringClear(t->pszWord);
vStringPut(t->pszWord,';');

// and extract variable declarations if possible
cxxParserExtractVariableDeclarations(pChain,0);
);
}
}

CXX_DEBUG_LEAVE_TEXT("Found if/for/while/switch parenthesis chain");
return true;
}

// must be opening bracket: parse it here.

bool bRet = cxxParserParseBlock(true);

if(bOkToExtractVariables)
{
// Kill the initial parenthesis
cxxTokenChainDestroyFirst(pChain);
// Fake the final semicolon
CXXToken * t = cxxTokenChainLast(pChain);
t->eType = CXXTokenTypeSemicolon;
vStringClear(t->pszWord);
vStringPut(t->pszWord,';');

// and extract variable declarations if possible
cxxParserExtractVariableDeclarations(pChain,0);
}

CXX_DEBUG_LEAVE();

return bRet;
return true;
}

static rescanReason cxxParserMain(const unsigned int passCount)
Expand Down Expand Up @@ -1836,6 +1879,8 @@ void cxxParserCleanup(langType language CTAGS_ATTR_UNUSED,bool initialized CTAGS
// The next line forces this function to be called only once
g_bFirstRun = true;

if(g_cxx.pUngetToken)
cxxTokenDestroy(g_cxx.pUngetToken);
if(g_cxx.pTokenChain)
cxxTokenChainDestroy(g_cxx.pTokenChain);
if(g_cxx.pTemplateTokenChain)
Expand Down
Loading

0 comments on commit 6ce94e0

Please sign in to comment.