c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/YamlParser.java
author Ilia Gromov <ilia@netbeans.org>
Wed, 07 Jun 2017 20:23:29 +0300
branchrelease82
changeset 18423 b9d9af239a0c
permissions -rw-r--r--
Fixing #270763 - Move clang-tidy integration to nb contrib
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - move wrapper
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - sign nbm
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - move tests
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - data for a new test
* * *
Fixed #270839 - [clang-tidy] Group checks in Editor hints
(transplanted from 35b6125ef00c470655dac6673075f5c12ec74593)
ilia@18415
     1
/*
ilia@18415
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
ilia@18415
     3
 *
ilia@18415
     4
 * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
ilia@18415
     5
 *
ilia@18415
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
ilia@18415
     7
 * Other names may be trademarks of their respective owners.
ilia@18415
     8
 *
ilia@18415
     9
 * The contents of this file are subject to the terms of either the GNU
ilia@18415
    10
 * General Public License Version 2 only ("GPL") or the Common
ilia@18415
    11
 * Development and Distribution License("CDDL") (collectively, the
ilia@18415
    12
 * "License"). You may not use this file except in compliance with the
ilia@18415
    13
 * License. You can obtain a copy of the License at
ilia@18415
    14
 * http://www.netbeans.org/cddl-gplv2.html
ilia@18415
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
ilia@18415
    16
 * specific language governing permissions and limitations under the
ilia@18415
    17
 * License.  When distributing the software, include this License Header
ilia@18415
    18
 * Notice in each file and include the License file at
ilia@18415
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
ilia@18415
    20
 * particular file as subject to the "Classpath" exception as provided
ilia@18415
    21
 * by Oracle in the GPL Version 2 section of the License file that
ilia@18415
    22
 * accompanied this code. If applicable, add the following below the
ilia@18415
    23
 * License Header, with the fields enclosed by brackets [] replaced by
ilia@18415
    24
 * your own identifying information:
ilia@18415
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
ilia@18415
    26
 *
ilia@18415
    27
 * If you wish your version of this file to be governed by only the CDDL
ilia@18415
    28
 * or only the GPL Version 2, indicate your decision by adding
ilia@18415
    29
 * "[Contributor] elects to include this software in this distribution
ilia@18415
    30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
ilia@18415
    31
 * single choice of license, a recipient has the option to distribute
ilia@18415
    32
 * your version of this file under either the CDDL, the GPL Version 2 or
ilia@18415
    33
 * to extend the choice of license to its licensees as provided above.
ilia@18415
    34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
ilia@18415
    35
 * Version 2 license, then the option applies only if the new code is
ilia@18415
    36
 * made subject to such option by the copyright holder.
ilia@18415
    37
 *
ilia@18415
    38
 * Contributor(s): Ilia Gromov
ilia@18415
    39
 */
ilia@18415
    40
package com.sun.tools.ide.analysis.modernize.impl;
ilia@18415
    41
ilia@18415
    42
import static com.sun.tools.ide.analysis.modernize.impl.YamlParser.Diagnostics.UNDEFINED;
ilia@18415
    43
import java.util.ArrayList;
ilia@18415
    44
import java.util.Collections;
ilia@18415
    45
import java.util.List;
ilia@18415
    46
import java.util.StringTokenizer;
ilia@18415
    47
import java.util.logging.Level;
ilia@18415
    48
ilia@18415
    49
/**
ilia@18415
    50
 *
ilia@18415
    51
 * @author Ilia Gromov
ilia@18415
    52
 */
ilia@18415
    53
