xref: /openbsd-src/gnu/llvm/clang/lib/Lex/TokenConcatenation.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- TokenConcatenation.cpp - Token Concatenation Avoidance -----------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file implements the TokenConcatenation class.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick 
13e5dd7070Spatrick #include "clang/Lex/TokenConcatenation.h"
14e5dd7070Spatrick #include "clang/Basic/CharInfo.h"
15e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
16e5dd7070Spatrick #include "llvm/Support/ErrorHandling.h"
17e5dd7070Spatrick using namespace clang;
18e5dd7070Spatrick 
19e5dd7070Spatrick 
20e5dd7070Spatrick /// IsStringPrefix - Return true if Str is a string prefix.
21e5dd7070Spatrick /// 'L', 'u', 'U', or 'u8'. Including raw versions.
IsStringPrefix(StringRef Str,bool CPlusPlus11)22e5dd7070Spatrick static bool IsStringPrefix(StringRef Str, bool CPlusPlus11) {
23e5dd7070Spatrick 
24e5dd7070Spatrick   if (Str[0] == 'L' ||
25e5dd7070Spatrick       (CPlusPlus11 && (Str[0] == 'u' || Str[0] == 'U' || Str[0] == 'R'))) {
26e5dd7070Spatrick 
27e5dd7070Spatrick     if (Str.size() == 1)
28e5dd7070Spatrick       return true; // "L", "u", "U", and "R"
29e5dd7070Spatrick 
30e5dd7070Spatrick     // Check for raw flavors. Need to make sure the first character wasn't
31e5dd7070Spatrick     // already R. Need CPlusPlus11 check for "LR".
32e5dd7070Spatrick     if (Str[1] == 'R' && Str[0] != 'R' && Str.size() == 2 && CPlusPlus11)
33e5dd7070Spatrick       return true; // "LR", "uR", "UR"
34e5dd7070Spatrick 
35e5dd7070Spatrick     // Check for "u8" and "u8R"
36e5dd7070Spatrick     if (Str[0] == 'u' && Str[1] == '8') {
37e5dd7070Spatrick       if (Str.size() == 2) return true; // "u8"
38e5dd7070Spatrick       if (Str.size() == 3 && Str[2] == 'R') return true; // "u8R"
39e5dd7070Spatrick     }
40e5dd7070Spatrick   }
41e5dd7070Spatrick 
42e5dd7070Spatrick   return false;
43e5dd7070Spatrick }
44e5dd7070Spatrick 
45e5dd7070Spatrick /// IsIdentifierStringPrefix - Return true if the spelling of the token
46e5dd7070Spatrick /// is literally 'L', 'u', 'U', or 'u8'. Including raw versions.
IsIdentifierStringPrefix(const Token & Tok) const47e5dd7070Spatrick bool TokenConcatenation::IsIdentifierStringPrefix(const Token &Tok) const {
48e5dd7070Spatrick   const LangOptions &LangOpts = PP.getLangOpts();
49e5dd7070Spatrick 
50e5dd7070Spatrick   if (!Tok.needsCleaning()) {
51e5dd7070Spatrick     if (Tok.getLength() < 1 || Tok.getLength() > 3)
52e5dd7070Spatrick       return false;
53e5dd7070Spatrick     SourceManager &SM = PP.getSourceManager();
54e5dd7070Spatrick     const char *Ptr = SM.getCharacterData(SM.getSpellingLoc(Tok.getLocation()));
55e5dd7070Spatrick     return IsStringPrefix(StringRef(Ptr, Tok.getLength()),
56e5dd7070Spatrick                           LangOpts.CPlusPlus11);
57e5dd7070Spatrick   }
58e5dd7070Spatrick 
59e5dd7070Spatrick   if (Tok.getLength() < 256) {
60e5dd7070Spatrick     char Buffer[256];
61e5dd7070Spatrick     const char *TokPtr = Buffer;
62e5dd7070Spatrick     unsigned length = PP.getSpelling(Tok, TokPtr);
63e5dd7070Spatrick     return IsStringPrefix(StringRef(TokPtr, length), LangOpts.CPlusPlus11);
64e5dd7070Spatrick   }
65e5dd7070Spatrick 
66e5dd7070Spatrick   return IsStringPrefix(StringRef(PP.getSpelling(Tok)), LangOpts.CPlusPlus11);
67e5dd7070Spatrick }
68e5dd7070Spatrick 
TokenConcatenation(const Preprocessor & pp)69e5dd7070Spatrick TokenConcatenation::TokenConcatenation(const Preprocessor &pp) : PP(pp) {
70e5dd7070Spatrick   memset(TokenInfo, 0, sizeof(TokenInfo));
71e5dd7070Spatrick 
72e5dd7070Spatrick   // These tokens have custom code in AvoidConcat.
73e5dd7070Spatrick   TokenInfo[tok::identifier      ] |= aci_custom;
74e5dd7070Spatrick   TokenInfo[tok::numeric_constant] |= aci_custom_firstchar;
75e5dd7070Spatrick   TokenInfo[tok::period          ] |= aci_custom_firstchar;
76e5dd7070Spatrick   TokenInfo[tok::amp             ] |= aci_custom_firstchar;
77e5dd7070Spatrick   TokenInfo[tok::plus            ] |= aci_custom_firstchar;
78e5dd7070Spatrick   TokenInfo[tok::minus           ] |= aci_custom_firstchar;
79e5dd7070Spatrick   TokenInfo[tok::slash           ] |= aci_custom_firstchar;
80e5dd7070Spatrick   TokenInfo[tok::less            ] |= aci_custom_firstchar;
81e5dd7070Spatrick   TokenInfo[tok::greater         ] |= aci_custom_firstchar;
82e5dd7070Spatrick   TokenInfo[tok::pipe            ] |= aci_custom_firstchar;
83e5dd7070Spatrick   TokenInfo[tok::percent         ] |= aci_custom_firstchar;
84e5dd7070Spatrick   TokenInfo[tok::colon           ] |= aci_custom_firstchar;
85e5dd7070Spatrick   TokenInfo[tok::hash            ] |= aci_custom_firstchar;
86e5dd7070Spatrick   TokenInfo[tok::arrow           ] |= aci_custom_firstchar;
87e5dd7070Spatrick 
88e5dd7070Spatrick   // These tokens have custom code in C++11 mode.
89e5dd7070Spatrick   if (PP.getLangOpts().CPlusPlus11) {
90e5dd7070Spatrick     TokenInfo[tok::string_literal      ] |= aci_custom;
91e5dd7070Spatrick     TokenInfo[tok::wide_string_literal ] |= aci_custom;
92e5dd7070Spatrick     TokenInfo[tok::utf8_string_literal ] |= aci_custom;
93e5dd7070Spatrick     TokenInfo[tok::utf16_string_literal] |= aci_custom;
94e5dd7070Spatrick     TokenInfo[tok::utf32_string_literal] |= aci_custom;
95e5dd7070Spatrick     TokenInfo[tok::char_constant       ] |= aci_custom;
96e5dd7070Spatrick     TokenInfo[tok::wide_char_constant  ] |= aci_custom;
97e5dd7070Spatrick     TokenInfo[tok::utf16_char_constant ] |= aci_custom;
98e5dd7070Spatrick     TokenInfo[tok::utf32_char_constant ] |= aci_custom;
99e5dd7070Spatrick   }
100e5dd7070Spatrick 
101e5dd7070Spatrick   // These tokens have custom code in C++17 mode.
102e5dd7070Spatrick   if (PP.getLangOpts().CPlusPlus17)
103e5dd7070Spatrick     TokenInfo[tok::utf8_char_constant] |= aci_custom;
104e5dd7070Spatrick 
105e5dd7070Spatrick   // These tokens have custom code in C++2a mode.
106ec727ea7Spatrick   if (PP.getLangOpts().CPlusPlus20)
107e5dd7070Spatrick     TokenInfo[tok::lessequal ] |= aci_custom_firstchar;
108e5dd7070Spatrick 
109e5dd7070Spatrick   // These tokens change behavior if followed by an '='.
110e5dd7070Spatrick   TokenInfo[tok::amp         ] |= aci_avoid_equal;           // &=
111e5dd7070Spatrick   TokenInfo[tok::plus        ] |= aci_avoid_equal;           // +=
112e5dd7070Spatrick   TokenInfo[tok::minus       ] |= aci_avoid_equal;           // -=
113e5dd7070Spatrick   TokenInfo[tok::slash       ] |= aci_avoid_equal;           // /=
114e5dd7070Spatrick   TokenInfo[tok::less        ] |= aci_avoid_equal;           // <=
115e5dd7070Spatrick   TokenInfo[tok::greater     ] |= aci_avoid_equal;           // >=
116e5dd7070Spatrick   TokenInfo[tok::pipe        ] |= aci_avoid_equal;           // |=
117e5dd7070Spatrick   TokenInfo[tok::percent     ] |= aci_avoid_equal;           // %=
118e5dd7070Spatrick   TokenInfo[tok::star        ] |= aci_avoid_equal;           // *=
119e5dd7070Spatrick   TokenInfo[tok::exclaim     ] |= aci_avoid_equal;           // !=
120e5dd7070Spatrick   TokenInfo[tok::lessless    ] |= aci_avoid_equal;           // <<=
121e5dd7070Spatrick   TokenInfo[tok::greatergreater] |= aci_avoid_equal;         // >>=
122e5dd7070Spatrick   TokenInfo[tok::caret       ] |= aci_avoid_equal;           // ^=
123e5dd7070Spatrick   TokenInfo[tok::equal       ] |= aci_avoid_equal;           // ==
124e5dd7070Spatrick }
125e5dd7070Spatrick 
126e5dd7070Spatrick /// GetFirstChar - Get the first character of the token \arg Tok,
127e5dd7070Spatrick /// avoiding calls to getSpelling where possible.
GetFirstChar(const Preprocessor & PP,const Token & Tok)128e5dd7070Spatrick static char GetFirstChar(const Preprocessor &PP, const Token &Tok) {
129e5dd7070Spatrick   if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
130e5dd7070Spatrick     // Avoid spelling identifiers, the most common form of token.
131e5dd7070Spatrick     return II->getNameStart()[0];
132e5dd7070Spatrick   } else if (!Tok.needsCleaning()) {
133e5dd7070Spatrick     if (Tok.isLiteral() && Tok.getLiteralData()) {
134e5dd7070Spatrick       return *Tok.getLiteralData();
135e5dd7070Spatrick     } else {
136e5dd7070Spatrick       SourceManager &SM = PP.getSourceManager();
137e5dd7070Spatrick       return *SM.getCharacterData(SM.getSpellingLoc(Tok.getLocation()));
138e5dd7070Spatrick     }
139e5dd7070Spatrick   } else if (Tok.getLength() < 256) {
140e5dd7070Spatrick     char Buffer[256];
141e5dd7070Spatrick     const char *TokPtr = Buffer;
142e5dd7070Spatrick     PP.getSpelling(Tok, TokPtr);
143e5dd7070Spatrick     return TokPtr[0];
144e5dd7070Spatrick   } else {
145e5dd7070Spatrick     return PP.getSpelling(Tok)[0];
146e5dd7070Spatrick   }
147e5dd7070Spatrick }
148e5dd7070Spatrick 
149e5dd7070Spatrick /// AvoidConcat - If printing PrevTok immediately followed by Tok would cause
150e5dd7070Spatrick /// the two individual tokens to be lexed as a single token, return true
151e5dd7070Spatrick /// (which causes a space to be printed between them).  This allows the output
152e5dd7070Spatrick /// of -E mode to be lexed to the same token stream as lexing the input
153e5dd7070Spatrick /// directly would.
154e5dd7070Spatrick ///
155e5dd7070Spatrick /// This code must conservatively return true if it doesn't want to be 100%
156e5dd7070Spatrick /// accurate.  This will cause the output to include extra space characters,
157e5dd7070Spatrick /// but the resulting output won't have incorrect concatenations going on.
158e5dd7070Spatrick /// Examples include "..", which we print with a space between, because we
159e5dd7070Spatrick /// don't want to track enough to tell "x.." from "...".
AvoidConcat(const Token & PrevPrevTok,const Token & PrevTok,const Token & Tok) const160e5dd7070Spatrick bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
161e5dd7070Spatrick                                      const Token &PrevTok,
162e5dd7070Spatrick                                      const Token &Tok) const {
163e5dd7070Spatrick   // Conservatively assume that every annotation token that has a printable
164e5dd7070Spatrick   // form requires whitespace.
165e5dd7070Spatrick   if (PrevTok.isAnnotation())
166e5dd7070Spatrick     return true;
167e5dd7070Spatrick 
168e5dd7070Spatrick   // First, check to see if the tokens were directly adjacent in the original
169e5dd7070Spatrick   // source.  If they were, it must be okay to stick them together: if there
170e5dd7070Spatrick   // were an issue, the tokens would have been lexed differently.
171e5dd7070Spatrick   SourceManager &SM = PP.getSourceManager();
172e5dd7070Spatrick   SourceLocation PrevSpellLoc = SM.getSpellingLoc(PrevTok.getLocation());
173e5dd7070Spatrick   SourceLocation SpellLoc = SM.getSpellingLoc(Tok.getLocation());
174e5dd7070Spatrick   if (PrevSpellLoc.getLocWithOffset(PrevTok.getLength()) == SpellLoc)
175e5dd7070Spatrick     return false;
176e5dd7070Spatrick 
177e5dd7070Spatrick   tok::TokenKind PrevKind = PrevTok.getKind();
178e5dd7070Spatrick   if (!PrevTok.isAnnotation() && PrevTok.getIdentifierInfo())
179e5dd7070Spatrick     PrevKind = tok::identifier; // Language keyword or named operator.
180e5dd7070Spatrick 
181e5dd7070Spatrick   // Look up information on when we should avoid concatenation with prevtok.
182e5dd7070Spatrick   unsigned ConcatInfo = TokenInfo[PrevKind];
183e5dd7070Spatrick 
184e5dd7070Spatrick   // If prevtok never causes a problem for anything after it, return quickly.
185e5dd7070Spatrick   if (ConcatInfo == 0) return false;
186e5dd7070Spatrick 
187e5dd7070Spatrick   if (ConcatInfo & aci_avoid_equal) {
188e5dd7070Spatrick     // If the next token is '=' or '==', avoid concatenation.
189e5dd7070Spatrick     if (Tok.isOneOf(tok::equal, tok::equalequal))
190e5dd7070Spatrick       return true;
191e5dd7070Spatrick     ConcatInfo &= ~aci_avoid_equal;
192e5dd7070Spatrick   }
193e5dd7070Spatrick   if (Tok.isAnnotation()) {
194e5dd7070Spatrick     // Modules annotation can show up when generated automatically for includes.
195e5dd7070Spatrick     assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,
196e5dd7070Spatrick                        tok::annot_module_end) &&
197e5dd7070Spatrick            "unexpected annotation in AvoidConcat");
198e5dd7070Spatrick     ConcatInfo = 0;
199e5dd7070Spatrick   }
200e5dd7070Spatrick 
201e5dd7070Spatrick   if (ConcatInfo == 0)
202e5dd7070Spatrick     return false;
203e5dd7070Spatrick 
204e5dd7070Spatrick   // Basic algorithm: we look at the first character of the second token, and
205e5dd7070Spatrick   // determine whether it, if appended to the first token, would form (or
206e5dd7070Spatrick   // would contribute) to a larger token if concatenated.
207e5dd7070Spatrick   char FirstChar = 0;
208e5dd7070Spatrick   if (ConcatInfo & aci_custom) {
209e5dd7070Spatrick     // If the token does not need to know the first character, don't get it.
210e5dd7070Spatrick   } else {
211e5dd7070Spatrick     FirstChar = GetFirstChar(PP, Tok);
212e5dd7070Spatrick   }
213e5dd7070Spatrick 
214e5dd7070Spatrick   switch (PrevKind) {
215e5dd7070Spatrick   default:
216e5dd7070Spatrick     llvm_unreachable("InitAvoidConcatTokenInfo built wrong");
217e5dd7070Spatrick 
218e5dd7070Spatrick   case tok::raw_identifier:
219e5dd7070Spatrick     llvm_unreachable("tok::raw_identifier in non-raw lexing mode!");
220e5dd7070Spatrick 
221e5dd7070Spatrick   case tok::string_literal:
222e5dd7070Spatrick   case tok::wide_string_literal:
223e5dd7070Spatrick   case tok::utf8_string_literal:
224e5dd7070Spatrick   case tok::utf16_string_literal:
225e5dd7070Spatrick   case tok::utf32_string_literal:
226e5dd7070Spatrick   case tok::char_constant:
227e5dd7070Spatrick   case tok::wide_char_constant:
228e5dd7070Spatrick   case tok::utf8_char_constant:
229e5dd7070Spatrick   case tok::utf16_char_constant:
230e5dd7070Spatrick   case tok::utf32_char_constant:
231e5dd7070Spatrick     if (!PP.getLangOpts().CPlusPlus11)
232e5dd7070Spatrick       return false;
233e5dd7070Spatrick 
234e5dd7070Spatrick     // In C++11, a string or character literal followed by an identifier is a
235e5dd7070Spatrick     // single token.
236e5dd7070Spatrick     if (Tok.getIdentifierInfo())
237e5dd7070Spatrick       return true;
238e5dd7070Spatrick 
239e5dd7070Spatrick     // A ud-suffix is an identifier. If the previous token ends with one, treat
240e5dd7070Spatrick     // it as an identifier.
241e5dd7070Spatrick     if (!PrevTok.hasUDSuffix())
242e5dd7070Spatrick       return false;
243*12c85518Srobert     [[fallthrough]];
244e5dd7070Spatrick   case tok::identifier:   // id+id or id+number or id+L"foo".
245e5dd7070Spatrick     // id+'.'... will not append.
246e5dd7070Spatrick     if (Tok.is(tok::numeric_constant))
247e5dd7070Spatrick       return GetFirstChar(PP, Tok) != '.';
248e5dd7070Spatrick 
249e5dd7070Spatrick     if (Tok.getIdentifierInfo() ||
250e5dd7070Spatrick         Tok.isOneOf(tok::wide_string_literal, tok::utf8_string_literal,
251e5dd7070Spatrick                     tok::utf16_string_literal, tok::utf32_string_literal,
252e5dd7070Spatrick                     tok::wide_char_constant, tok::utf8_char_constant,
253e5dd7070Spatrick                     tok::utf16_char_constant, tok::utf32_char_constant))
254e5dd7070Spatrick       return true;
255e5dd7070Spatrick 
256e5dd7070Spatrick     // If this isn't identifier + string, we're done.
257e5dd7070Spatrick     if (Tok.isNot(tok::char_constant) && Tok.isNot(tok::string_literal))
258e5dd7070Spatrick       return false;
259e5dd7070Spatrick 
260e5dd7070Spatrick     // Otherwise, this is a narrow character or string.  If the *identifier*
261e5dd7070Spatrick     // is a literal 'L', 'u8', 'u' or 'U', avoid pasting L "foo" -> L"foo".
262e5dd7070Spatrick     return IsIdentifierStringPrefix(PrevTok);
263e5dd7070Spatrick 
264e5dd7070Spatrick   case tok::numeric_constant:
265e5dd7070Spatrick     return isPreprocessingNumberBody(FirstChar) ||
266e5dd7070Spatrick            FirstChar == '+' || FirstChar == '-';
267e5dd7070Spatrick   case tok::period:          // ..., .*, .1234
268e5dd7070Spatrick     return (FirstChar == '.' && PrevPrevTok.is(tok::period)) ||
269e5dd7070Spatrick            isDigit(FirstChar) ||
270e5dd7070Spatrick            (PP.getLangOpts().CPlusPlus && FirstChar == '*');
271e5dd7070Spatrick   case tok::amp:             // &&
272e5dd7070Spatrick     return FirstChar == '&';
273e5dd7070Spatrick   case tok::plus:            // ++
274e5dd7070Spatrick     return FirstChar == '+';
275e5dd7070Spatrick   case tok::minus:           // --, ->, ->*
276e5dd7070Spatrick     return FirstChar == '-' || FirstChar == '>';
277e5dd7070Spatrick   case tok::slash:           //, /*, //
278e5dd7070Spatrick     return FirstChar == '*' || FirstChar == '/';
279e5dd7070Spatrick   case tok::less:            // <<, <<=, <:, <%
280e5dd7070Spatrick     return FirstChar == '<' || FirstChar == ':' || FirstChar == '%';
281e5dd7070Spatrick   case tok::greater:         // >>, >>=
282e5dd7070Spatrick     return FirstChar == '>';
283e5dd7070Spatrick   case tok::pipe:            // ||
284e5dd7070Spatrick     return FirstChar == '|';
285e5dd7070Spatrick   case tok::percent:         // %>, %:
286e5dd7070Spatrick     return FirstChar == '>' || FirstChar == ':';
287e5dd7070Spatrick   case tok::colon:           // ::, :>
288e5dd7070Spatrick     return FirstChar == '>' ||
289e5dd7070Spatrick     (PP.getLangOpts().CPlusPlus && FirstChar == ':');
290e5dd7070Spatrick   case tok::hash:            // ##, #@, %:%:
291e5dd7070Spatrick     return FirstChar == '#' || FirstChar == '@' || FirstChar == '%';
292e5dd7070Spatrick   case tok::arrow:           // ->*
293e5dd7070Spatrick     return PP.getLangOpts().CPlusPlus && FirstChar == '*';
294e5dd7070Spatrick   case tok::lessequal:       // <=> (C++2a)
295ec727ea7Spatrick     return PP.getLangOpts().CPlusPlus20 && FirstChar == '>';
296e5dd7070Spatrick   }
297e5dd7070Spatrick }
298