[clang][Tooling] Choose header insertion location in global module fragment (#151624)
Ensures that headers are inserted after `module;` declaration by updating minimum offset.
This commit is contained in:
parent
8259be65c7
commit
c76a9afe24
@ -74,13 +74,24 @@ void skipComments(Lexer &Lex, Token &Tok) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the offset after header guard directives and any comments
|
bool checkAndConsumeModuleDecl(const SourceManager &SM, Lexer &Lex,
|
||||||
// before/after header guards (e.g. #ifndef/#define pair, #pragma once). If no
|
Token &Tok) {
|
||||||
// header guard is present in the code, this will return the offset after
|
bool Matched = Tok.is(tok::raw_identifier) &&
|
||||||
// skipping all comments from the start of the code.
|
Tok.getRawIdentifier() == "module" &&
|
||||||
unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,
|
!Lex.LexFromRawLexer(Tok) && Tok.is(tok::semi) &&
|
||||||
StringRef Code,
|
!Lex.LexFromRawLexer(Tok);
|
||||||
const IncludeStyle &Style) {
|
return Matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines the minimum offset into the file where we want to insert header
|
||||||
|
// includes. This will be put (when available):
|
||||||
|
// - after `#pragma once`
|
||||||
|
// - after header guards (`#ifdef` and `#define`)
|
||||||
|
// - after opening global module (`module;`)
|
||||||
|
// - after any comments at the start of the file or immediately following one of
|
||||||
|
// the above constructs
|
||||||
|
unsigned getMinHeaderInsertionOffset(StringRef FileName, StringRef Code,
|
||||||
|
const IncludeStyle &Style) {
|
||||||
// \p Consume returns location after header guard or 0 if no header guard is
|
// \p Consume returns location after header guard or 0 if no header guard is
|
||||||
// found.
|
// found.
|
||||||
auto ConsumeHeaderGuardAndComment =
|
auto ConsumeHeaderGuardAndComment =
|
||||||
@ -95,7 +106,17 @@ unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,
|
|||||||
return std::max(InitialOffset, Consume(SM, Lex, Tok));
|
return std::max(InitialOffset, Consume(SM, Lex, Tok));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return std::max(
|
|
||||||
|
auto ModuleDecl = ConsumeHeaderGuardAndComment(
|
||||||
|
[](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
|
||||||
|
if (checkAndConsumeModuleDecl(SM, Lex, Tok)) {
|
||||||
|
skipComments(Lex, Tok);
|
||||||
|
return SM.getFileOffset(Tok.getLocation());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto HeaderAndPPOffset = std::max(
|
||||||
// #ifndef/#define
|
// #ifndef/#define
|
||||||
ConsumeHeaderGuardAndComment(
|
ConsumeHeaderGuardAndComment(
|
||||||
[](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
|
[](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
|
||||||
@ -115,6 +136,7 @@ unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,
|
|||||||
return SM.getFileOffset(Tok.getLocation());
|
return SM.getFileOffset(Tok.getLocation());
|
||||||
return 0;
|
return 0;
|
||||||
}));
|
}));
|
||||||
|
return std::max(HeaderAndPPOffset, ModuleDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a sequence of tokens is like
|
// Check if a sequence of tokens is like
|
||||||
@ -280,8 +302,7 @@ const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern);
|
|||||||
HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
|
HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
|
||||||
const IncludeStyle &Style)
|
const IncludeStyle &Style)
|
||||||
: FileName(FileName), Code(Code), FirstIncludeOffset(-1),
|
: FileName(FileName), Code(Code), FirstIncludeOffset(-1),
|
||||||
MinInsertOffset(
|
MinInsertOffset(getMinHeaderInsertionOffset(FileName, Code, Style)),
|
||||||
getOffsetAfterHeaderGuardsAndComments(FileName, Code, Style)),
|
|
||||||
MaxInsertOffset(MinInsertOffset +
|
MaxInsertOffset(MinInsertOffset +
|
||||||
getMaxHeaderInsertionOffset(
|
getMaxHeaderInsertionOffset(
|
||||||
FileName, Code.drop_front(MinInsertOffset), Style)),
|
FileName, Code.drop_front(MinInsertOffset), Style)),
|
||||||
|
@ -594,6 +594,148 @@ TEST_F(HeaderIncludesTest, CanDeleteAfterCode) {
|
|||||||
EXPECT_EQ(Expected, remove(Code, "\"b.h\""));
|
EXPECT_EQ(Expected, remove(Code, "\"b.h\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HeaderIncludesTest, InsertInGlobalModuleFragment) {
|
||||||
|
// Ensure header insertions go only in the global module fragment
|
||||||
|
std::string Code = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
export module foo;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
std::string Expected = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
#include <vector>
|
||||||
|
export module foo;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
|
||||||
|
auto InsertedCode = insert(Code, "<vector>");
|
||||||
|
EXPECT_EQ(Expected, insert(Code, "<vector>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HeaderIncludesTest, InsertInGlobalModuleFragmentWithPP) {
|
||||||
|
// Ensure header insertions go only in the global module fragment
|
||||||
|
std::string Code = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
// some more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// more comment
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
std::string Expected = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
// some more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// more comment
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
|
||||||
|
EXPECT_EQ(Expected, insert(Code, "<vector>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HeaderIncludesTest, InsertInGlobalModuleFragmentWithPPIncludes) {
|
||||||
|
// Ensure header insertions go only in the global module fragment
|
||||||
|
std::string Code = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
// some more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// more comment
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
std::string Expected = R"cpp(// comments
|
||||||
|
|
||||||
|
// more comments
|
||||||
|
|
||||||
|
// some more comments
|
||||||
|
|
||||||
|
module;
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// comment
|
||||||
|
|
||||||
|
#ifndef MACRO_NAME
|
||||||
|
#define MACRO_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// more comment
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::vector<int> ints {};
|
||||||
|
})cpp";
|
||||||
|
|
||||||
|
EXPECT_EQ(Expected, insert(Code, "<vector>"));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace tooling
|
} // namespace tooling
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
Loading…
x
Reference in New Issue
Block a user