public class YamlParser {
ilia@18415
    54
    // TODO check for no-replacements case
ilia@18415
    55
ilia@18415
    56
    private static final YamlParser INSTANCE = new YamlParser();
ilia@18415
    57
ilia@18415
    58
    public static YamlParser getDefault() {
ilia@18415
    59
        return INSTANCE;
ilia@18415
    60
    }
ilia@18415
    61
ilia@18415
    62
    private static final String OPEN_TAG = "---"; //NOI18N
ilia@18415
    63
    private static final String CLOSE_TAG = "..."; //NOI18N
ilia@18415
    64
ilia@18415
    65
    private static final String MAIN_SOURCE_FILE = "MainSourceFile"; //NOI18N
ilia@18415
    66
    private static final String CHECK_NAME = "CheckName"; //NOI18N
ilia@18415
    67
    private static final String LEVEL = "Level"; //NOI18N
ilia@18415
    68
    private static final String MESSAGE_FILE_PATH = "MessageFilePath"; //NOI18N
ilia@18415
    69
    private static final String MESSAGE_FILE_OFFSET = "MessageFileOffset"; //NOI18N
ilia@18415
    70
    private static final String MESSAGE = "Message"; //NOI18N
ilia@18415
    71
    private static final String REPLACEMENTS = "Replacements"; //NOI18N
ilia@18415
    72
    private static final String DIAGNOSTICS = "Diagnostics"; //NOI18N
ilia@18415
    73
ilia@18415
    74
    private static final String FILE_PATH = "FilePath"; //NOI18N
ilia@18415
    75
    private static final String OFFSET = "Offset"; //NOI18N
ilia@18415
    76
    private static final String LENGTH = "Length"; //NOI18N
ilia@18415
    77
    private static final String REPLACEMENT_TEXT = "ReplacementText"; //NOI18N
ilia@18415
    78
ilia@18415
    79
    // clang-modernize:
ilia@18415
    80
// ---
ilia@18415
    81
// MainSourceFile:  /home/ilia/NetBeansProjects/CppApplication_46/main.cpp
ilia@18415
    82
// Replacements:    
ilia@18415
    83
//   - FilePath:        /home/ilia/NetBeansProjects/CppApplication_46/newfile.h
ilia@18415
    84
//     Offset:          101
ilia@18415
    85
//     Length:          1
ilia@18415
    86
//     ReplacementText: nullptr
ilia@18415
    87
//   - FilePath:        /home/ilia/NetBeansProjects/CppApplication_46/newfile1.h
ilia@18415
    88
//     Offset:          380
ilia@18415
    89
//     Length:          1
ilia@18415
    90
//     ReplacementText: nullptr
ilia@18415
    91
//   - FilePath:        /home/ilia/NetBeansProjects/CppApplication_46/main.cpp
ilia@18415
    92
//     Offset:          152
ilia@18415
    93
//     Length:          1
ilia@18415
    94
//     ReplacementText: nullptr
ilia@18415
    95
// ...
ilia@18415
    96
    // clang-tidy:
ilia@18415
    97
// ---
ilia@18415
    98
// MainSourceFile:  ''
ilia@18415
    99
// Diagnostics:     
ilia@18415
   100
//   CheckName:       misc-macro-parentheses
ilia@18415
   101
//   Replacements:    
ilia@18415
   102
//     - FilePath:        /media/SSD_/code/zdoom/main_1.cpp
ilia@18415
   103
//       Offset:          1352
ilia@18415
   104
//       Length:          0
ilia@18415
   105
//       ReplacementText: '('
ilia@18415
   106
//     - FilePath:        /media/SSD_/code/zdoom/main_1.cpp
ilia@18415
   107
//       Offset:          1353
ilia@18415
   108
//       Length:          0
ilia@18415
   109
//       ReplacementText: ')'
ilia@18415
   110
// Diagnostics:     
ilia@18415
   111
//   CheckName:       misc-macro-parentheses
ilia@18415
   112
//   Replacements:    
ilia@18415
   113
//     - FilePath:        /media/SSD_/code/zdoom/main_1.cpp
ilia@18415
   114
//       Offset:          1446
ilia@18415
   115
//       Length:          0
ilia@18415
   116
//       ReplacementText: '('
ilia@18415
   117
//     - FilePath:        /media/SSD_/code/zdoom/main_1.cpp
ilia@18415
   118
//       Offset:          1447
ilia@18415
   119
//       Length:          0
ilia@18415
   120
//       ReplacementText: ')'
ilia@18415
   121
// ...
ilia@18415
   122
    public List<Diagnostics> parseYaml(String output) {
ilia@18415
   123
        try {
ilia@18415
   124
            if (output.isEmpty()) {
ilia@18415
   125
                // No warnings in header = no 'Main Source File' element
ilia@18415
   126
                return Collections.EMPTY_LIST;
ilia@18415
   127
            }
ilia@18415
   128
            StringTokenizer st = new StringTokenizer(output, "\n"); //NOI18N
ilia@18415
   129
            String line;
ilia@18415
   130
ilia@18415
   131
            // ---
ilia@18415
   132
            line = st.nextToken();
ilia@18415
   133
ilia@18415
   134
            line = st.nextToken();
ilia@18415
   135
            String mainSourceFile = getValue(MAIN_SOURCE_FILE, line);
ilia@18415
   136
ilia@18415
   137
            // Replacements:    
ilia@18415
   138
            line = st.nextToken();
ilia@18415
   139
            ClangTidyVersion version = line.contains(REPLACEMENTS)
ilia@18415
   140
                    ? ClangTidyVersion.OLD_FORMAT
ilia@18415
   141
                    : ClangTidyVersion.NEW_FORMAT;
ilia@18415
   142
ilia@18415
   143
            List<Diagnostics> list = version.parser.parse(st);
ilia@18415
   144
ilia@18415
   145
            return list;
ilia@18415
   146
        } catch (Exception ex) {
ilia@18415
   147
            ModernizeErrorProvider.LOG.log(Level.INFO, "Can't parse output line");
ilia@18415
   148
            return Collections.EMPTY_LIST;
ilia@18415
   149
        }
ilia@18415
   150
    }
ilia@18415
   151
ilia@18415
   152
    private static List<Replacement> parseReplacements(StringTokenizer st) {
ilia@18415
   153
        List<Replacement> replacements = new ArrayList<Replacement>();
ilia@18415
   154
        String line;
ilia@18415
   155
ilia@18415
   156
        // eat Replacements:
ilia@18415
   157
        line = st.nextToken();
ilia@18415
   158
ilia@18415
   159
        if (!line.contains(REPLACEMENTS)) {
ilia@18415
   160
            return replacements;
ilia@18415
   161
        }
ilia@18415
   162
ilia@18415
   163
        while (st.hasMoreTokens()) {
ilia@18415
   164
            line = st.nextToken();
ilia@18415
   165
ilia@18415
   166
            if (line.equals(CLOSE_TAG) || !line.contains(FILE_PATH)) {
ilia@18415
   167
                break;
ilia@18415
   168
            }
ilia@18415
   169
ilia@18415
   170
            String filePath = getValue(FILE_PATH, line);
ilia@18415
   171
ilia@18415
   172
            line = st.nextToken();
ilia@18415
   173
            int offset = Integer.parseInt(getValue(OFFSET, line));
ilia@18415
   174
ilia@18415
   175
            line = st.nextToken();
ilia@18415
   176
            int length = Integer.parseInt(getValue(LENGTH, line));
ilia@18415
   177
ilia@18415
   178
            line = st.nextToken();
ilia@18415
   179
            String replacementText = getValue(REPLACEMENT_TEXT, line);
ilia@18415
   180
            while (replacementText == null && st.hasMoreElements()) {
ilia@18415
   181
                line = line + "\n" + st.nextElement(); //NOI18N
ilia@18415
   182
                replacementText = getValue(REPLACEMENT_TEXT, line);
ilia@18415
   183
            }
ilia@18415
   184
ilia@18415
   185
            Replacement replacement = new Replacement(filePath, offset, length, replacementText);
ilia@18415
   186
            replacements.add(replacement);
ilia@18415
   187
ilia@18415
   188
        }
ilia@18415
   189
        return replacements;
ilia@18415
   190
    }
ilia@18415
   191
ilia@18415
   192
    private static String getValue(String key, String line) {
ilia@18415
   193
        int keyStart = line.indexOf(key);
ilia@18415
   194
        assert keyStart != -1;
ilia@18415
   195
        int keyEnd = keyStart + key.length() + 1;
ilia@18415
   196
        String trimmed = line.substring(keyEnd).trim();
ilia@18415
   197
        boolean isTextValue = trimmed.charAt(0) == '\'';
ilia@18415
   198
        if (isTextValue) {
ilia@18415
   199
            if (trimmed.length() == 1) {
ilia@18415
   200
                // '
ilia@18415
   201
                // abcd'
ilia@18415
   202
                return null;
ilia@18415
   203
            } else if (trimmed.charAt(trimmed.length() - 1) == '\'') {
ilia@18415
   204
                trimmed = trimmed.substring(1, trimmed.length() - 1);
ilia@18415
   205
            } else {
ilia@18415
   206
                return null;
ilia@18415
   207
            }
ilia@18415
   208
        }
ilia@18415
   209
        return trimmed;
ilia@18415
   210
    }
ilia@18415
   211
ilia@18415
   212
    public static class TranslationUnitDiagnostics {
ilia@18415
   213
ilia@18415
   214
        private final String mainSourceFilePath;
ilia@18415
   215
        private final String context;
ilia@18415
   216
        private final List<Diagnostics> diags;
ilia@18415
   217
ilia@18415
   218
        public TranslationUnitDiagnostics(String mainSourceFilePath, String context, List<Diagnostics> diags) {
ilia@18415
   219
            this.mainSourceFilePath = mainSourceFilePath;
ilia@18415
   220
            this.context = context;
ilia@18415
   221
            this.diags = diags;
ilia@18415
   222
        }
ilia@18415
   223
ilia@18415
   224
        public String getMainSourceFilePath() {
ilia@18415
   225
            return mainSourceFilePath;
ilia@18415
   226
        }
ilia@18415
   227
ilia@18415
   228
        public String getContext() {
ilia@18415
   229
            return context;
ilia@18415
   230
        }
ilia@18415
   231
ilia@18415
   232
        public List<Diagnostics> getDiags() {
ilia@18415
   233
            return diags;
ilia@18415
   234
        }
ilia@18415
   235
    }
ilia@18415
   236
ilia@18415
   237
    public static class Diagnostics {
ilia@18415
   238
ilia@18415
   239
        public static final String UNDEFINED = "undefined"; // NOI18N
ilia@18415
   240
ilia@18415
   241
        public enum Level {
ilia@18415
   242
            warning, error
ilia@18415
   243
        }
ilia@18415
   244
ilia@18415
   245
        private final String checkName;
ilia@18415
   246
        private final Diagnostics.Level level;
ilia@18415
   247
        private final String messageFilePath;
ilia@18415
   248
        private final int messageFileOffset;
ilia@18415
   249
        private final String message;
ilia@18415
   250
        private final List<Replacement> replacements;
ilia@18415
   251
ilia@18415
   252
        public Diagnostics(String checkName, Diagnostics.Level level, String messageFilePath, int messageFileOffset, String message, List<Replacement> replacements) {
ilia@18415
   253
            this.checkName = checkName;
ilia@18415
   254
            this.level = level;
ilia@18415
   255
            this.messageFilePath = messageFilePath;
ilia@18415
   256
            this.messageFileOffset = messageFileOffset;
ilia@18415
   257
            this.message = message;
ilia@18415
   258
            this.replacements = replacements;
ilia@18415
   259
        }
ilia@18415
   260
ilia@18415
   261
        public String getCheckName() {
ilia@18415
   262
            return checkName;
ilia@18415
   263
        }
ilia@18415
   264
ilia@18415
   265
        public Level getLevel() {
ilia@18415
   266
            return level;
ilia@18415
   267
        }
ilia@18415
   268
ilia@18415
   269
        public String getMessageFilePath() {
ilia@18415
   270
            return messageFilePath;
ilia@18415
   271
        }
ilia@18415
   272
ilia@18415
   273
        public int getMessageFileOffset() {
ilia@18415
   274
            return messageFileOffset;
ilia@18415
   275
        }
ilia@18415
   276
ilia@18415
   277
        public String getMessage() {
ilia@18415
   278
            return message;
ilia@18415
   279
        }
ilia@18415
   280
ilia@18415
   281
        public List<Replacement> getReplacements() {
ilia@18415
   282
            return replacements;
ilia@18415
   283
        }
ilia@18415
   284
    }
ilia@18415
   285
ilia@18415
   286
    public static class Replacement {
ilia@18415
   287
ilia@18415
   288
        public final String filePath;
ilia@18415
   289
        public final int offset;
ilia@18415
   290
        public final int length;
ilia@18415
   291
        public final String replacementText;
ilia@18415
   292
ilia@18415
   293
        public Replacement(String FilePath, int offset, int length, String replacementText) {
ilia@18415
   294
            this.filePath = FilePath;
ilia@18415
   295
            this.offset = offset;
ilia@18415
   296
            this.length = length;
ilia@18415
   297
            this.replacementText = replacementText;
ilia@18415
   298
        }
ilia@18415
   299
    }
ilia@18415
   300
ilia@18415
   301
    private static enum ClangTidyVersion {
ilia@18415
   302
        NEW_FORMAT(new NewFormatParser()),
ilia@18415
   303
        OLD_FORMAT(new OldFormatParser());
ilia@18415
   304
ilia@18415
   305
        public final TidyParser parser;
ilia@18415
   306
ilia@18415
   307
        private ClangTidyVersion(TidyParser parser) {
ilia@18415
   308
            this.parser = parser;
ilia@18415
   309
        }
ilia@18415
   310
    }
ilia@18415
   311
ilia@18415
   312
    private static abstract class TidyParser {
ilia@18415
   313
ilia@18415
   314
        abstract List<Diagnostics> parse(StringTokenizer st);
ilia@18415
   315
    }
ilia@18415
   316
ilia@18415
   317
    private static class NewFormatParser extends TidyParser {
ilia@18415
   318
ilia@18415
   319
        @Override
ilia@18415
   320
        public List<Diagnostics> parse(StringTokenizer st) {
ilia@18415
   321
            List<Diagnostics> list = new ArrayList<Diagnostics>();
ilia@18415
   322
ilia@18415
   323
            String line;
ilia@18415
   324
            while (st.hasMoreElements()) {
ilia@18415
   325
                line = st.nextToken();
ilia@18415
   326
                String checkName = getValue(CHECK_NAME, line);
ilia@18415
   327
ilia@18415
   328
                line = st.nextToken();
ilia@18415
   329
                Diagnostics.Level level = Diagnostics.Level.valueOf(getValue(LEVEL, line).toLowerCase());
ilia@18415
   330
ilia@18415
   331
                line = st.nextToken();
ilia@18415
   332
                String messageFilePath = getValue(MESSAGE_FILE_PATH, line);
ilia@18415
   333
ilia@18415
   334
                line = st.nextToken();
ilia@18415
   335
                int messageFileOffset = Integer.parseInt(getValue(MESSAGE_FILE_OFFSET, line));
ilia@18415
   336
ilia@18415
   337
                line = st.nextToken();
ilia@18415
   338
                String message = getValue(MESSAGE, line);
ilia@18415
   339
ilia@18415
   340
                List<Replacement> replacements = parseReplacements(st);
ilia@18415
   341
ilia@18415
   342
                list.add(new Diagnostics(checkName, level, messageFilePath, messageFileOffset, message, replacements));
ilia@18415
   343
            }
ilia@18415
   344
            return list;
ilia@18415
   345
        }
ilia@18415
   346
ilia@18415
   347
    }
ilia@18415
   348
ilia@18415
   349
    private static class OldFormatParser extends TidyParser {
ilia@18415
   350
ilia@18415
   351
        @Override
ilia@18415
   352
        public List<Diagnostics> parse(StringTokenizer st) {
ilia@18415
   353
            List<Replacement> parseReplacements = parseReplacements(st);
ilia@18415
   354
            return Collections.singletonList(new Diagnostics(UNDEFINED, Diagnostics.Level.warning, "", -1, "", parseReplacements)); //NOI18N
ilia@18415
   355
        }
ilia@18415
   356
    }
ilia@18415
   357
}