Skip to content

Commit 9f16ded

Browse files
committed
[clang][lex] Introduce new single-module-parse mode
1 parent ece10a6 commit 9f16ded

File tree

5 files changed

+134
-9
lines changed

5 files changed

+134
-9
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,10 @@ def fno_modules_prune_non_affecting_module_map_files :
32473247
Group<f_Group>, Flags<[]>, Visibility<[CC1Option]>,
32483248
MarshallingInfoNegativeFlag<HeaderSearchOpts<"ModulesPruneNonAffectingModuleMaps">>,
32493249
HelpText<"Do not prune non-affecting module map files when writing module files">;
3250+
def fmodules_single_module_parse_mode :
3251+
Flag<["-"], "fmodules-single-module-parse-mode">,
3252+
Group<f_Group>, Flags<[]>, Visibility<[CC1Option]>,
3253+
MarshallingInfoFlag<PreprocessorOpts<"SingleModuleParseMode">>;
32503254

32513255
def fincremental_extensions :
32523256
Flag<["-"], "fincremental-extensions">,

clang/include/clang/Lex/PreprocessorOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ class PreprocessorOptions {
152152
/// that the client can get the maximum amount of information from the parser.
153153
bool SingleFileParseMode = false;
154154

155+
/// When enabled, preprocessor is in a mode for parsing a single module only.
156+
///
157+
/// Disables imports of other modules and if there are any unresolved
158+
/// identifiers in preprocessor directive conditions it causes all blocks to
159+
/// be skipped so that the client can get a strict subset of the contents.
160+
bool SingleModuleParseMode = false;
161+
155162
/// When enabled, the preprocessor will construct editor placeholder tokens.
156163
bool LexEditorPlaceholders = true;
157164

clang/lib/Lex/PPDirectives.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,10 +2073,11 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
20732073
return;
20742074

20752075
if (FilenameTok.isNot(tok::header_name)) {
2076-
if (FilenameTok.is(tok::identifier) && PPOpts.SingleFileParseMode) {
2076+
if (FilenameTok.is(tok::identifier) &&
2077+
(PPOpts.SingleFileParseMode || PPOpts.SingleModuleParseMode)) {
20772078
// If we saw #include IDENTIFIER and lexing didn't turn in into a header
2078-
// name, it was undefined. In 'single-file-parse' mode, just skip the
2079-
// directive without emitting diagnostics - the identifier might be
2079+
// name, it was undefined. In 'single-{file,module}-parse' mode, just skip
2080+
// the directive without emitting diagnostics - the identifier might be
20802081
// normally defined in previously-skipped include directive.
20812082
DiscardUntilEndOfDirective();
20822083
return;
@@ -2384,10 +2385,15 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
23842385
(getLangOpts().CPlusPlusModules || getLangOpts().Modules) &&
23852386
ModuleToImport && !ModuleToImport->isHeaderUnit();
23862387

2388+
if (MaybeTranslateInclude && (UsableHeaderUnit || UsableClangHeaderModule) &&
2389+
PPOpts.SingleModuleParseMode) {
2390+
Action = IncludeLimitReached;
2391+
}
23872392
// Determine whether we should try to import the module for this #include, if
23882393
// there is one. Don't do so if precompiled module support is disabled or we
23892394
// are processing this module textually (because we're building the module).
2390-
if (MaybeTranslateInclude && (UsableHeaderUnit || UsableClangHeaderModule)) {
2395+
else if (MaybeTranslateInclude &&
2396+
(UsableHeaderUnit || UsableClangHeaderModule)) {
23912397
// If this include corresponds to a module but that module is
23922398
// unavailable, diagnose the situation and bail out.
23932399
// FIXME: Remove this; loadModule does the same check (but produces
@@ -3439,6 +3445,13 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
34393445
CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(),
34403446
/*wasskip*/false, /*foundnonskip*/false,
34413447
/*foundelse*/false);
3448+
} else if (PPOpts.SingleModuleParseMode && !MI) {
3449+
// In 'single-module-parse mode' undefined identifiers trigger skipping of
3450+
// all the directive blocks. We lie here and set FoundNonSkipPortion so that
3451+
// even any \#else blocks get skipped.
3452+
SkipExcludedConditionalBlock(
3453+
HashToken.getLocation(), DirectiveTok.getLocation(),
3454+
/*FoundNonSkipPortion=*/true, /*FoundElse=*/false);
34423455
} else if (!MI == isIfndef || RetainExcludedCB) {
34433456
// Yes, remember that we are inside a conditional, then lex the next token.
34443457
CurPPLexer->pushConditionalLevel(DirectiveTok.getLocation(),
@@ -3493,6 +3506,13 @@ void Preprocessor::HandleIfDirective(Token &IfToken,
34933506
// the directive blocks.
34943507
CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false,
34953508
/*foundnonskip*/false, /*foundelse*/false);
3509+
} else if (PPOpts.SingleModuleParseMode && DER.IncludedUndefinedIds) {
3510+
// In 'single-module-parse mode' undefined identifiers trigger skipping of
3511+
// all the directive blocks. We lie here and set FoundNonSkipPortion so that
3512+
// even any \#else blocks get skipped.
3513+
SkipExcludedConditionalBlock(HashToken.getLocation(), IfToken.getLocation(),
3514+
/*FoundNonSkipPortion=*/true,
3515+
/*FoundElse=*/false);
34963516
} else if (ConditionalTrue || RetainExcludedCB) {
34973517
// Yes, remember that we are inside a conditional, then lex the next token.
34983518
CurPPLexer->pushConditionalLevel(IfToken.getLocation(), /*wasskip*/false,

clang/lib/Lex/PPExpressions.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,13 @@ static bool EvaluateDirectiveSubExpr(PPValue &LHS, unsigned MinPrec,
593593
Token &PeekTok, bool ValueLive,
594594
bool &IncludedUndefinedIds,
595595
Preprocessor &PP) {
596-
if (PP.getPreprocessorOpts().SingleFileParseMode && IncludedUndefinedIds) {
597-
// The single-file parse mode behavior kicks in as soon as single identifier
598-
// is undefined. If we've already seen one, there's no point in continuing
599-
// with the rest of the expression. Besides saving work, this also prevents
600-
// calling undefined function-like macros.
596+
if ((PP.getPreprocessorOpts().SingleFileParseMode ||
597+
PP.getPreprocessorOpts().SingleModuleParseMode) &&
598+
IncludedUndefinedIds) {
599+
// The single-{file,module}-parse mode behavior kicks in as soon as single
600+
// identifier is undefined. If we've already seen one, there's no point in
601+
// continuing with the rest of the expression. Besides saving work, this
602+
// also prevents calling undefined function-like macros.
601603
PP.DiscardUntilEndOfDirective(PeekTok);
602604
return true;
603605
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache \
5+
// RUN: -emit-module %t/module.modulemap -fmodule-name=B -o %t/cache/B.pcm \
6+
// RUN: -fmodules-single-module-parse-mode 2>&1 | FileCheck %s
7+
8+
// Modules are not imported.
9+
// CHECK-NOT: A.h:1:2: error: unreachable
10+
11+
// Headers belonging to this module are included.
12+
// CHECK: B2.h:2:2: warning: success
13+
14+
// Non-modular headers are included.
15+
// CHECK: T.h:2:2: warning: success
16+
17+
// No branches are entered for #if UNDEFINED.
18+
// CHECK-NOT: B1.h:6:2: error: unreachable
19+
// CHECK-NOT: B1.h:8:2: error: unreachable
20+
// CHECK-NOT: B1.h:10:2: error: unreachable
21+
22+
// No branches are entered for #ifdef UNDEFINED.
23+
// CHECK-NOT: B1.h:14:2: error: unreachable
24+
// CHECK-NOT: B1.h:16:2: error: unreachable
25+
26+
// No branches are entered for #ifndef UNDEFINED.
27+
// CHECK-NOT: B1.h:20:2: error: unreachable
28+
// CHECK-NOT: B1.h:22:2: error: unreachable
29+
30+
// No error messages are emitted for UNDEFINED_FUNCTION_LIKE().
31+
// CHECK-NOT: B1.h:25:2: error: unreachable
32+
33+
// The correct branch is entered for #if DEFINED.
34+
// CHECK: B1.h:32:3: warning: success
35+
// CHECK-NOT: B1.h:34:3: error: unreachable
36+
// CHECK-NOT: B1.h:36:3: error: unreachable
37+
38+
// Headers belonging to this module are included.
39+
// CHECK: B2.h:2:2: warning: success
40+
41+
//--- module.modulemap
42+
module A { header "A.h" }
43+
module B {
44+
header "B1.h"
45+
header "B2.h"
46+
}
47+
//--- A.h
48+
#error unreachable
49+
//--- B1.h
50+
#include "A.h"
51+
#include "B2.h"
52+
#include "T.h"
53+
54+
#if UNDEFINED
55+
# error unreachable
56+
#elif UNDEFINED2
57+
# error unreachable
58+
#else
59+
# error unreachable
60+
#endif
61+
62+
#ifdef UNDEFINED
63+
# error unreachable
64+
#else
65+
# error unreachable
66+
#endif
67+
68+
#ifndef UNDEFINED
69+
# error unreachable
70+
#else
71+
# error unreachable
72+
#endif
73+
74+
#if UNDEFINED_FUNCTION_LIKE()
75+
#endif
76+
77+
#define DEFINED_1 1
78+
#define DEFINED_2 1
79+
80+
#if DEFINED_1
81+
# warning success
82+
#elif DEFINED_2
83+
# error unreachable
84+
#else
85+
# error unreachable
86+
#endif
87+
//--- B2.h
88+
// Headers belonging to this module are included.
89+
#warning success
90+
//--- T.h
91+
// Non-modular headers are included.
92+
#warning success

0 commit comments

Comments
 (0)