aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/include/clang/Lex/MultipleIncludeOpt.h
blob: 78cf69fd74af597b7482f8289293f3299652fafa (plain) (blame)
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
181
182
183
184
185
186
187
188
189
190
191
#pragma once

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

//===--- 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

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif