reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
//===--- MultipleIncludeOpt.h - Header Multiple-Include Optzn ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Defines the MultipleIncludeOpt interface.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H
#define LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H

#include "clang/Basic/SourceLocation.h"

namespace clang {
class IdentifierInfo;

/// Implements the simple state machine that the Lexer class uses to
/// detect files subject to the 'multiple-include' optimization.
///
/// The public methods in this class are triggered by various
/// events that occur when a file is lexed, and after the entire file is lexed,
/// information about which macro (if any) controls the header is returned.
class MultipleIncludeOpt {
  /// ReadAnyTokens - This is set to false when a file is first opened and true
  /// any time a token is returned to the client or a (non-multiple-include)
  /// directive is parsed.  When the final \#endif is parsed this is reset back
  /// to false, that way any tokens before the first \#ifdef or after the last
  /// \#endif can be easily detected.
  bool ReadAnyTokens;

  /// ImmediatelyAfterTopLevelIfndef - This is true when the only tokens
  /// processed in the file so far is an #ifndef and an identifier.  Used in
  /// the detection of header guards in a file.
  bool ImmediatelyAfterTopLevelIfndef;

  /// ReadAnyTokens - This is set to false when a file is first opened and true
  /// any time a token is returned to the client or a (non-multiple-include)
  /// directive is parsed.  When the final #endif is parsed this is reset back
  /// to false, that way any tokens before the first #ifdef or after the last
  /// #endif can be easily detected.
  bool DidMacroExpansion;

  /// TheMacro - The controlling macro for a file, if valid.
  ///
  const IdentifierInfo *TheMacro;

  /// DefinedMacro - The macro defined right after TheMacro, if any.
  const IdentifierInfo *DefinedMacro;

  SourceLocation MacroLoc;
  SourceLocation DefinedLoc;
public:
  MultipleIncludeOpt() {
    ReadAnyTokens = false;
    ImmediatelyAfterTopLevelIfndef = false;
    DidMacroExpansion = false;
    TheMacro = nullptr;
    DefinedMacro = nullptr;
  }

  SourceLocation GetMacroLocation() const {
    return MacroLoc;
  }

  SourceLocation GetDefinedLocation() const {
    return DefinedLoc;
  }

  void resetImmediatelyAfterTopLevelIfndef() {
    ImmediatelyAfterTopLevelIfndef = false;
  }

  void SetDefinedMacro(IdentifierInfo *M, SourceLocation Loc) {
    DefinedMacro = M;
    DefinedLoc = Loc;
  }

  /// Invalidate - Permanently mark this file as not being suitable for the
  /// include-file optimization.
  void Invalidate() {
    // If we have read tokens but have no controlling macro, the state-machine
    // below can never "accept".
    ReadAnyTokens = true;
    ImmediatelyAfterTopLevelIfndef = false;
    DefinedMacro = nullptr;
    TheMacro = nullptr;
  }

  /// getHasReadAnyTokensVal - This is used for the \#ifndef handshake at the
  /// top of the file when reading preprocessor directives.  Otherwise, reading
  /// the "ifndef x" would count as reading tokens.
  bool getHasReadAnyTokensVal() const { return ReadAnyTokens; }

  /// getImmediatelyAfterTopLevelIfndef - returns true if the last directive
  /// was an #ifndef at the beginning of the file.
  bool getImmediatelyAfterTopLevelIfndef() const {
    return ImmediatelyAfterTopLevelIfndef;
  }

  // If a token is read, remember that we have seen a side-effect in this file.
  void ReadToken() {
    ReadAnyTokens = true;
    ImmediatelyAfterTopLevelIfndef = false;
  }

  /// ExpandedMacro - When a macro is expanded with this lexer as the current
  /// buffer, this method is called to disable the MIOpt if needed.
  void ExpandedMacro() { DidMacroExpansion = true; }

  /// Called when entering a top-level \#ifndef directive (or the
  /// "\#if !defined" equivalent) without any preceding tokens.
  ///
  /// Note, we don't care about the input value of 'ReadAnyTokens'.  The caller
  /// ensures that this is only called if there are no tokens read before the
  /// \#ifndef.  The caller is required to do this, because reading the \#if
  /// line obviously reads in tokens.
  void EnterTopLevelIfndef(const IdentifierInfo *M, SourceLocation Loc) {
    // If the macro is already set, this is after the top-level #endif.
    if (TheMacro)
      return Invalidate();

    // If we have already expanded a macro by the end of the #ifndef line, then
    // there is a macro expansion *in* the #ifndef line.  This means that the
    // condition could evaluate differently when subsequently #included.  Reject
    // this.
    if (DidMacroExpansion)
      return Invalidate();

    // Remember that we're in the #if and that we have the macro.
    ReadAnyTokens = true;
    ImmediatelyAfterTopLevelIfndef = true;
    TheMacro = M;
    MacroLoc = Loc;
  }

  /// Invoked when a top level conditional (except \#ifndef) is found.
  void EnterTopLevelConditional() {
    // If a conditional directive (except #ifndef) is found at the top level,
    // there is a chunk of the file not guarded by the controlling macro.
    Invalidate();
  }

  /// Called when the lexer exits the top-level conditional.
  void ExitTopLevelConditional() {
    // If we have a macro, that means the top of the file was ok.  Set our state
    // back to "not having read any tokens" so we can detect anything after the
    // #endif.
    if (!TheMacro) return Invalidate();

    // At this point, we haven't "read any tokens" but we do have a controlling
    // macro.
    ReadAnyTokens = false;
    ImmediatelyAfterTopLevelIfndef = false;
  }

  /// Once the entire file has been lexed, if there is a controlling
  /// macro, return it.
  const IdentifierInfo *GetControllingMacroAtEndOfFile() const {
    // If we haven't read any tokens after the #endif, return the controlling
    // macro if it's valid (if it isn't, it will be null).
    if (!ReadAnyTokens)
      return TheMacro;
    return nullptr;
  }

  /// If the ControllingMacro is followed by a macro definition, return
  /// the macro that was defined.
  const IdentifierInfo *GetDefinedMacro() const {
    return DefinedMacro;
  }
};

}  // end namespace clang

#endif