Skip to content

Commit

Permalink
Merge pull request #2406 from guwirth/pp-refactoring-14
Browse files Browse the repository at this point in the history
support Feature Checking Macros
  • Loading branch information
guwirth authored Jul 26, 2022
2 parents c5229cd + d94614f commit c910f95
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,14 @@ private BigInteger evalLeaf(AstNode exprAst) {
if (!macroEvaluationStack.contains(id)) {
PPMacro macro = pp.getMacro(id);
if (macro != null) {
macroEvaluationStack.push(id);
result = evalToInt(TokenUtils.merge(macro.replacementList), exprAst);
macroEvaluationStack.pop();
if (macro.replacementList.size() == 1 && macro.replacementList.get(0).getValue().equals(macro.identifier)) {
// special case, self-referencing macro, e.g. __has_include=__has_include
result = BigInteger.ONE;
} else {
macroEvaluationStack.push(id);
result = evalToInt(TokenUtils.merge(macro.replacementList), exprAst);
macroEvaluationStack.pop();
}
}
} else {
LOG.debug("preprocessor: self-referential macro '{}' detected;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
final class PPPredefinedMacros {

private static final String[] predefinedMacros = {
//
// C++
//
"__FILE__ \"file\"",
"__LINE__ 1",
// indicates 'date unknown'. should suffice
Expand All @@ -37,8 +40,23 @@ final class PPPredefinedMacros {
"__STDC_HOSTED__ 1",
// set C++14 as default
"__cplusplus 201402L",
//
// __has_include support (C++17)
"__has_include 1"
//
"__has_include __has_include", // define __has_include as macro, for e.g. #if __has_include
"__has_include_next __has_include_next", // define __has_include as macro, for e.g. #if __has_include
//
// source: https://clang.llvm.org/docs/LanguageExtensions.html
//
"__has_builtin(x) 0",
"__has_feature(x) 0",
"__has_extension(x) 0",
"__has_cpp_attribute(x) 0",
"__has_c_attribute(x) 0",
"__has_attribute(x) 0",
"__has_declspec_attribute(x) 0",
"__is_identifier(x) 1",
"__has_warning(x) 0"
};

private PPPredefinedMacros() {
Expand Down
43 changes: 21 additions & 22 deletions cxx-squid/src/main/java/org/sonar/cxx/preprocessor/PPReplace.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import com.sonar.cxx.sslr.impl.token.TokenUtils;
import java.util.ArrayList;
import java.util.List;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.cxx.parser.CxxKeyword;
import org.sonar.cxx.parser.CxxTokenType;

Expand All @@ -36,7 +34,6 @@
*/
class PPReplace {

private static final Logger LOG = Loggers.get(PPReplace.class);
private final CxxPreprocessor pp;

PPReplace(CxxPreprocessor pp) {
Expand Down Expand Up @@ -95,6 +92,7 @@ int replaceFunctionLikeMacro(PPMacro macro, List<Token> restTokens, List<Token>
* the replacement-list. The sequence is terminated by the matching ) token, skipping intervening matched pairs of
* left and right parentheses.
*/
@SuppressWarnings({"java:S3776", "java:S1541"})
private static int extractArguments(List<Token> tokens, List<Token> arguments) {
// argument list must start with '('
int size = tokens.size();
Expand Down Expand Up @@ -149,7 +147,6 @@ private static int extractArguments(List<Token> tokens, List<Token> arguments) {
}
}

LOG.error("preprocessor 'matchArguments' error, missing ')': {}", tokens.toString());
return 0;
}

Expand Down Expand Up @@ -177,6 +174,7 @@ private List<Token> replaceParams(PPMacro macro, List<Token> arguments) {
* identifiers (which are not macro-expanded first) and then concatenates the result.
*
*/
@SuppressWarnings({"java:S3776"})
private void handleOperators(List<Token> replacementList, List<String> parameters, List<Token> arguments,
List<Token> result) {

Expand All @@ -196,22 +194,20 @@ private void handleOperators(List<Token> replacementList, List<String> parameter
//
// not a token to be replaced by a macro argument
//
if ((i = handleVaOpt(view, parameters, arguments, result)) <= 0) {
if ((i = handleConcatenation(view, parameters, arguments, result)) <= 0) {
result.add(token);
}
if (((i = handleVaOpt(view, parameters, arguments, result)) <= 0)
&& ((i = handleConcatenation(view, parameters, arguments, result)) <= 0)) {
result.add(token);
}
} else if (parameterIndex < arguments.size()) {
//
// token to be replaced by a macro argument
//
argument = arguments.get(parameterIndex);

if ((i = handleConcatenation(view, parameters, arguments, result)) <= 0) {
if (tokensConsumed < 1 || !handleStringification(
replacementList.subList(tokensConsumed - 1, replacementList.size()), argument, result)) {
newValue = expand(argument.getValue());
}
if (((i = handleConcatenation(view, parameters, arguments, result)) <= 0)
&& (tokensConsumed < 1 || !handleStringification(
replacementList.subList(tokensConsumed - 1, replacementList.size()), argument, result))) {
newValue = expand(argument.getValue());
}
}

Expand Down Expand Up @@ -259,8 +255,8 @@ private static Token getReplacementToken(Token token, List<String> parameters, L
* (1) A ## ## B == A ## B
* (2) A ## B ## C ...
*/
private int handleConcatenation(List<Token> replacementList, List<String> parameters, List<Token> arguments,
List<Token> result) {
private static int handleConcatenation(List<Token> replacementList, List<String> parameters, List<Token> arguments,
List<Token> result) {

int tokensConsumed = 0;

Expand Down Expand Up @@ -288,7 +284,7 @@ && isIdentifier(replacementList.get(tokensConsumed).getType())
* In function-like macros, a # operator before an identifier in the argument-list runs the identifier through
* parameter argument and encloses the result in quotes, effectively creating a string literal.
*/
private boolean handleStringification(List<Token> replacementList, Token argument, List<Token> result) {
private static boolean handleStringification(List<Token> replacementList, Token argument, List<Token> result) {
if (PPPunctuator.HASH.equals(replacementList.get(0).getType())) {
result.set(result.size() - 1,
PPGeneratedToken.build(argument, argument.getType(),
Expand All @@ -307,7 +303,7 @@ private boolean handleStringification(List<Token> replacementList, Token argumen
* the ## does nothing when the variable arguments are present, but removes the comma when the variable arguments are
* not present: this makes it possible to define macros such as fprintf (stderr, format, ##__VA_ARGS__).
*/
private void handleEmptyVaArgs(List<Token> replacementList, List<Token> result) {
private static void handleEmptyVaArgs(List<Token> replacementList, List<Token> result) {
if (!"__VA_ARGS__".equals(replacementList.get(0).getValue())) {
return;
}
Expand All @@ -329,6 +325,8 @@ private void handleEmptyVaArgs(List<Token> replacementList, List<Token> result)
case COMMA: // (2)
result.remove(lastIndex);
break;
default:
break;
}
}
}
Expand All @@ -344,6 +342,7 @@ private void handleEmptyVaArgs(List<Token> replacementList, List<Token> result)
* __VA_OPT__ ( pp-tokensopt )
* </code>
*/
@SuppressWarnings({"java:S3776", "java:S1142"})
private int handleVaOpt(List<Token> replacementList, List<String> parameters, List<Token> arguments,
List<Token> result) {
var firstIndex = -1;
Expand All @@ -370,21 +369,21 @@ private int handleVaOpt(List<Token> replacementList, List<String> parameters, Li
}
}

int consumedTokens = 0;

if (firstIndex != -1 && lastIndex != -1) {
if (parameters.size() == arguments.size()) {
// __VA_OPT__ ( pp-tokensopt ), keep pp-tokensopt
var ppTokens = replacementList.subList(firstIndex + 1, lastIndex);
handleOperators(ppTokens, parameters, arguments, result);
return 2 + ppTokens.size();
consumedTokens = 2 + ppTokens.size();
} else {
// remove __VA_OPT__ ( pp-tokensopt )
return 1 + lastIndex - firstIndex;
consumedTokens = 1 + lastIndex - firstIndex;
}
}

LOG.error("preprocessor '__VA_OPT__* error: {}:{}",
replacementList.get(0).getLine(), replacementList.get(0).getColumn()); // todo
return 0;
return consumedTokens;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,127 @@ void has_include() {
.isEqualTo("r = 0 ; EOF");
}

@Test
void featureCheckingMacros() {
//
// source: https://clang.llvm.org/docs/LanguageExtensions.html
//
assertThat(parse(
"#ifdef __has_builtin\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_feature\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_extension\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_cpp_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_c_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_declspec_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __is_identifier\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_attribute\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_include\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_include_next\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");

assertThat(parse(
"#ifdef __has_warning\n"
+ "# define OK 1\n"
+ "#else\n"
+ "# define OK 0\n"
+ "#endif\n"
+ "r = OK;"))
.isEqualTo("r = 1 ; EOF");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,17 @@ void std_macro_evaluated_as_expected() {
assertThat(evaluate("__STDC__")).isTrue();
assertThat(evaluate("__STDC_HOSTED__")).isTrue();
assertThat(evaluate("__cplusplus")).isTrue();
assertThat(evaluate("__has_builtin")).isFalse();
assertThat(evaluate("__has_feature")).isFalse();
assertThat(evaluate("__has_extension")).isFalse();
assertThat(evaluate("__has_cpp_attribute")).isFalse();
assertThat(evaluate("__has_c_attribute")).isFalse();
assertThat(evaluate("__has_attribute")).isFalse();
assertThat(evaluate("__has_declspec_attribute")).isFalse();
assertThat(evaluate("__is_identifier")).isTrue();
assertThat(evaluate("__has_include")).isTrue();
assertThat(evaluate("__has_include_next")).isTrue();
assertThat(evaluate("__has_warning")).isFalse();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,24 @@ void testPredefinedMacroValues() {
"__STDC__",
"__STDC_HOSTED__",
"__cplusplus",
"__has_include"
// __has_include support (C++17)
"__has_include",
"__has_include_next",
// source: https://clang.llvm.org/docs/LanguageExtensions.html
"__has_builtin",
"__has_feature",
"__has_extension",
"__has_cpp_attribute",
"__has_c_attribute",
"__has_attribute",
"__has_declspec_attribute",
"__is_identifier",
"__has_warning"
);
String[] result = PPPredefinedMacros.predefinedMacroValues();
assertThat(result)
.hasSize(8)
.allMatch(s -> expResult.contains(s.split(" ")[0]));
.hasSize(18)
.allMatch(s -> expResult.contains(s.split("[^a-zA-Z_]")[0]));
}

}

0 comments on commit c910f95

Please sign in to comment.