Skip to content

Commit

Permalink
Merge pull request #1211 from guwirth/cpp17/lambda-capture-this
Browse files Browse the repository at this point in the history
C++17 lambda extensions
  • Loading branch information
guwirth authored Aug 24, 2017
2 parents 5071f0d + ac065b1 commit b023b2a
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 74 deletions.
3 changes: 1 addition & 2 deletions cxx-squid/dox/2016-11-28_cpp17_grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,7 @@ init-capture:
& identifier initializer

lambda-declarator:
( parameter-declaration-clause ) decl-specifier-seqopt
noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt
( parameter-declaration-clause ) decl-specifier-seqopt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

fold-expression:
( cast-expression fold-operator ... )
Expand Down
18 changes: 1 addition & 17 deletions cxx-squid/dox/grammar-diff.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,6 @@ user-defined-floating-literal:
hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-part ud-suffix
hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part ud-suffix

simple-capture:
identifier
& identifier
this
* this

lambda-declarator:
( parameter-declaration-clause ) decl-specifier-seqopt
noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

postfix-expression:
primary-expression
postfix-expression [ expr-or-braced-init-list ]
Expand Down Expand Up @@ -202,8 +192,7 @@ balanced-token:
any token other than a parenthesis, a bracket, or a brace

parameters-and-qualifiers:
( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt
( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt

trailing-return-type:
-> type-id
Expand All @@ -228,11 +217,6 @@ member-declaration:
deduction-guide:
explicitopt template-name ( parameter-declaration-clause ) -> simple-template-id ;

noexcept-specifier:
noexcept ( constant-expression )
noexcept
throw ( )

group-part:
if-section
control-line
Expand Down
2 changes: 1 addition & 1 deletion cxx-squid/src/main/java/org/sonar/cxx/api/CxxGrammar.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public class CxxGrammar extends Grammar {
public Rule handler;
public Rule exceptionDeclaration;
public Rule throwExpression;
public Rule exceptionSpecification;
public Rule noexceptSpecifier;
public Rule dynamicExceptionSpecification;
public Rule typeIdList;
public Rule noexceptSpecification;
Expand Down
76 changes: 44 additions & 32 deletions cxx-squid/src/main/java/org/sonar/cxx/parser/CxxGrammarImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public enum CxxGrammarImpl implements GrammarRuleKey {
captureDefault,
captureList,
capture,
simpleCapture,
initCapture,
lambdaDeclarator,
foldExpression,
foldOperator,
Expand Down Expand Up @@ -320,10 +322,8 @@ public enum CxxGrammarImpl implements GrammarRuleKey {
handler,
exceptionDeclaration,
throwExpression,
exceptionSpecification,
dynamicExceptionSpecification,
typeIdList,
noexceptSpecification;
noexceptSpecifier;

public static final Logger LOG = Loggers.get(CxxGrammarImpl.class);

Expand Down Expand Up @@ -456,25 +456,45 @@ private static void expressions(LexerfulGrammarBuilder b) {
b.rule(lambdaIntroducer).is("[", b.optional(lambdaCapture), "]"); // C++

b.rule(lambdaCapture).is(
b.firstOf( // todo ok but wrong order?
b.sequence(captureDefault, ",", captureList), // C++
captureList, // C++
captureDefault // C++
b.firstOf(
b.sequence(captureDefault, b.optional(b.sequence(",", captureList))), // C++
captureList // C++
)
);

b.rule(captureDefault).is(b.firstOf("&", "=")); // C++
b.rule(captureDefault).is(
b.sequence(b.firstOf("&", "="), b.nextNot(capture)) // C++
);

b.rule(captureList).is(b.sequence(capture, b.optional("...")), b.zeroOrMore(",", b.sequence(capture, b.optional("...")))); // C++
b.rule(captureList).is(
b.sequence(capture, b.optional("...")), b.zeroOrMore(",", b.sequence(capture, b.optional("..."))) // C++
);

b.rule(capture).is( // todo simple-capture, init-capture
b.rule(capture).is(
b.firstOf(
expression,
b.sequence("&", expression)
));
b.sequence(simpleCapture, b.nextNot("=")), // C++
initCapture // C++
)
);

b.rule(simpleCapture).is(
b.firstOf(
IDENTIFIER, // C++
b.sequence("&", IDENTIFIER), // C++
CxxKeyword.THIS, // C++
b.sequence("*", CxxKeyword.THIS) // C++
)
);

b.rule(initCapture).is(
b.firstOf(
b.sequence(IDENTIFIER, initializer), // C++
b.sequence("&", IDENTIFIER, initializer) // C++
)
);

b.rule(lambdaDeclarator).is(
"(", parameterDeclarationClause, ")", b.optional(CxxKeyword.MUTABLE), b.optional(exceptionSpecification), b.optional(attributeSpecifierSeq), b.optional(trailingReturnType) // C++
"(", parameterDeclarationClause, ")", b.optional(declSpecifierSeq), b.optional(noexceptSpecifier), b.optional(attributeSpecifierSeq), b.optional(trailingReturnType) // C++
);

b.rule(foldExpression).is(
Expand Down Expand Up @@ -1119,12 +1139,12 @@ private static void declarators(LexerfulGrammarBuilder b) {

b.rule(parametersAndQualifiers).is(
"(", parameterDeclarationClause, ")", // C++
b.optional(attributeSpecifierSeq), // C++ todo wrong position
b.optional(cvQualifierSeq), // C++
b.optional(cliFunctionModifiers), // C++/CLI
b.optional(refQualifier), // C++
b.optional(exceptionSpecification), // C++
b.optional(trailingReturnType) // todo wrong?
b.optional(attributeSpecifierSeq), // C++ todo wrong position
b.optional(cvQualifierSeq), // C++
b.optional(cliFunctionModifiers), // C++/CLI
b.optional(refQualifier), // C++
b.optional(noexceptSpecifier), // C++
b.optional(trailingReturnType) // todo wrong?
);

b.rule(trailingReturnType).is(
Expand Down Expand Up @@ -1756,29 +1776,21 @@ private static void exceptionHandling(LexerfulGrammarBuilder b) {
)
);

b.rule(exceptionSpecification).is(
b.rule(noexceptSpecifier).is(
b.firstOf(
dynamicExceptionSpecification, // C++
noexceptSpecification // C++
b.sequence(CxxKeyword.NOEXCEPT, "(", constantExpression, ")"), // C++
CxxKeyword.NOEXCEPT, // C++
b.sequence(CxxKeyword.THROW, "(", b.optional(typeIdList), ")") // C++ / Microsoft: typeIdList
)
);

b.rule(dynamicExceptionSpecification).is(CxxKeyword.THROW, "(", b.optional(typeIdList), ")"); // C++

b.rule(typeIdList).is(
b.firstOf(
b.sequence(typeId, b.optional("..."), b.zeroOrMore(",", typeId, b.optional("..."))), // C++
"..."// Microsoft extension
)
);

b.rule(noexceptSpecification).is(
b.firstOf(
b.sequence(CxxKeyword.NOEXCEPT, "(", constantExpression, ")"), // C++
CxxKeyword.NOEXCEPT // C++
)
);

}

private static void cliAttributes(LexerfulGrammarBuilder b) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ public void parametersAndQualifiers() {
mockRule(CxxGrammarImpl.attributeSpecifierSeq);
mockRule(CxxGrammarImpl.cvQualifierSeq);
mockRule(CxxGrammarImpl.refQualifier);
mockRule(CxxGrammarImpl.exceptionSpecification);
mockRule(CxxGrammarImpl.noexceptSpecifier);

assertThat(p).matches("( parameterDeclarationClause )");
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq");
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq cvQualifierSeq");
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq cvQualifierSeq refQualifier");
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq cvQualifierSeq refQualifier exceptionSpecification");
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq cvQualifierSeq refQualifier noexceptSpecifier");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void exceptionDeclaration() {

@Test
public void exceptionSpecification_reallife() {
p.setRootRule(g.rule(CxxGrammarImpl.exceptionSpecification));
p.setRootRule(g.rule(CxxGrammarImpl.noexceptSpecifier));

assertThat(p).matches("throw()");
assertThat(p).matches("throw(...)");
Expand All @@ -66,7 +66,7 @@ public void typeIdList() {

@Test
public void noexceptSpecification() {
p.setRootRule(g.rule(CxxGrammarImpl.noexceptSpecification));
p.setRootRule(g.rule(CxxGrammarImpl.noexceptSpecifier));

mockRule(CxxGrammarImpl.constantExpression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ public void lambdaExpression_reallife() {
assertThat(p).matches("[] ( ) { }");
assertThat(p).matches("[] ( ) mutable { }");
assertThat(p).matches("[] ( ) noexcept { }");
assertThat(p).matches("[] ( ) constexpr { }");
assertThat(p).matches("[] ( ) throw(X,Y) { }");
assertThat(p).matches("[] ( ) mutable noexcept { }");
assertThat(p).matches("[] ( ) mutable throw(X,Y) { }");
assertThat(p).matches("[] ( ) constexpr mutable { }");
assertThat(p).matches("[] (int n) { }");
assertThat(p).matches("[&] ( ) { }");
assertThat(p).matches("[&foo] (int n) { }");
Expand All @@ -61,6 +63,8 @@ public void lambdaExpression_reallife() {
assertThat(p).matches("[] () -> long long { return 1; }");
assertThat(p).matches("[] (const string& addr) { return addr.find( \".org\" ) != string::npos; }");
assertThat(p).matches("[this] () { cout << _x; }");
assertThat(p).matches("[*this] () { cout << _x; }");
assertThat(p).matches("[=, *this] { }");
assertThat(p).matches("[] (int x, int y) -> int { return x + y; }");
assertThat(p).matches("[](auto x, auto y) { return x + y; }");
assertThat(p).matches("[](const auto& m) { return m.size(); }");
Expand Down Expand Up @@ -109,49 +113,75 @@ public void captureDefault() {
assertThat(p).matches("=");
}

@Test
public void captureList() {
p.setRootRule(g.rule(CxxGrammarImpl.captureList));
mockRule(CxxGrammarImpl.capture);

assertThat(p).matches("capture"); // or 1, optional out
assertThat(p).matches("capture ..."); // or 1, optional in
assertThat(p).matches("capture , capture"); // or 1, optional out
assertThat(p).matches("capture , capture ..."); // or 1, optional in
}

@Test
public void capture() {
p.setRootRule(g.rule(CxxGrammarImpl.capture));
mockRule(CxxGrammarImpl.simpleCapture);
mockRule(CxxGrammarImpl.initCapture);

assertThat(p).matches("simpleCapture");
assertThat(p).matches("initCapture");
}

@Test
public void simpleCapture() {
p.setRootRule(g.rule(CxxGrammarImpl.simpleCapture));

assertThat(p).matches("foo");
assertThat(p).matches("&foo");
assertThat(p).matches("this");
assertThat(p).matches("value = 1");
assertThat(p).matches("u=move(u)");
assertThat(p).matches("value = std::move(ptr)");
assertThat(p).matches("&r = x, y = x+1");
assertThat(p).matches("*this");
}

@Test
public void captureList() {
p.setRootRule(g.rule(CxxGrammarImpl.captureList));
mockRule(CxxGrammarImpl.capture);
public void initCapture() {
p.setRootRule(g.rule(CxxGrammarImpl.initCapture));
mockRule(CxxGrammarImpl.initializer);

assertThat(p).matches("capture"); // or 1, optional out
assertThat(p).matches("capture ..."); // or 1, optional in
assertThat(p).matches("capture , capture"); // or 1, optional out
assertThat(p).matches("capture , capture ..."); // or 1, optional in
assertThat(p).matches("foo initializer");
assertThat(p).matches("&foo initializer");
}

@Test
public void initCapture_reallife() {
p.setRootRule(g.rule(CxxGrammarImpl.initCapture));

assertThat(p).matches("value = 1");
assertThat(p).matches("&r = x");
assertThat(p).matches("u = move(u)");
assertThat(p).matches("value = std::move(ptr)");
}

@Test
public void lambdaDeclarator() {
p.setRootRule(g.rule(CxxGrammarImpl.lambdaDeclarator));
mockRule(CxxGrammarImpl.parameterDeclarationClause);
mockRule(CxxGrammarImpl.exceptionSpecification);
mockRule(CxxGrammarImpl.noexceptSpecifier);
mockRule(CxxGrammarImpl.attributeSpecifierSeq);
mockRule(CxxGrammarImpl.trailingReturnType);

assertThat(p).matches("( parameterDeclarationClause ) "); // all opt out
assertThat(p).matches("( parameterDeclarationClause ) mutable"); // mutable in
assertThat(p).matches("( parameterDeclarationClause ) exceptionSpecification"); // exceptionSpecification in
assertThat(p).matches("( parameterDeclarationClause ) noexceptSpecifier"); // noexceptSpecifier in
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq"); // attributeSpecifierSeq in
assertThat(p).matches("( parameterDeclarationClause ) trailingReturnType"); // trailingReturnType in
assertThat(p).matches("( parameterDeclarationClause ) mutable exceptionSpecification"); // complex 1
assertThat(p).matches("( parameterDeclarationClause ) mutable exceptionSpecification attributeSpecifierSeq"); // complex 2
assertThat(p).matches("( parameterDeclarationClause ) mutable exceptionSpecification attributeSpecifierSeq trailingReturnType"); // complex
assertThat(p).matches("( parameterDeclarationClause ) mutable noexceptSpecifier"); // complex 1
assertThat(p).matches("( parameterDeclarationClause ) mutable noexceptSpecifier attributeSpecifierSeq"); // complex 2
assertThat(p).matches("( parameterDeclarationClause ) mutable noexceptSpecifier attributeSpecifierSeq trailingReturnType"); // complex
// 3
assertThat(p).matches("( parameterDeclarationClause ) exceptionSpecification attributeSpecifierSeq"); // complex 4
assertThat(p).matches("( parameterDeclarationClause ) exceptionSpecification attributeSpecifierSeq trailingReturnType"); // complex 5
assertThat(p).matches("( parameterDeclarationClause ) noexceptSpecifier attributeSpecifierSeq"); // complex 4
assertThat(p).matches("( parameterDeclarationClause ) noexceptSpecifier attributeSpecifierSeq trailingReturnType"); // complex 5
assertThat(p).matches("( parameterDeclarationClause ) attributeSpecifierSeq trailingReturnType"); // complex 6
}
}
14 changes: 14 additions & 0 deletions cxx-squid/src/test/resources/parser/own/C++17/constexpr-lambdas.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
auto add1 = [](int a, int b) constexpr { return a + b; };
int arr[add1(1, 2)];

auto monoid = [](auto v) { return [=] { return v; }; };
auto add2 = [](auto m1) constexpr {
auto ret = m1();
return [=](auto m2) mutable {
auto m1val = m1();
auto plus = [=](auto m2val) mutable constexpr
{ return m1val += m2val; };
ret = plus(m2());
return monoid(ret);
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
struct my_struct {
int x;
void value();
};

void my_struct::value() {
// Capture *this: This will allow the lambda expression to capture the enclosing object by copy.
// This will make possible to use safely the lambda expression even after the enclosing object has been destroyed.
[=, *this](){};
}

0 comments on commit b023b2a

Please sign in to comment.