#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/type_traits.h"
#include <vector>
+#include <list>
namespace clang {
class DiagnosticClient;
DiagnosticClient *Client;
bool OwnsDiagClient;
SourceManager *SourceMgr;
-
- /// DiagMappings - Mapping information for diagnostics. Mapping info is
+
+ /// \brief Mapping information for diagnostics. Mapping info is
/// packed into four bits per diagnostic. The low three bits are the mapping
/// (an instance of diag::Mapping), or zero if unset. The high bit is set
/// when the mapping was established as a user mapping. If the high bit is
/// clear, then the low bits are set to the default value, and should be
/// mapped with -pedantic, -Werror, etc.
- class DiagMappings {
- unsigned char Values[diag::DIAG_UPPER_LIMIT/2];
+ ///
+ /// Contrary to DiagMappings, a new DiagState is created and kept around when
+ /// diagnostic pragmas modify the state so that we know what is the diagnostic
+ /// state at any given source location.
+ class DiagState {
+ mutable llvm::DenseMap<unsigned, unsigned> DiagMap;
public:
- DiagMappings() : Values() /*zero-initialization of array*/ { }
+ void setMapping(diag::kind Diag, unsigned Map) { DiagMap[Diag] = Map; }
- void setMapping(diag::kind Diag, unsigned Map) {
- size_t Shift = (Diag & 1)*4;
- Values[Diag/2] = (Values[Diag/2] & ~(15 << Shift)) | (Map << Shift);
+ diag::Mapping getMapping(diag::kind Diag) const {
+ return (diag::Mapping)DiagMap[Diag];
}
+ };
- diag::Mapping getMapping(diag::kind Diag) const {
- return (diag::Mapping)((Values[Diag/2] >> (Diag & 1)*4) & 15);
+ /// \brief Keeps and automatically disposes all DiagStates that we create.
+ std::list<DiagState> DiagStates;
+
+ /// \brief Represents a point in source where the diagnostic state was
+ /// modified because of a pragma. 'Loc' can be null if the point represents
+ /// the diagnostic state modifications done through the command-line.
+ struct DiagStatePoint {
+ DiagState *State;
+ FullSourceLoc Loc;
+ DiagStatePoint(DiagState *State, FullSourceLoc Loc)
+ : State(State), Loc(Loc) { }
+
+ bool operator<(const DiagStatePoint &RHS) const {
+ // If Loc is invalid it means it came from <command-line>, in which case
+ // we regard it as coming before any valid source location.
+ if (RHS.Loc.isInvalid())
+ return false;
+ if (Loc.isInvalid())
+ return true;
+ return Loc.isBeforeInTranslationUnitThan(RHS.Loc);
}
};
- mutable std::vector<DiagMappings> DiagMappingsStack;
+ /// \brief A vector of all DiagStatePoints representing changes in diagnostic
+ /// state due to diagnostic pragmas. The vector is always sorted according to
+ /// the SourceLocation of the DiagStatePoint.
+ typedef std::vector<DiagStatePoint> DiagStatePointsTy;
+ mutable DiagStatePointsTy DiagStatePoints;
+
+ /// \brief Keeps the DiagState that was active during each diagnostic 'push'
+ /// so we can get back at it when we 'pop'.
+ std::vector<DiagState *> DiagStateOnPushStack;
+
+ DiagState *GetCurDiagState() const {
+ assert(!DiagStatePoints.empty());
+ return DiagStatePoints.back().State;
+ }
+
+ void PushDiagStatePoint(DiagState *State, SourceLocation L) {
+ FullSourceLoc Loc(L, *SourceMgr);
+ // Make sure that DiagStatePoints is always sorted according to Loc.
+ assert((Loc.isValid() || DiagStatePoints.empty()) &&
+ "Adding invalid loc point after another point");
+ assert((Loc.isInvalid() || DiagStatePoints.empty() ||
+ DiagStatePoints.back().Loc.isInvalid() ||
+ DiagStatePoints.back().Loc.isBeforeInTranslationUnitThan(Loc)) &&
+ "Previous point loc comes after or is the same as new one");
+ DiagStatePoints.push_back(DiagStatePoint(State,
+ FullSourceLoc(Loc, *SourceMgr)));
+ }
+
+ /// \brief Finds the DiagStatePoint that contains the diagnostic state of
+ /// the given source location.
+ DiagStatePointsTy::iterator GetDiagStatePointForLoc(SourceLocation Loc) const;
/// ErrorOccurred / FatalErrorOccurred - This is set to true when an error or
/// fatal error is emitted, and is sticky.
/// pushMappings - Copies the current DiagMappings and pushes the new copy
/// onto the top of the stack.
- void pushMappings();
+ void pushMappings(SourceLocation Loc);
/// popMappings - Pops the current DiagMappings off the top of the stack
/// causing the new top of the stack to be the active mappings. Returns
/// true if the pop happens, false if there is only one DiagMapping on the
/// stack.
- bool popMappings();
+ bool popMappings(SourceLocation Loc);
/// \brief Set the diagnostic client associated with this diagnostic object.
///
void DecrementAllExtensionsSilenced() { --AllExtensionsSilenced; }
bool hasAllExtensionsSilenced() { return AllExtensionsSilenced != 0; }
- /// setDiagnosticMapping - This allows the client to specify that certain
+ /// \brief This allows the client to specify that certain
/// warnings are ignored. Notes can never be mapped, errors can only be
/// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily.
- void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map) {
- assert(Diag < diag::DIAG_UPPER_LIMIT &&
- "Can only map builtin diagnostics");
- assert((Diags->isBuiltinWarningOrExtension(Diag) ||
- (Map == diag::MAP_FATAL || Map == diag::MAP_ERROR)) &&
- "Cannot map errors into warnings!");
- setDiagnosticMappingInternal(Diag, Map, true);
- }
+ ///
+ /// \param Loc The source location that this change of diagnostic state should
+ /// take affect. It can be null if we are setting the latest state.
+ void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map,
+ SourceLocation Loc);
/// setDiagnosticGroupMapping - Change an entire diagnostic group (e.g.
/// "unknown-pragmas" to have the specified mapping. This returns true and
/// ignores the request if "Group" was unknown, false otherwise.
- bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map) {
- return Diags->setDiagnosticGroupMapping(Group, Map, *this);
+ ///
+ /// 'Loc' is the source location that this change of diagnostic state should
+ /// take affect. It can be null if we are setting the state from command-line.
+ bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map,
+ SourceLocation Loc = SourceLocation()) {
+ return Diags->setDiagnosticGroupMapping(Group, Map, Loc, *this);
}
bool hasErrorOccurred() const { return ErrorOccurred; }
// Diagnostic classification and reporting interfaces.
//
- /// getDiagnosticLevel - Based on the way the client configured the Diagnostic
+ /// \brief Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
- Level getDiagnosticLevel(unsigned DiagID) const {
- return (Level)Diags->getDiagnosticLevel(DiagID, *this);
+ ///
+ /// \param Loc The source location we are interested in finding out the
+ /// diagnostic state. Can be null in order to query the latest state.
+ Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc) const {
+ return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
}
/// Report - Issue the message to the client. @c DiagID is a member of the
/// getDiagnosticMappingInfo - Return the mapping info currently set for the
/// specified builtin diagnostic. This returns the high bit encoding, or zero
/// if the field is completely uninitialized.
- diag::Mapping getDiagnosticMappingInfo(diag::kind Diag) const {
- return DiagMappingsStack.back().getMapping(Diag);
+ diag::Mapping getDiagnosticMappingInfo(diag::kind Diag,
+ DiagState *State) const {
+ return State->getMapping(Diag);
}
void setDiagnosticMappingInternal(unsigned DiagId, unsigned Map,
- bool isUser) const {
+ DiagState *State, bool isUser) const {
if (isUser) Map |= 8; // Set the high bit for user mappings.
- DiagMappingsStack.back().setMapping((diag::kind)DiagId, Map);
+ State->setMapping((diag::kind)DiagId, Map);
}
// This is private state used by DiagnosticBuilder. We put it here instead of
namespace clang {
class Diagnostic;
+ class SourceLocation;
// Import the diagnostic enums themselves.
namespace diag {
/// "unknown-pragmas" to have the specified mapping. This returns true and
/// ignores the request if "Group" was unknown, false otherwise.
bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map,
- Diagnostic &Diag) const;
+ SourceLocation Loc, Diagnostic &Diag) const;
- /// getDiagnosticLevel - Based on the way the client configured the Diagnostic
+ /// \brief Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
- DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID,
+ ///
+ /// \param Loc The source location we are interested in finding out the
+ /// diagnostic state. Can be null in order to query the latest state.
+ DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
const Diagnostic &Diag) const;
/// getDiagnosticLevel - This is an internal implementation helper used when
/// DiagClass is already known.
DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID,
unsigned DiagClass,
+ SourceLocation Loc,
const Diagnostic &Diag) const;
/// ProcessDiag - This is the method used to report a diagnostic that is
#ifndef LLVM_CLANG_SOURCELOCATION_H
#define LLVM_CLANG_SOURCELOCATION_H
+#include "llvm/Support/PointerLikeTypeTraits.h"
#include <utility>
#include <cassert>
bool isInSystemHeader() const;
+ /// \brief Determines the order of 2 source locations in the translation unit.
+ ///
+ /// \returns true if this source location comes before 'Loc', false otherwise.
+ bool isBeforeInTranslationUnitThan(SourceLocation Loc) const;
+
+ /// \brief Determines the order of 2 source locations in the translation unit.
+ ///
+ /// \returns true if this source location comes before 'Loc', false otherwise.
+ bool isBeforeInTranslationUnitThan(FullSourceLoc Loc) const {
+ assert(Loc.isValid());
+ assert(SrcMgr == Loc.SrcMgr && "Loc comes from another SourceManager!");
+ return isBeforeInTranslationUnitThan((SourceLocation)Loc);
+ }
+
/// Prints information about this FullSourceLoc to stderr. Useful for
/// debugging.
void dump() const { SourceLocation::dump(*SrcMgr); }
template <>
struct isPodLike<clang::FileID> { static const bool value = true; };
+ // Teach SmallPtrSet how to handle SourceLocation.
+ template<>
+ class PointerLikeTypeTraits<clang::SourceLocation> {
+ public:
+ static inline void *getAsVoidPointer(clang::SourceLocation L) {
+ return (void*)L.getRawEncoding();
+ }
+ static inline clang::SourceLocation getFromVoidPointer(void *P) {
+ return clang::SourceLocation::getFromRawEncoding((unsigned)(uintptr_t)P);
+ }
+ enum { NumLowBitsAvailable = 0 };
+ };
+
} // end namespace llvm
#endif
/// AllowRedefinitionsWithoutWarning - True if this macro can be redefined
/// without emitting a warning.
bool IsAllowRedefinitionsWithoutWarning : 1;
+
+ /// \brief Must warn if the macro is unused at the end of translation unit.
+ bool IsWarnIfUnused : 1;
~MacroInfo() {
assert(ArgumentList == 0 && "Didn't call destroy before dtor!");
IsAllowRedefinitionsWithoutWarning = Val;
}
+ /// \brief Set the value of the IsWarnIfUnused flag.
+ void setIsWarnIfUnused(bool val) {
+ IsWarnIfUnused = val;
+ }
+
/// setArgumentList - Set the specified list of identifiers as the argument
/// list for this macro.
void setArgumentList(IdentifierInfo* const *List, unsigned NumArgs,
return IsAllowRedefinitionsWithoutWarning;
}
+ /// \brief Return true if we should emit a warning if the macro is unused.
+ bool isWarnIfUnused() const {
+ return IsWarnIfUnused;
+ }
+
/// getNumTokens - Return the number of tokens that this macro expands to.
///
unsigned getNumTokens() const {
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
/// to the actual definition of the macro.
llvm::DenseMap<IdentifierInfo*, MacroInfo*> Macros;
+ /// \brief Macros that we want to warn because they are not used at the end
+ /// of the translation unit; we store just their SourceLocations instead
+ /// something like MacroInfo*. The benefit of this is that when we are
+ /// deserializing from PCH, we don't need to deserialize identifier & macros
+ /// just so that we can report that they are unused, we just warn using
+ /// the SourceLocations of this set (that will be filled by the ASTReader).
+ /// We are using SmallPtrSet instead of a vector for faster removal.
+ typedef llvm::SmallPtrSet<SourceLocation, 32> WarnUnusedMacroLocsTy;
+ WarnUnusedMacroLocsTy WarnUnusedMacroLocs;
+
/// MacroArgCache - This is a "freelist" of MacroArg objects that can be
/// reused for quick allocation.
MacroArgs *MacroArgCache;
// Return true and store the first token only if any CommentHandler
// has inserted some tokens and getCommentRetentionState() is false.
bool HandleComment(Token &Token, SourceRange Comment);
+
+ /// \brief A macro is used, update information about macros that need unused
+ /// warnings.
+ void markMacroAsUsed(MacroInfo *MI);
};
/// \brief Abstract base class that describes a handler that will receive
ErrorLimit = 0;
TemplateBacktraceLimit = 0;
- // Set all mappings to 'unset'.
- DiagMappingsStack.clear();
- DiagMappingsStack.push_back(DiagMappings());
+ // Create a DiagState and DiagStatePoint representing diagnostic changes
+ // through command-line.
+ DiagStates.push_back(DiagState());
+ PushDiagStatePoint(&DiagStates.back(), SourceLocation());
Reset();
}
}
-void Diagnostic::pushMappings() {
- // Avoids undefined behavior when the stack has to resize.
- DiagMappingsStack.reserve(DiagMappingsStack.size() + 1);
- DiagMappingsStack.push_back(DiagMappingsStack.back());
+void Diagnostic::pushMappings(SourceLocation Loc) {
+ DiagStateOnPushStack.push_back(GetCurDiagState());
}
-bool Diagnostic::popMappings() {
- if (DiagMappingsStack.size() == 1)
+bool Diagnostic::popMappings(SourceLocation Loc) {
+ if (DiagStateOnPushStack.empty())
return false;
- DiagMappingsStack.pop_back();
+ if (DiagStateOnPushStack.back() != GetCurDiagState()) {
+ // State changed at some point between push/pop.
+ PushDiagStatePoint(DiagStateOnPushStack.back(), Loc);
+ }
+ DiagStateOnPushStack.pop_back();
return true;
}
DelayedDiagArg2.clear();
}
+Diagnostic::DiagStatePointsTy::iterator
+Diagnostic::GetDiagStatePointForLoc(SourceLocation L) const {
+ assert(!DiagStatePoints.empty());
+ assert(DiagStatePoints.front().Loc.isInvalid() &&
+ "Should have created a DiagStatePoint for command-line");
+
+ FullSourceLoc Loc(L, *SourceMgr);
+ if (Loc.isInvalid())
+ return DiagStatePoints.end() - 1;
+
+ DiagStatePointsTy::iterator Pos = DiagStatePoints.end();
+ FullSourceLoc LastStateChangePos = DiagStatePoints.back().Loc;
+ if (LastStateChangePos.isValid() &&
+ Loc.isBeforeInTranslationUnitThan(LastStateChangePos))
+ Pos = std::upper_bound(DiagStatePoints.begin(), DiagStatePoints.end(),
+ DiagStatePoint(0, Loc));
+ --Pos;
+ return Pos;
+}
+
+/// \brief This allows the client to specify that certain
+/// warnings are ignored. Notes can never be mapped, errors can only be
+/// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily.
+///
+/// \param The source location that this change of diagnostic state should
+/// take affect. It can be null if we are setting the latest state.
+void Diagnostic::setDiagnosticMapping(diag::kind Diag, diag::Mapping Map,
+ SourceLocation L) {
+ assert(Diag < diag::DIAG_UPPER_LIMIT &&
+ "Can only map builtin diagnostics");
+ assert((Diags->isBuiltinWarningOrExtension(Diag) ||
+ (Map == diag::MAP_FATAL || Map == diag::MAP_ERROR)) &&
+ "Cannot map errors into warnings!");
+ assert(!DiagStatePoints.empty());
+
+ FullSourceLoc Loc(L, *SourceMgr);
+ FullSourceLoc LastStateChangePos = DiagStatePoints.back().Loc;
+
+ // Common case; setting all the diagnostics of a group in one place.
+ if (Loc.isInvalid() || Loc == LastStateChangePos) {
+ setDiagnosticMappingInternal(Diag, Map, GetCurDiagState(), true);
+ return;
+ }
+
+ // Another common case; modifying diagnostic state in a source location
+ // after the previous one.
+ if ((Loc.isValid() && LastStateChangePos.isInvalid()) ||
+ LastStateChangePos.isBeforeInTranslationUnitThan(Loc)) {
+ // A diagnostic pragma occured, create a new DiagState initialized with
+ // the current one and a new DiagStatePoint to record at which location
+ // the new state became active.
+ DiagStates.push_back(*GetCurDiagState());
+ PushDiagStatePoint(&DiagStates.back(), Loc);
+ setDiagnosticMappingInternal(Diag, Map, GetCurDiagState(), true);
+ return;
+ }
+
+ // We allow setting the diagnostic state in random source order for
+ // completeness but it should not be actually happening in normal practice.
+
+ DiagStatePointsTy::iterator Pos = GetDiagStatePointForLoc(Loc);
+ assert(Pos != DiagStatePoints.end());
+
+ // Update all diagnostic states that are active after the given location.
+ for (DiagStatePointsTy::iterator
+ I = Pos+1, E = DiagStatePoints.end(); I != E; ++I) {
+ setDiagnosticMappingInternal(Diag, Map, I->State, true);
+ }
+
+ // If the location corresponds to an existing point, just update its state.
+ if (Pos->Loc == Loc) {
+ setDiagnosticMappingInternal(Diag, Map, Pos->State, true);
+ return;
+ }
+
+ // Create a new state/point and fit it into the vector of DiagStatePoints
+ // so that the vector is always ordered according to location.
+ Pos->Loc.isBeforeInTranslationUnitThan(Loc);
+ DiagStates.push_back(*Pos->State);
+ DiagState *NewState = &DiagStates.back();
+ setDiagnosticMappingInternal(Diag, Map, NewState, true);
+ DiagStatePoints.insert(Pos+1, DiagStatePoint(NewState,
+ FullSourceLoc(Loc, *SourceMgr)));
+}
+
void DiagnosticBuilder::FlushCounts() {
DiagObj->NumDiagArgs = NumArgs;
DiagObj->NumDiagRanges = NumRanges;
/// getDiagnosticLevel - Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
-DiagnosticIDs::Level DiagnosticIDs::getDiagnosticLevel(unsigned DiagID,
- const Diagnostic &Diag) const {
+DiagnosticIDs::Level
+DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
+ const Diagnostic &Diag) const {
// Handle custom diagnostics, which cannot be mapped.
if (DiagID >= diag::DIAG_UPPER_LIMIT)
return CustomDiagInfo->getLevel(DiagID);
unsigned DiagClass = getBuiltinDiagClass(DiagID);
assert(DiagClass != CLASS_NOTE && "Cannot get diagnostic level of a note!");
- return getDiagnosticLevel(DiagID, DiagClass, Diag);
+ return getDiagnosticLevel(DiagID, DiagClass, Loc, Diag);
}
-/// getDiagnosticLevel - Based on the way the client configured the Diagnostic
+/// \brief Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
+///
+/// \param Loc The source location we are interested in finding out the
+/// diagnostic state. Can be null in order to query the latest state.
DiagnosticIDs::Level
DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass,
- const Diagnostic &Diag) const {
+ SourceLocation Loc,
+ const Diagnostic &Diag) const {
// Specific non-error diagnostics may be mapped to various levels from ignored
// to error. Errors can only be mapped to fatal.
DiagnosticIDs::Level Result = DiagnosticIDs::Fatal;
+ Diagnostic::DiagStatePointsTy::iterator
+ Pos = Diag.GetDiagStatePointForLoc(Loc);
+ Diagnostic::DiagState *State = Pos->State;
+
// Get the mapping information, if unset, compute it lazily.
- unsigned MappingInfo = Diag.getDiagnosticMappingInfo((diag::kind)DiagID);
+ unsigned MappingInfo = Diag.getDiagnosticMappingInfo((diag::kind)DiagID,
+ State);
if (MappingInfo == 0) {
MappingInfo = GetDefaultDiagMapping(DiagID);
- Diag.setDiagnosticMappingInternal(DiagID, MappingInfo, false);
+ Diag.setDiagnosticMappingInternal(DiagID, MappingInfo, State, false);
}
switch (MappingInfo & 7) {
}
static void MapGroupMembers(const WarningOption *Group, diag::Mapping Mapping,
- Diagnostic &Diag) {
+ SourceLocation Loc, Diagnostic &Diag) {
// Option exists, poke all the members of its diagnostic set.
if (const short *Member = Group->Members) {
for (; *Member != -1; ++Member)
- Diag.setDiagnosticMapping(*Member, Mapping);
+ Diag.setDiagnosticMapping(*Member, Mapping, Loc);
}
// Enable/disable all subgroups along with this one.
if (const short *SubGroups = Group->SubGroups) {
for (; *SubGroups != (short)-1; ++SubGroups)
- MapGroupMembers(&OptionTable[(short)*SubGroups], Mapping, Diag);
+ MapGroupMembers(&OptionTable[(short)*SubGroups], Mapping, Loc, Diag);
}
}
/// ignores the request if "Group" was unknown, false otherwise.
bool DiagnosticIDs::setDiagnosticGroupMapping(const char *Group,
diag::Mapping Map,
+ SourceLocation Loc,
Diagnostic &Diag) const {
+ assert((Loc.isValid() ||
+ Diag.DiagStatePoints.empty() ||
+ Diag.DiagStatePoints.back().Loc.isInvalid()) &&
+ "Loc should be invalid only when the mapping comes from command-line");
+ assert((Loc.isInvalid() || Diag.DiagStatePoints.empty() ||
+ Diag.DiagStatePoints.back().Loc.isInvalid() ||
+ !Diag.SourceMgr->isBeforeInTranslationUnit(Loc,
+ Diag.DiagStatePoints.back().Loc)) &&
+ "Source location of new mapping is before the previous one!");
+
WarningOption Key = { Group, 0, 0 };
const WarningOption *Found =
std::lower_bound(OptionTable, OptionTable + OptionTableSize, Key,
strcmp(Found->Name, Group) != 0)
return true; // Option not found.
- MapGroupMembers(Found, Map, Diag);
+ MapGroupMembers(Found, Map, Loc, Diag);
return false;
}
// *map* warnings/extensions to errors.
ShouldEmitInSystemHeader = DiagClass == CLASS_ERROR;
- DiagLevel = getDiagnosticLevel(DiagID, DiagClass, Diag);
+ DiagLevel = getDiagnosticLevel(DiagID, DiagClass, Info.getLocation(),
+ Diag);
}
}
return SrcMgr->isInSystemHeader(*this);
}
+bool FullSourceLoc::isBeforeInTranslationUnitThan(SourceLocation Loc) const {
+ assert(isValid());
+ return SrcMgr->isBeforeInTranslationUnit(*this, Loc);
+}
+
const char *FullSourceLoc::getCharacterData(bool *Invalid) const {
assert(isValid());
return SrcMgr->getCharacterData(*this, Invalid);
IsBuiltinMacro = false;
IsFromAST = false;
IsDisabled = false;
- IsUsed = true;
+ IsUsed = false;
IsAllowRedefinitionsWithoutWarning = false;
+ IsWarnIfUnused = false;
ArgumentList = 0;
NumArguments = 0;
}
}
- // If this is the primary source file, remember that this macro hasn't been
- // used yet.
- if (isInPrimaryFile())
- MI->setIsUsed(false);
-
MI->setDefinitionEndLoc(LastTok.getLocation());
// Finally, if this identifier already had a macro defined for it, verify that
setMacroInfo(MacroNameTok.getIdentifierInfo(), MI);
+ assert(!MI->isUsed());
+ // If we need warning for not using the macro, add its location in the
+ // warn-because-unused-macro set. If it gets used it will be removed from set.
+ if (isInPrimaryFile() && // don't warn for include'd macros.
+ Diags->getDiagnosticLevel(diag::pp_macro_not_used,
+ MI->getDefinitionLoc()) != Diagnostic::Ignored) {
+ MI->setIsWarnIfUnused(true);
+ WarnUnusedMacroLocs.insert(MI->getDefinitionLoc());
+ }
+
// If the callbacks want to know, tell them about the macro definition.
if (Callbacks)
Callbacks->MacroDefined(MacroNameTok, MI);
if (Callbacks)
Callbacks->MacroUndefined(MacroNameTok, MI);
+ if (MI->isWarnIfUnused())
+ WarnUnusedMacroLocs.erase(MI->getDefinitionLoc());
+
// Free macro definition.
ReleaseMacroInfo(MI);
setMacroInfo(MacroNameTok.getIdentifierInfo(), 0);
// If there is a macro, process it.
if (MI) // Mark it used.
- MI->setIsUsed(true);
+ markMacroAsUsed(MI);
// Should we include the stuff contained by this directive?
if (!MI == isIfndef) {
// If there is a macro, mark it used.
if (Result.Val != 0 && ValueLive) {
MacroInfo *Macro = PP.getMacroInfo(II);
- Macro->setIsUsed(true);
+ PP.markMacroAsUsed(Macro);
}
// Consume identifier.
CurPPLexer = 0;
- // This is the end of the top-level file. If the diag::pp_macro_not_used
- // diagnostic is enabled, look for macros that have not been used.
- if (getDiagnostics().getDiagnosticLevel(diag::pp_macro_not_used) !=
- Diagnostic::Ignored) {
- for (macro_iterator I = macro_begin(false), E = macro_end(false);
- I != E; ++I)
- if (!I->second->isUsed())
- Diag(I->second->getDefinitionLoc(), diag::pp_macro_not_used);
- }
+ // This is the end of the top-level file. 'WarnUnusedMacroLocs' has collected
+ // all macro locations that we need to warn because they are not used.
+ for (WarnUnusedMacroLocsTy::iterator
+ I=WarnUnusedMacroLocs.begin(), E=WarnUnusedMacroLocs.end(); I!=E; ++I)
+ Diag(*I, diag::pp_macro_not_used);
return true;
}
}
// Notice that this macro has been used.
- MI->setIsUsed(true);
+ markMacroAsUsed(MI);
// If we started lexing a macro, enter the macro expansion body.
}
CreateString(OS.str().data(), OS.str().size(), Tok, Tok.getLocation());
}
+
+void Preprocessor::markMacroAsUsed(MacroInfo *MI) {
+ // If the 'used' status changed, and the macro requires 'unused' warning,
+ // remove its SourceLocation from the warn-for-unused-macro locations.
+ if (MI->isWarnIfUnused() && !MI->isUsed())
+ WarnUnusedMacroLocs.erase(MI->getDefinitionLoc());
+ MI->setIsUsed(true);
+}
if (iter != PragmaPushMacroInfo.end()) {
// Release the MacroInfo currently associated with IdentInfo.
MacroInfo *CurrentMI = getMacroInfo(IdentInfo);
- if (CurrentMI) ReleaseMacroInfo(CurrentMI);
+ if (CurrentMI) {
+ if (CurrentMI->isWarnIfUnused())
+ WarnUnusedMacroLocs.erase(CurrentMI->getDefinitionLoc());
+ ReleaseMacroInfo(CurrentMI);
+ }
// Get the MacroInfo we want to reinstall.
MacroInfo *MacroToReInstall = iter->second.back();
explicit PragmaDiagnosticHandler() : PragmaHandler("diagnostic") {}
virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &DiagToken) {
+ SourceLocation DiagLoc = DiagToken.getLocation();
Token Tok;
PP.LexUnexpandedToken(Tok);
if (Tok.isNot(tok::identifier)) {
else if (II->isStr("fatal"))
Map = diag::MAP_FATAL;
else if (II->isStr("pop")) {
- if (!PP.getDiagnostics().popMappings())
+ if (!PP.getDiagnostics().popMappings(DiagLoc))
PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop);
return;
} else if (II->isStr("push")) {
- PP.getDiagnostics().pushMappings();
+ PP.getDiagnostics().pushMappings(DiagLoc);
return;
} else {
PP.Diag(Tok, diag::warn_pragma_diagnostic_invalid);
}
if (PP.getDiagnostics().setDiagnosticGroupMapping(WarningName.c_str()+2,
- Map))
+ Map, DiagLoc))
PP.Diag(StrToks[0].getLocation(),
diag::warn_pragma_diagnostic_unknown_warning) << WarningName;
}
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
unsigned diag_NeverFallThroughOrReturn;
bool funMode;
+ SourceLocation FuncLoc;
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
CheckFallThroughDiagnostics D;
+ D.FuncLoc = Func->getLocation();
D.diag_MaybeFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_MaybeFallThrough_ReturnsNonVoid =
bool checkDiagnostics(Diagnostic &D, bool ReturnsVoid,
bool HasNoReturn) const {
if (funMode) {
- return (D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function)
- == Diagnostic::Ignored || ReturnsVoid)
- && (D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr)
- == Diagnostic::Ignored || !HasNoReturn)
- && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
- == Diagnostic::Ignored || !ReturnsVoid);
+ return (ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
+ FuncLoc) == Diagnostic::Ignored)
+ && (!HasNoReturn ||
+ D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
+ FuncLoc) == Diagnostic::Ignored)
+ && (!ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
+ == Diagnostic::Ignored);
}
// For blocks.
return ReturnsVoid && !HasNoReturn
- && (D.getDiagnosticLevel(diag::warn_suggest_noreturn_block)
- == Diagnostic::Ignored || !ReturnsVoid);
+ && (!ReturnsVoid ||
+ D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
+ == Diagnostic::Ignored);
}
};
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) {
Diagnostic &D = S.getDiagnostics();
DefaultPolicy.enableCheckUnreachable = (unsigned)
- (D.getDiagnosticLevel(diag::warn_unreachable) != Diagnostic::Ignored);
+ (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
+ Diagnostic::Ignored);
}
void clang::sema::
if (!Suspicious) return;
// ...but it's currently ignored...
- if (S.Diags.getDiagnosticLevel(diag::warn_impcast_integer_sign_conditional))
+ if (S.Diags.getDiagnosticLevel(diag::warn_impcast_integer_sign_conditional,
+ CC))
return;
// ...and -Wsign-compare isn't...
- if (!S.Diags.getDiagnosticLevel(diag::warn_mixed_sign_conditional))
+ if (!S.Diags.getDiagnosticLevel(diag::warn_mixed_sign_conditional, CC))
return;
// ...then check whether it would have warned about either of the
void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
// This is actually a lot of work to potentially be doing on every
// cast; don't do it if we're ignoring -Wcast_align (as is the default).
- if (getDiagnostics().getDiagnosticLevel(diag::warn_cast_align)
+ if (getDiagnostics().getDiagnosticLevel(diag::warn_cast_align,
+ TRange.getBegin())
== Diagnostic::Ignored)
return;
<< Context.BuiltinInfo.GetName(BID)
<< R;
if (Context.BuiltinInfo.getHeaderName(BID) &&
- Diags.getDiagnosticLevel(diag::ext_implicit_lib_function_decl)
+ Diags.getDiagnosticLevel(diag::ext_implicit_lib_function_decl, Loc)
!= Diagnostic::Ignored)
Diag(Loc, diag::note_please_include_header)
<< Context.BuiltinInfo.getHeaderName(BID)
///
void Sema::CheckShadow(Scope *S, VarDecl *D, const LookupResult& R) {
// Return if warning is ignored.
- if (Diags.getDiagnosticLevel(diag::warn_decl_shadow) == Diagnostic::Ignored)
+ if (Diags.getDiagnosticLevel(diag::warn_decl_shadow, R.getNameLoc()) ==
+ Diagnostic::Ignored)
return;
// Don't diagnose declarations at file scope. The scope might not
/// \brief Check -Wshadow without the advantage of a previous lookup.
void Sema::CheckShadow(Scope *S, VarDecl *D) {
+ if (Diags.getDiagnosticLevel(diag::warn_decl_shadow, D->getLocation()) ==
+ Diagnostic::Ignored)
+ return;
+
LookupResult R(*this, D->getDeclName(), D->getLocation(),
Sema::LookupOrdinaryName, Sema::ForRedeclaration);
LookupName(R, S);
void Sema::DiagnoseUnusedParameters(ParmVarDecl * const *Param,
ParmVarDecl * const *ParamEnd) {
- if (Diags.getDiagnosticLevel(diag::warn_unused_parameter) ==
- Diagnostic::Ignored)
- return;
-
// Don't diagnose unused-parameter errors in template instantiations; we
// will already have done so in the template itself.
if (!ActiveTemplateInstantiations.empty())
<< D->getDeclName() << Size;
}
- if (Diags.getDiagnosticLevel(diag::warn_parameter_size)==Diagnostic::Ignored)
- return;
-
// Warn if any parameter is pass-by-value and larger than the specified
// threshold.
for (; Param != ParamEnd; ++Param) {
CheckParmsForFunctionDef(FD->param_begin(), FD->param_end(),
/*CheckParameterNames=*/true);
- bool ShouldCheckShadow =
- Diags.getDiagnosticLevel(diag::warn_decl_shadow) != Diagnostic::Ignored;
-
// Introduce our parameters into the function scope
for (unsigned p = 0, NumParams = FD->getNumParams(); p < NumParams; ++p) {
ParmVarDecl *Param = FD->getParamDecl(p);
// If this has an identifier, add it to the scope stack.
if (Param->getIdentifier() && FnBodyScope) {
- if (ShouldCheckShadow)
- CheckShadow(FnBodyScope, Param);
+ CheckShadow(FnBodyScope, Param);
PushOnScopeChains(Param, FnBodyScope);
}
if (Constructor->getDeclContext()->isDependentContext())
return;
- if (SemaRef.Diags.getDiagnosticLevel(diag::warn_initializer_out_of_order)
- == Diagnostic::Ignored)
+ // Don't check initializers order unless the warning is enabled at the
+ // location of at least one initializer.
+ bool ShouldCheckOrder = false;
+ for (unsigned InitIndex = 0; InitIndex != NumInits; ++InitIndex) {
+ CXXBaseOrMemberInitializer *Init = Inits[InitIndex];
+ if (SemaRef.Diags.getDiagnosticLevel(diag::warn_initializer_out_of_order,
+ Init->getSourceLocation())
+ != Diagnostic::Ignored) {
+ ShouldCheckOrder = true;
+ break;
+ }
+ }
+ if (!ShouldCheckOrder)
return;
// Build the list of bases and members in the order that they'll
IDecl->lookupInstanceMethod(method->getSelector());
if (!MethodInClass || !MethodInClass->isSynthesized()) {
unsigned DIAG = diag::warn_unimplemented_protocol_method;
- if (Diags.getDiagnosticLevel(DIAG) != Diagnostic::Ignored) {
+ if (Diags.getDiagnosticLevel(DIAG, ImpLoc)
+ != Diagnostic::Ignored) {
WarnUndefinedMethod(ImpLoc, method, IncompleteImpl, DIAG);
Diag(method->getLocation(), diag::note_method_declared_at);
Diag(CDecl->getLocation(), diag::note_required_for_protocol_at)
!ClsMap.count(method->getSelector()) &&
(!Super || !Super->lookupClassMethod(method->getSelector()))) {
unsigned DIAG = diag::warn_unimplemented_protocol_method;
- if (Diags.getDiagnosticLevel(DIAG) != Diagnostic::Ignored) {
+ if (Diags.getDiagnosticLevel(DIAG, ImpLoc) != Diagnostic::Ignored) {
WarnUndefinedMethod(ImpLoc, method, IncompleteImpl, DIAG);
Diag(method->getLocation(), diag::note_method_declared_at);
Diag(IDecl->getLocation(), diag::note_required_for_protocol_at) <<
ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second;
bool strictSelectorMatch = receiverIdOrClass && warn &&
- (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl) !=
+ (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl,
+ R.getBegin()) !=
Diagnostic::Ignored);
if (warn && MethList.Method && MethList.Next) {
bool issueWarning = false;
if (Params.empty())
return;
- bool ShouldCheckShadow =
- Diags.getDiagnosticLevel(diag::warn_decl_shadow) != Diagnostic::Ignored;
-
for (BlockDecl::param_iterator AI = CurBlock->TheDecl->param_begin(),
E = CurBlock->TheDecl->param_end(); AI != E; ++AI) {
(*AI)->setOwningFunction(CurBlock->TheDecl);
// If this has an identifier, add it to the scope stack.
if ((*AI)->getIdentifier()) {
- if (ShouldCheckShadow)
- CheckShadow(CurBlock->TheScope, *AI);
+ CheckShadow(CurBlock->TheScope, *AI);
PushOnScopeChains(*AI, CurBlock->TheScope);
}
E->getSourceRange();
if (EvalResult.Diag &&
- Diags.getDiagnosticLevel(diag::ext_expr_not_ice) != Diagnostic::Ignored)
+ Diags.getDiagnosticLevel(diag::ext_expr_not_ice, EvalResult.DiagLoc)
+ != Diagnostic::Ignored)
Diag(EvalResult.DiagLoc, EvalResult.Diag);
if (Result)
while (Idx < UserDiagMappings.size()) {
unsigned DiagID = UserDiagMappings[Idx++];
unsigned Map = UserDiagMappings[Idx++];
- Diag.setDiagnosticMappingInternal(DiagID, Map, /*isUser=*/true);
+ Diag.setDiagnosticMappingInternal(DiagID, Map, Diag.GetCurDiagState(),
+ /*isUser=*/true);
}
}
void ASTWriter::WriteUserDiagnosticMappings(const Diagnostic &Diag) {
RecordData Record;
for (unsigned i = 0; i != diag::DIAG_UPPER_LIMIT; ++i) {
- diag::Mapping Map = Diag.getDiagnosticMappingInfo(i);
+ diag::Mapping Map = Diag.getDiagnosticMappingInfo(i,Diag.GetCurDiagState());
if (Map & 0x8) { // user mapping.
Record.push_back(i);
Record.push_back(Map & 0x7);
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -Wall -Wunused-macros -Wunused-parameter -verify %s
+
+// rdar://8365684
+struct S {
+ void m1() { int b = b==b; } // expected-warning {{always evaluates to true}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-compare"
+ void m2() { int b = b==b; }
+#pragma clang diagnostic pop
+
+ void m3() { int b = b==b; } // expected-warning {{always evaluates to true}}
+};
+
+//------------------------------------------------------------------------------
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-compare"
+template <typename T>
+struct TS {
+ void m() { T b = b==b; }
+};
+#pragma clang diagnostic pop
+
+void f() {
+ TS<int> ts;
+ ts.m();
+}
+
+//------------------------------------------------------------------------------
+
+#define UNUSED_MACRO1 // expected-warning {{macro is not used}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-macros"
+#define UNUSED_MACRO2
+#pragma clang diagnostic pop
+
+//------------------------------------------------------------------------------
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type"
+int g() { }
+#pragma clang diagnostic pop
+
+//------------------------------------------------------------------------------
+
+void ww(
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+ int x,
+#pragma clang diagnostic pop
+ int y) // expected-warning {{unused}}
+{
+}
+
+//------------------------------------------------------------------------------
+
+struct S2 {
+ int x, y;
+ S2() :
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreorder"
+ y(),
+ x()
+#pragma clang diagnostic pop
+ {}
+};
+
+//------------------------------------------------------------------------------