diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java index 51994d1ce75..520c156573f 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java @@ -11541,5 +11541,26 @@ public class AST2CPPTests extends AST2TestBase { BindingAssertionHelper bh = getAssertionHelper(); ICPPVariable test = bh.assertNonProblemOnFirstIdentifier("test"); assertTrue(test.getType() instanceof IProblemType); // resolution is ambiguous + } + + // constexpr int lambdas_supported = + // #if __has_feature(cxx_lambdas) + // 1; + // #else + // 0; + // #endif + // + // constexpr int generic_lambdas_supported = + // #if __has_feature(cxx_generic_lambdas) + // 1; + // #else + // 0; + // #endif + public void testHasFeature_442325() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + helper.assertVariableValue("lambdas_supported", 1); + // Note: when support for generic lambdas is implemented, + // this test will need to be updated. + helper.assertVariableValue("generic_lambdas_supported", 0); } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java index 956894f3a16..d81d29936ea 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java @@ -778,9 +778,16 @@ public class AST2TestBase extends BaseTestCase { } public void assertVariableType(String variableName, IType expectedType) { - IVariable var = assertNonProblem(variableName, IVariable.class); + IVariable var = assertNonProblem(variableName); assertSameType(expectedType, var.getType()); } + + public void assertVariableValue(String variableName, long expectedValue) { + IVariable var = assertNonProblem(variableName); + assertNotNull(var.getInitialValue()); + assertNotNull(var.getInitialValue().numericalValue()); + assertEquals(expectedValue, var.getInitialValue().numericalValue().longValue()); + } public U assertType(T obj, Class... cs) { for (Class c : cs) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/Keywords.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/Keywords.java index c2b7cf67d0d..274dcc982f1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/Keywords.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/Keywords.java @@ -310,6 +310,8 @@ public class Keywords { public static final char[] cPRAGMA = "pragma".toCharArray(); public static final char[] cLINE = "line".toCharArray(); public static final char[] cDEFINED= "defined".toCharArray(); + /** @since 5.11 */ + public static final char[] c__HAS_FEATURE = "__has_feature".toCharArray(); /** @since 5.2*/ public static final char[] _Pragma= "_Pragma".toCharArray(); public static final char[] cVA_ARGS= "__VA_ARGS__".toCharArray(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 6b9617fc552..48dba5636cf 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -85,6 +85,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { public static final int tSPACE= IToken.FIRST_RESERVED_PREPROCESSOR + 3; public static final int tNOSPACE= IToken.FIRST_RESERVED_PREPROCESSOR + 4; public static final int tMACRO_PARAMETER= IToken.FIRST_RESERVED_PREPROCESSOR + 5; + public static final int t__HAS_FEATURE = IToken.FIRST_RESERVED_PREPROCESSOR + 6; private static final int ORIGIN_PREPROCESSOR_DIRECTIVE = OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE; private static final int ORIGIN_INACTIVE_CODE = OffsetLimitReachedException.ORIGIN_INACTIVE_CODE; @@ -108,7 +109,9 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final char[] ONCE = "once".toCharArray(); //$NON-NLS-1$ static final int NO_EXPANSION = 0x01; - static final int PROTECT_DEFINED = 0x02; + // Set in contexts where preprocessor intrinsics such as 'defined' + // or '__has_feature' need to be recognized. + static final int PROTECT_INTRINSICS = 0x02; static final int STOP_AT_NL = 0x04; static final int CHECK_NUMBERS = 0x08; static final int REPORT_SIGNIFICANT_MACROS = 0x10; @@ -224,6 +227,8 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { } } + private static Set sSupportedFeatures; + TokenSequence fInputToMacroExpansion= new TokenSequence(false); TokenSequence fLineInputToMacroExpansion= new TokenSequence(true); @@ -1883,7 +1888,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { scannerCtx.clearInactiveCodeMarkerToken(); int options= STOP_AT_NL | REPORT_SIGNIFICANT_MACROS; if (isCondition) - options |= PROTECT_DEFINED; + options |= PROTECT_INTRINSICS; loop: while (true) { Token t= internalFetchToken(scannerCtx, options, withinExpansion); @@ -1899,6 +1904,9 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { case tDEFINED: options |= NO_EXPANSION; break; + case t__HAS_FEATURE: + options |= NO_EXPANSION; + break; case IToken.tLPAREN: break; default: @@ -1988,9 +1996,15 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { boolean withinExpansion) throws OffsetLimitReachedException { final boolean reportSignificant = (options & REPORT_SIGNIFICANT_MACROS) != 0; final char[] name= identifier.getCharImage(); - if ((options & PROTECT_DEFINED) != 0 && CharArrayUtils.equals(name, Keywords.cDEFINED)) { - identifier.setType(tDEFINED); - return false; + if ((options & PROTECT_INTRINSICS) != 0) { + if (CharArrayUtils.equals(name, Keywords.cDEFINED)) { + identifier.setType(tDEFINED); + return false; + } + if (CharArrayUtils.equals(name, Keywords.c__HAS_FEATURE)) { + identifier.setType(t__HAS_FEATURE); + return false; + } } PreprocessorMacro macro= fMacroDictionary.get(name); if (macro == null) { @@ -2066,4 +2080,94 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { || CharArrayUtils.equals(__DATE__.getNameCharArray(), name) || CharArrayUtils.equals(__TIME__.getNameCharArray(), name); } + + @SuppressWarnings("nls") + public static Set getSupportedFeatures() { + if (sSupportedFeatures == null) { + sSupportedFeatures = new HashSet<>(); + + // C++98 features + sSupportedFeatures.add("cxx_exceptions"); + sSupportedFeatures.add("cxx_rtti"); + + // C++11 features + // missing: cxx_access_control_sfinae (needs to be tested) + sSupportedFeatures.add("cxx_alias_templates"); + sSupportedFeatures.add("cxx_alignas"); + sSupportedFeatures.add("cxx_alignof"); + sSupportedFeatures.add("cxx_attributes"); + sSupportedFeatures.add("cxx_auto_type"); + sSupportedFeatures.add("cxx_constexpr"); + sSupportedFeatures.add("cxx_decltype"); + // missing: cxx_decltype_incomplete_return_types (needs to be tested; see bug 324096) + sSupportedFeatures.add("cxx_default_function_template_args"); + sSupportedFeatures.add("cxx_defaulted_functions"); + sSupportedFeatures.add("cxx_delegating_constructors"); + sSupportedFeatures.add("cxx_explicit_conversions"); + sSupportedFeatures.add("cxx_generalized_initializers"); + // missing: cxx_implicit_moves (bug 327301) + sSupportedFeatures.add("cxx_inheriting_constructors"); + sSupportedFeatures.add("cxx_inline_namespaces"); + sSupportedFeatures.add("cxx_lambdas"); + sSupportedFeatures.add("cxx_local_type_template_args"); + sSupportedFeatures.add("cxx_noexcept"); + sSupportedFeatures.add("cxx_nonstatic_member_init"); + sSupportedFeatures.add("cxx_nullptr"); + sSupportedFeatures.add("cxx_override_control"); + sSupportedFeatures.add("cxx_range_for"); + sSupportedFeatures.add("cxx_raw_string_literals"); + sSupportedFeatures.add("cxx_reference_qualified_functions"); + sSupportedFeatures.add("cxx_rvalue_references"); + sSupportedFeatures.add("cxx_static_assert"); + sSupportedFeatures.add("cxx_strong_enums"); + sSupportedFeatures.add("cxx_thread_local"); + sSupportedFeatures.add("cxx_trailing_return"); + sSupportedFeatures.add("cxx_unicode_literals"); + // missing: cxx_unrestricted_unions (bug 327299) + sSupportedFeatures.add("cxx_user_literals"); + sSupportedFeatures.add("cxx_variadic_templates"); + + // C++14 features + // none supported yet + + // C11 features + sSupportedFeatures.add("c_alignas"); + sSupportedFeatures.add("c_alignof"); + // missing: c_atomic (bug 445297) + // missing: c_generic_selections (bug 445296) + // missing: c_static_assert (bug 445297) + // missing: c_thread_local (bug 445297) + + // Type trait primitives + // missing: has_nothrow_assign + // missing: has_nothrow_copy + // missing: has_nothrow_constructor + // missing: has_trivial_assign + sSupportedFeatures.add("has_trivial_copy"); + // missing: has_trivial_constructor + // missing: has_trivial_destructor + // missing: has_virtual_destructor + sSupportedFeatures.add("is_abstract"); + sSupportedFeatures.add("is_base_of"); + sSupportedFeatures.add("is_class"); + // missing: is_constructible + // missing: is_convertible_to + // missing: is_destructible + // missing: is_empty + sSupportedFeatures.add("is_enum"); + sSupportedFeatures.add("is_final"); + // missing: is_interface_class + // missing: is_literal + // missing: is_nothrow_assignable + // missing: is_nothrow_constructible + // missing: is_nothrow_destructible + sSupportedFeatures.add("is_pod"); + sSupportedFeatures.add("is_polymorphic"); + // missing: is_trivially_assignable + // missing: is_trivially_constructible + sSupportedFeatures.add("is_union"); + sSupportedFeatures.add("underlying_type"); + } + return sSupportedFeatures; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ExpressionEvaluator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ExpressionEvaluator.java index ec046024afa..ca78dfb9434 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ExpressionEvaluator.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ExpressionEvaluator.java @@ -258,6 +258,8 @@ public class ExpressionEvaluator { return 0; case CPreprocessor.tDEFINED: return handleDefined(); + case CPreprocessor.t__HAS_FEATURE: + return handleHasFeature(); case IToken.tLPAREN: consume(); long r1 = expression(); @@ -322,6 +324,25 @@ public class ExpressionEvaluator { } return result; } + + private long handleHasFeature() throws EvalException { + consume(); // '__has_feature' + if (LA() != IToken.tLPAREN) { + throw new EvalException(IProblem.SCANNER_EXPRESSION_SYNTAX_ERROR, null); + } + consume(); // opening parenthesis + if (LA() != IToken.tIDENTIFIER) { + throw new EvalException(IProblem.SCANNER_EXPRESSION_SYNTAX_ERROR, null); + } + final char[] featureName = fTokens.getCharImage(); + boolean supported = CPreprocessor.getSupportedFeatures().contains(new String(featureName)); + consume(); // feature name token + if (LA() != IToken.tRPAREN) { + throw new EvalException(IProblem.SCANNER_MISSING_R_PAREN, null); + } + consume(); // closing parenthesis + return supported ? 1 : 0; + } private int LA() { return fTokens.getType(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MacroExpander.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MacroExpander.java index 17d53908aa8..3fc31dea7ff 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MacroExpander.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/MacroExpander.java @@ -155,7 +155,7 @@ public class MacroExpander { public TokenList expand(ITokenSequence lexer, final int ppOptions, PreprocessorMacro macro, Token identifier, boolean completionMode, ScannerContext scannerContext) throws OffsetLimitReachedException { - final boolean protectDefined= (ppOptions & CPreprocessor.PROTECT_DEFINED) != 0; + final boolean protectIntrinsics= (ppOptions & CPreprocessor.PROTECT_INTRINSICS) != 0; if ((ppOptions & CPreprocessor.REPORT_SIGNIFICANT_MACROS) != 0) { fReportMacros= scannerContext; fReportUndefined= (ppOptions & CPreprocessor.IGNORE_UNDEFINED_SIGNIFICANT_MACROS) == 0; @@ -184,7 +184,7 @@ public class MacroExpander { input.prepend(firstExpansion); - result= expandAll(input, forbidden, protectDefined, null); + result= expandAll(input, forbidden, protectIntrinsics, null); } catch (CompletionInMacroExpansionException e) { // For content assist in macro expansions, we return the list of tokens of the // parameter at the current cursor position and hope that they make sense if @@ -351,7 +351,7 @@ public class MacroExpander { } private TokenList expandAll(TokenSource input, IdentityHashMap forbidden, - boolean protectDefinedConstructs, MacroExpansionTracker tracker) throws OffsetLimitReachedException { + boolean protectIntrinsics, MacroExpansionTracker tracker) throws OffsetLimitReachedException { final TokenList result= new TokenList(); boolean protect= false; Token l= null; @@ -366,10 +366,14 @@ public class MacroExpander { PreprocessorMacro macro= fDictionary.get(image); if (protect || (tracker != null && tracker.isDone())) { result.append(t); - } else if (protectDefinedConstructs && Arrays.equals(image, Keywords.cDEFINED)) { + } else if (protectIntrinsics && Arrays.equals(image, Keywords.cDEFINED)) { t.setType(CPreprocessor.tDEFINED); result.append(t); protect= true; + } else if (protectIntrinsics && Arrays.equals(image, Keywords.c__HAS_FEATURE)) { + t.setType(CPreprocessor.t__HAS_FEATURE); + result.append(t); + protect= true; } else if (macro == null || (macro.isFunctionStyle() && !input.findLParenthesis())) { // Tricky: Don't mark function-style macros if you don't find the left parenthesis if (fReportMacros != null) {