Changeset View
Standalone View
clang/lib/Lex/Pragma.cpp
//===- Pragma.cpp - Pragma registration and handling ----------------------===// | //===- Pragma.cpp - Pragma registration and handling ----------------------===// | |||||||||||
// | // | |||||||||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||||||||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | |||||||||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||||||||||
// | // | |||||||||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | |||||||||||
// | // | |||||||||||
// This file implements the PragmaHandler/PragmaTable interfaces and implements | // This file implements the PragmaHandler/PragmaTable interfaces and implements | |||||||||||
// pragma related methods of the Preprocessor class. | // pragma related methods of the Preprocessor class. | |||||||||||
// | // | |||||||||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | |||||||||||
#include "clang/Lex/Pragma.h" | #include "clang/Lex/Pragma.h" | |||||||||||
#include "clang/Basic/Diagnostic.h" | #include "clang/Basic/Diagnostic.h" | |||||||||||
#include "clang/Basic/DiagnosticLex.h" | ||||||||||||
#include "clang/Basic/FileManager.h" | #include "clang/Basic/FileManager.h" | |||||||||||
#include "clang/Basic/IdentifierTable.h" | #include "clang/Basic/IdentifierTable.h" | |||||||||||
#include "clang/Basic/LLVM.h" | #include "clang/Basic/LLVM.h" | |||||||||||
#include "clang/Basic/LangOptions.h" | #include "clang/Basic/LangOptions.h" | |||||||||||
#include "clang/Basic/Module.h" | #include "clang/Basic/Module.h" | |||||||||||
#include "clang/Basic/SourceLocation.h" | #include "clang/Basic/SourceLocation.h" | |||||||||||
#include "clang/Basic/SourceManager.h" | #include "clang/Basic/SourceManager.h" | |||||||||||
#include "clang/Basic/TokenKinds.h" | #include "clang/Basic/TokenKinds.h" | |||||||||||
#include "clang/Lex/HeaderSearch.h" | #include "clang/Lex/HeaderSearch.h" | |||||||||||
#include "clang/Lex/LexDiagnostic.h" | #include "clang/Lex/LexDiagnostic.h" | |||||||||||
#include "clang/Lex/Lexer.h" | #include "clang/Lex/Lexer.h" | |||||||||||
#include "clang/Lex/LiteralSupport.h" | #include "clang/Lex/LiteralSupport.h" | |||||||||||
#include "clang/Lex/MacroInfo.h" | #include "clang/Lex/MacroInfo.h" | |||||||||||
#include "clang/Lex/ModuleLoader.h" | #include "clang/Lex/ModuleLoader.h" | |||||||||||
#include "clang/Lex/PPCallbacks.h" | #include "clang/Lex/PPCallbacks.h" | |||||||||||
#include "clang/Lex/Preprocessor.h" | #include "clang/Lex/Preprocessor.h" | |||||||||||
#include "clang/Lex/PreprocessorLexer.h" | #include "clang/Lex/PreprocessorLexer.h" | |||||||||||
#include "clang/Lex/PreprocessorOptions.h" | #include "clang/Lex/PreprocessorOptions.h" | |||||||||||
#include "clang/Lex/Token.h" | #include "clang/Lex/Token.h" | |||||||||||
#include "clang/Lex/TokenLexer.h" | #include "clang/Lex/TokenLexer.h" | |||||||||||
#include "llvm/ADT/ArrayRef.h" | #include "llvm/ADT/ArrayRef.h" | |||||||||||
#include "llvm/ADT/DenseMap.h" | #include "llvm/ADT/DenseMap.h" | |||||||||||
#include "llvm/ADT/Optional.h" | ||||||||||||
#include "llvm/ADT/STLExtras.h" | #include "llvm/ADT/STLExtras.h" | |||||||||||
#include "llvm/ADT/SmallString.h" | #include "llvm/ADT/SmallString.h" | |||||||||||
#include "llvm/ADT/SmallVector.h" | #include "llvm/ADT/SmallVector.h" | |||||||||||
#include "llvm/ADT/StringSwitch.h" | ||||||||||||
#include "llvm/ADT/StringRef.h" | #include "llvm/ADT/StringRef.h" | |||||||||||
#include "llvm/ADT/StringSwitch.h" | ||||||||||||
#include "llvm/Support/Compiler.h" | #include "llvm/Support/Compiler.h" | |||||||||||
#include "llvm/Support/ErrorHandling.h" | #include "llvm/Support/ErrorHandling.h" | |||||||||||
#include "llvm/Support/Timer.h" | #include "llvm/Support/Timer.h" | |||||||||||
#include <algorithm> | #include <algorithm> | |||||||||||
#include <cassert> | #include <cassert> | |||||||||||
#include <cstddef> | #include <cstddef> | |||||||||||
#include <cstdint> | #include <cstdint> | |||||||||||
#include <limits> | #include <limits> | |||||||||||
▲ Show 20 Lines • Show All 439 Lines • ▼ Show 20 Lines | void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { | |||||||||||
// Emit a line marker. This will change any source locations from this point | // Emit a line marker. This will change any source locations from this point | |||||||||||
// forward to realize they are in a system header. | // forward to realize they are in a system header. | |||||||||||
// Create a line note with this information. | // Create a line note with this information. | |||||||||||
SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine() + 1, | SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine() + 1, | |||||||||||
FilenameID, /*IsEntry=*/false, /*IsExit=*/false, | FilenameID, /*IsEntry=*/false, /*IsExit=*/false, | |||||||||||
SrcMgr::C_System); | SrcMgr::C_System); | |||||||||||
} | } | |||||||||||
/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. | static llvm::Optional<Token> LexHeader(Preprocessor &PP, | |||||||||||
void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { | Optional<FileEntryRef> &File, | |||||||||||
bool SuppressIncludeNotFoundError) { | ||||||||||||
Token FilenameTok; | Token FilenameTok; | |||||||||||
if (LexHeaderName(FilenameTok, /*AllowConcatenation*/false)) | if (PP.LexHeaderName(FilenameTok, /*AllowConcatenation*/ false)) | |||||||||||
return; | return llvm::None; | |||||||||||
aaron.ballman: Please return `llvm::None` in all these cases to make it more clear what's going on. | ||||||||||||
// If the next token wasn't a header-name, diagnose the error. | // If the next token wasn't a header-name, diagnose the error. | |||||||||||
if (FilenameTok.isNot(tok::header_name)) { | if (FilenameTok.isNot(tok::header_name)) { | |||||||||||
Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename); | PP.Diag(FilenameTok.getLocation(), diag::err_pp_expects_filename); | |||||||||||
return; | return llvm::None; | |||||||||||
} | } | |||||||||||
// Reserve a buffer to get the spelling. | // Reserve a buffer to get the spelling. | |||||||||||
SmallString<128> FilenameBuffer; | SmallString<128> FilenameBuffer; | |||||||||||
bool Invalid = false; | bool Invalid = false; | |||||||||||
StringRef Filename = getSpelling(FilenameTok, FilenameBuffer, &Invalid); | StringRef Filename = PP.getSpelling(FilenameTok, FilenameBuffer, &Invalid); | |||||||||||
if (Invalid) | if (Invalid) | |||||||||||
return; | return llvm::None; | |||||||||||
bool isAngled = | bool isAngled = | |||||||||||
GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); | PP.GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); | |||||||||||
// If GetIncludeFilenameSpelling set the start ptr to null, there was an | // If GetIncludeFilenameSpelling set the start ptr to null, there was an | |||||||||||
// error. | // error. | |||||||||||
if (Filename.empty()) | if (Filename.empty()) | |||||||||||
return; | return llvm::None; | |||||||||||
// Search include directories for this file. | // Search include directories for this file. | |||||||||||
const DirectoryLookup *CurDir; | const DirectoryLookup *CurDir; | |||||||||||
Optional<FileEntryRef> File = | File = PP.LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr, | |||||||||||
LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr, | nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, | |||||||||||
nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr); | nullptr); | |||||||||||
if (!File) { | if (!File) { | |||||||||||
if (!SuppressIncludeNotFoundError) | if (!SuppressIncludeNotFoundError) | |||||||||||
Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; | PP.Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; | |||||||||||
return llvm::None; | ||||||||||||
} | ||||||||||||
return FilenameTok; | ||||||||||||
} | ||||||||||||
/// HandlePragmaIncludeInstead - Handle \#pragma clang include_instead(header). | ||||||||||||
void Preprocessor::HandlePragmaIncludeInstead(Token &Tok) { | ||||||||||||
// Get the current file lexer we're looking at. Ignore _Pragma 'files' etc. | ||||||||||||
PreprocessorLexer *TheLexer = getCurrentFileLexer(); | ||||||||||||
if (!SourceMgr.isInSystemHeader(Tok.getLocation())) { | ||||||||||||
Diag(Tok, diag::pp_pragma_include_instead_not_sysheader); | ||||||||||||
return; | ||||||||||||
} | ||||||||||||
Lex(Tok); | ||||||||||||
if (Tok.isNot(tok::l_paren)) { | ||||||||||||
Diag(Tok, diag::pp_pragma_include_instead_unexpected_token) | ||||||||||||
<< "(" << getSpelling(Tok); | ||||||||||||
return; | ||||||||||||
} | ||||||||||||
aaron.ballman: | ||||||||||||
Optional<FileEntryRef> File; | ||||||||||||
I thought I deleted this. cjdb: I thought I deleted this. | ||||||||||||
llvm::Optional<Token> FilenameTok = | ||||||||||||
LexHeader(*this, File, SuppressIncludeNotFoundError); | ||||||||||||
if (!FilenameTok) | ||||||||||||
return; | ||||||||||||
Lex(Tok); | ||||||||||||
if (Tok.isNot(tok::r_paren)) { | ||||||||||||
Diag(Tok, diag::pp_pragma_include_instead_unexpected_token) | ||||||||||||
<< ")" << getSpelling(Tok); | ||||||||||||
aaron.ballman: | ||||||||||||
return; | return; | |||||||||||
} | } | |||||||||||
HeaderInfo.AddFileAlias( | ||||||||||||
aaron.ballman: | ||||||||||||
Alternatively: remove spelling altogether. cjdb: Alternatively: remove `spelling` altogether. | ||||||||||||
TheLexer->getFileEntry(), | ||||||||||||
{FilenameTok->getLiteralData(), FilenameTok->getLength()}); | ||||||||||||
Not Done ReplyInline Actionsactually, get getSpelling() is probably necessary in case getLiteralData() returns null? hans: actually, get getSpelling() is probably necessary in case getLiteralData() returns null? | ||||||||||||
} | ||||||||||||
/// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. | ||||||||||||
void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { | ||||||||||||
Optional<FileEntryRef> File; | ||||||||||||
llvm::Optional<Token> FilenameTok = | ||||||||||||
LexHeader(*this, File, SuppressIncludeNotFoundError); | ||||||||||||
if (!FilenameTok) | ||||||||||||
return; | ||||||||||||
const FileEntry *CurFile = getCurrentFileLexer()->getFileEntry(); | const FileEntry *CurFile = getCurrentFileLexer()->getFileEntry(); | |||||||||||
// If this file is older than the file it depends on, emit a diagnostic. | // If this file is older than the file it depends on, emit a diagnostic. | |||||||||||
if (CurFile && CurFile->getModificationTime() < File->getModificationTime()) { | if (CurFile && CurFile->getModificationTime() < File->getModificationTime()) { | |||||||||||
// Lex tokens at the end of the message and include them in the message. | // Lex tokens at the end of the message and include them in the message. | |||||||||||
std::string Message; | std::string Message; | |||||||||||
aaron.ballman: | ||||||||||||
Lex(DependencyTok); | Lex(DependencyTok); | |||||||||||
while (DependencyTok.isNot(tok::eod)) { | while (DependencyTok.isNot(tok::eod)) { | |||||||||||
Message += getSpelling(DependencyTok) + " "; | Message += getSpelling(DependencyTok) + " "; | |||||||||||
Lex(DependencyTok); | Lex(DependencyTok); | |||||||||||
} | } | |||||||||||
// Remove the trailing ' ' if present. | // Remove the trailing ' ' if present. | |||||||||||
if (!Message.empty()) | if (!Message.empty()) | |||||||||||
Message.erase(Message.end()-1); | Message.erase(Message.end()-1); | |||||||||||
Diag(FilenameTok, diag::pp_out_of_date_dependency) << Message; | Diag(*FilenameTok, diag::pp_out_of_date_dependency) << Message; | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
/// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro. | /// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro. | |||||||||||
/// Return the IdentifierInfo* associated with the macro to push or pop. | /// Return the IdentifierInfo* associated with the macro to push or pop. | |||||||||||
IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) { | IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) { | |||||||||||
// Remember the pragma token location. | // Remember the pragma token location. | |||||||||||
Token PragmaTok = Tok; | Token PragmaTok = Tok; | |||||||||||
▲ Show 20 Lines • Show All 458 Lines • ▼ Show 20 Lines | struct PragmaSystemHeaderHandler : public PragmaHandler { | |||||||||||
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, | void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, | |||||||||||
Token &SHToken) override { | Token &SHToken) override { | |||||||||||
PP.HandlePragmaSystemHeader(SHToken); | PP.HandlePragmaSystemHeader(SHToken); | |||||||||||
PP.CheckEndOfDirective("pragma"); | PP.CheckEndOfDirective("pragma"); | |||||||||||
} | } | |||||||||||
}; | }; | |||||||||||
/// PragmaIncludeInsteadHandler - "\#pragma clang include_instead(header)" marks | ||||||||||||
aaron.ballman: | ||||||||||||
/// the current file as non-includable if the including header is not a system | ||||||||||||
/// header. | ||||||||||||
struct PragmaIncludeInsteadHandler : public PragmaHandler { | ||||||||||||
PragmaIncludeInsteadHandler() : PragmaHandler("include_instead") {} | ||||||||||||
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, | ||||||||||||
Token &IIToken) override { | ||||||||||||
PP.HandlePragmaIncludeInstead(IIToken); | ||||||||||||
} | ||||||||||||
}; | ||||||||||||
struct PragmaDependencyHandler : public PragmaHandler { | struct PragmaDependencyHandler : public PragmaHandler { | |||||||||||
PragmaDependencyHandler() : PragmaHandler("dependency") {} | PragmaDependencyHandler() : PragmaHandler("dependency") {} | |||||||||||
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, | void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, | |||||||||||
Token &DepToken) override { | Token &DepToken) override { | |||||||||||
PP.HandlePragmaDependency(DepToken); | PP.HandlePragmaDependency(DepToken); | |||||||||||
} | } | |||||||||||
}; | }; | |||||||||||
▲ Show 20 Lines • Show All 896 Lines • ▼ Show 20 Lines | void Preprocessor::RegisterBuiltinPragmas() { | |||||||||||
AddPragmaHandler("GCC", new PragmaDiagnosticHandler("GCC")); | AddPragmaHandler("GCC", new PragmaDiagnosticHandler("GCC")); | |||||||||||
AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Warning, | AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Warning, | |||||||||||
"GCC")); | "GCC")); | |||||||||||
AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Error, | AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Error, | |||||||||||
"GCC")); | "GCC")); | |||||||||||
// #pragma clang ... | // #pragma clang ... | |||||||||||
AddPragmaHandler("clang", new PragmaPoisonHandler()); | AddPragmaHandler("clang", new PragmaPoisonHandler()); | |||||||||||
AddPragmaHandler("clang", new PragmaSystemHeaderHandler()); | AddPragmaHandler("clang", new PragmaSystemHeaderHandler()); | |||||||||||
AddPragmaHandler("clang", new PragmaIncludeInsteadHandler()); | ||||||||||||
Not Done ReplyInline Actions@rsmith wrote:
@rsmith, I think you misunderstand part of the design goal here. You said "you can only reach the implementation detail header through one of the named headers" — this is true only for a maybe-surprising definition of "you." For example, if the user-programmer's "MyThing.hpp" tries to include <__utility/move.h> directly (or if IWYU wants to suggest how to include it), we expect the tool to suggest that you must include <utility>, not <__utility/move.h>. However, if the library's <algorithm> tries to include <__utility/move.h> directly, that's totally fine and expected. The entire point of these detail headers is to make sure that library headers don't have to #include <utility> when all they need is one little piece of it. So we do need some way to distinguish user-programmer headers like "MyThing.hpp" from library headers like <algorithm>. At the moment this is done by limiting the usefulness of the feature to just system headers, and making every system header a "friend" of every other system header. But one could imagine allowing a detail header to specify not only "here's how my non-friends should get to me" (e.g. by including <utility>) but also "here's a list of headers and/or directories which should be considered friends of mine" (and which could then include me directly, e.g. <algorithm> or <__iterator/iter_move.h>). We'd need a reliable syntax for that, though. Quuxplusone: @rsmith wrote:
> I'm not in love with the design of this feature. Basing the accept / warn… | ||||||||||||
Not Done ReplyInline ActionsI think what you're suggesting is exactly what I suggested later: "We'd presumably need two lists of headers: the external headers that we encourage people to include to get at the implementation detail header, and the internal list of headers that are also allowed to use it but that we shouldn't list in the diagnostic." [That was an edit I made after my original comment, though; I realized I forgot to mention this and went back and added it. I guess your reply and my edit happened at around the same time?] As for syntax, something like this would make some sense to me: #pragma clang include_instead(<foo>, "bar"; "private.h") (Putting all the headers in a single pragma removes the need to track and check this at the end of the file.) rsmith: I think what you're suggesting is exactly what I suggested later: "We'd presumably need two… | ||||||||||||
AddPragmaHandler("clang", new PragmaDebugHandler()); | AddPragmaHandler("clang", new PragmaDebugHandler()); | |||||||||||
AddPragmaHandler("clang", new PragmaDependencyHandler()); | AddPragmaHandler("clang", new PragmaDependencyHandler()); | |||||||||||
AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang")); | AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang")); | |||||||||||
AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler()); | AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler()); | |||||||||||
AddPragmaHandler("clang", new PragmaAssumeNonNullHandler()); | AddPragmaHandler("clang", new PragmaAssumeNonNullHandler()); | |||||||||||
// #pragma clang module ... | // #pragma clang module ... | |||||||||||
auto *ModuleHandler = new PragmaNamespace("module"); | auto *ModuleHandler = new PragmaNamespace("module"); | |||||||||||
Show All 36 Lines |
Please return llvm::None in all these cases to make it more clear what's going on.