1 //===--- RestrictSystemIncludesCheck.cpp - clang-tidy ---------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "RestrictSystemIncludesCheck.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/HeaderSearch.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/Support/Path.h"
19 namespace clang::tidy::portability
{
21 void RestrictedIncludesPPCallbacks::InclusionDirective(
22 SourceLocation HashLoc
, const Token
&IncludeTok
, StringRef FileName
,
23 bool IsAngled
, CharSourceRange FilenameRange
, OptionalFileEntryRef File
,
24 StringRef SearchPath
, StringRef RelativePath
, const Module
*SuggestedModule
,
25 bool ModuleImported
, SrcMgr::CharacteristicKind FileType
) {
26 if (!Check
.contains(FileName
) && SrcMgr::isSystem(FileType
)) {
27 SmallString
<256> FullPath
;
28 llvm::sys::path::append(FullPath
, SearchPath
);
29 llvm::sys::path::append(FullPath
, RelativePath
);
30 // Bucket the allowed include directives by the id of the file they were
32 IncludeDirectives
[SM
.getFileID(HashLoc
)].emplace_back(
33 HashLoc
, FilenameRange
, FileName
, FullPath
.str(),
34 SM
.isInMainFile(HashLoc
));
38 void RestrictedIncludesPPCallbacks::EndOfMainFile() {
39 for (const auto &Bucket
: IncludeDirectives
) {
40 const FileIncludes
&FileDirectives
= Bucket
.second
;
42 // Emit fixits for all restricted includes.
43 for (const auto &Include
: FileDirectives
) {
44 // Fetch the length of the include statement from the start to just after
45 // the newline, for finding the end (including the newline).
46 unsigned ToLen
= std::strcspn(SM
.getCharacterData(Include
.Loc
), "\n") + 1;
47 CharSourceRange ToRange
= CharSourceRange::getCharRange(
48 Include
.Loc
, Include
.Loc
.getLocWithOffset(ToLen
));
50 if (!Include
.IsInMainFile
) {
53 "system include %0 not allowed, transitively included from %1");
54 D
<< Include
.IncludeFile
<< SM
.getFilename(Include
.Loc
);
55 D
<< FixItHint::CreateRemoval(ToRange
);
58 auto D
= Check
.diag(Include
.Loc
, "system include %0 not allowed");
59 D
<< Include
.IncludeFile
;
60 D
<< FixItHint::CreateRemoval(ToRange
);
65 void RestrictSystemIncludesCheck::registerPPCallbacks(
66 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
68 std::make_unique
<RestrictedIncludesPPCallbacks
>(*this, SM
));
71 void RestrictSystemIncludesCheck::storeOptions(
72 ClangTidyOptions::OptionMap
&Opts
) {
73 Options
.store(Opts
, "Includes", AllowedIncludes
);
76 } // namespace clang::tidy::portability