1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-01 14:15:23 +02:00

Bug 457301 - Organize Includes ignores IWYU pragma: keep

This commit is contained in:
Sergey Prigogin 2015-01-12 18:34:26 -08:00
parent 6a7b37ca6a
commit 49997a17b3
8 changed files with 179 additions and 18 deletions

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2014 QNX Software Systems and others.
* Copyright (c) 2000, 2015 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -234,6 +234,22 @@ public class CCorePreferenceConstants {
*/
public static final String INCLUDE_PRIVATE_PATTERN = "includes.privatePattern"; //$NON-NLS-1$
/**
* Default value for {@link #INCLUDE_KEEP_PATTERN}.
* @since 5.9
*/
public static final String DEFAULT_INCLUDE_KEEP_PATTERN = "IWYU\\s+(pragma:?\\s+)?keep"; //$NON-NLS-1$
/**
* Preference key for the regular expression pattern that, when appears in a comment on the same
* line as include statement, indicates that the include statement should be preserved when
* organizing includes.
* @see "https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas"
*
* @since 5.9
*/
public static final String INCLUDE_KEEP_PATTERN = "includes.keepPattern"; //$NON-NLS-1$
/**
* A named preference that controls whether the parser should skip trivial expressions in initializer lists.
* <p>
@ -301,17 +317,31 @@ public class CCorePreferenceConstants {
/**
* Returns the node in the preference in the given context.
*
* @param key The preference key.
* @param cProject The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should
* be avoided.
* @return Returns the node matching the given context.
*/
private static IEclipsePreferences getPreferenceNode(String key, ICProject cProject) {
IProject project = cProject == null ? null : cProject.getProject();
return getPreferenceNode(key, project);
}
/**
* Returns the node in the preference in the given context.
*
* @param key The preference key.
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should
* be avoided.
* @return Returns the node matching the given context.
*/
private static IEclipsePreferences getPreferenceNode(String key, ICProject project) {
private static IEclipsePreferences getPreferenceNode(String key, IProject project) {
IEclipsePreferences node = null;
if (project != null) {
node = new ProjectScope(project.getProject()).getNode(CCorePlugin.PLUGIN_ID);
node = new ProjectScope(project).getNode(CCorePlugin.PLUGIN_ID);
if (node.get(key, null) != null) {
return node;
}
@ -331,6 +361,7 @@ public class CCorePreferenceConstants {
/**
* Returns the string value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
@ -343,6 +374,20 @@ public class CCorePreferenceConstants {
/**
* Returns the string value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
* @return Returns the current value for the string.
* @since 5.9
*/
public static String getPreference(String key, IProject project) {
return getPreference(key, project, null);
}
/**
* Returns the string value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
@ -354,8 +399,23 @@ public class CCorePreferenceConstants {
return getPreferenceNode(key, project).get(key, defaultValue);
}
/**
* Returns the string value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
* @param defaultValue The default value if not specified in the preferences.
* @return Returns the current value of the preference.
* @since 5.9
*/
public static String getPreference(String key, IProject project, String defaultValue) {
return getPreferenceNode(key, project).get(key, defaultValue);
}
/**
* Returns the integer value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
@ -367,8 +427,23 @@ public class CCorePreferenceConstants {
return getPreferenceNode(key, project).getInt(key, defaultValue);
}
/**
* Returns the integer value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
* @param defaultValue The default value if not specified in the preferences.
* @return Returns the current value of the preference.
* @since 5.9
*/
public static int getPreference(String key, IProject project, int defaultValue) {
return getPreferenceNode(key, project).getInt(key, defaultValue);
}
/**
* Returns the boolean value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
@ -380,6 +455,20 @@ public class CCorePreferenceConstants {
return getPreferenceNode(key, project).getBoolean(key, defaultValue);
}
/**
* Returns the boolean value for the given key in the given context.
*
* @param key The preference key
* @param project The current context or {@code null} if no context is available and
* the workspace setting should be taken. Note that passing {@code null} should be avoided.
* @param defaultValue The default value if not specified in the preferences.
* @return Returns the current value of the preference.
* @since 5.9
*/
public static boolean getPreference(String key, IProject project, boolean defaultValue) {
return getPreferenceNode(key, project).getBoolean(key, defaultValue);
}
/**
* Returns the scopes for preference lookup.
*

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2014 QNX Software Systems and others.
* Copyright (c) 2000, 2015 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -68,6 +68,7 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer {
defaultPreferences.put(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_BEGIN_EXPORTS_PATTERN);
defaultPreferences.put(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_END_EXPORTS_PATTERN);
defaultPreferences.put(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_PRIVATE_PATTERN);
defaultPreferences.put(CCorePreferenceConstants.INCLUDE_KEEP_PATTERN, CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN);
// Scalability defaults.
defaultPreferences.putBoolean(CCorePreferenceConstants.SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS, CCorePreferenceConstants.DEFAULT_SCALABILITY_SKIP_TRIVIAL_EXPRESSIONS);

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2013, 2014 Google, Inc and others.
* Copyright (c) 2013, 2015 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -428,6 +428,23 @@ public class IncludeOrganizerTest extends IncludesTestBase {
assertExpectedResults();
}
//h1.h
//h2.h
//h3.h
//source.cpp
//#include "h1.h"
//#include "h2.h" // IWYU pragma: keep
//#include "h3.h" /* IWYU pragma: keep */
//====================
//#include "h2.h" // IWYU pragma: keep
//#include "h3.h" /* IWYU pragma: keep */
public void testPragmaKeep() throws Exception {
assertExpectedResults();
}
//h1.h
//#define M2(t, p) t p

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2013, 2014 Google, Inc and others.
* Copyright (c) 2013, 2015 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -13,12 +13,15 @@ package org.eclipse.cdt.internal.corext.codemanipulation;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.cdt.core.CCorePreferenceConstants;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
@ -48,6 +51,7 @@ public class InclusionContext {
private final IncludePreferences fPreferences;
private String fSourceContents;
private String fLineDelimiter;
private Pattern fKeepPragmaPattern;
private IPath fTuLocation;
public InclusionContext(ITranslationUnit tu) {
@ -295,6 +299,20 @@ public class InclusionContext {
return fLineDelimiter;
}
public Pattern getKeepPragmaPattern() {
if (fKeepPragmaPattern == null) {
String keepPattern = CCorePreferenceConstants.getPreference(
CCorePreferenceConstants.INCLUDE_KEEP_PATTERN, fProject,
CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN);
try {
fKeepPragmaPattern = Pattern.compile(keepPattern);
} catch (PatternSyntaxException e) {
fKeepPragmaPattern = Pattern.compile(CCorePreferenceConstants.DEFAULT_INCLUDE_KEEP_PATTERN);
}
}
return fKeepPragmaPattern;
}
/**
* Sets the effective translation unit location that overrides the default value obtained by
* calling {@code getTranslationUnit().getLocation()}.

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2013, 2014 Google, Inc and others.
* Copyright (c) 2013, 2015 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -45,12 +45,14 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock {
private static final Key KEY_BEGIN_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_BEGIN_EXPORTS_PATTERN);
private static final Key KEY_END_EXPORTS_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_END_EXPORTS_PATTERN);
private static final Key KEY_PRIVATE_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_PRIVATE_PATTERN);
private static final Key KEY_KEEP_PATTERN = getCDTCoreKey(CCorePreferenceConstants.INCLUDE_KEEP_PATTERN);
private static Key[] ALL_KEYS = {
KEY_EXPORT_PATTERN,
KEY_BEGIN_EXPORTS_PATTERN,
KEY_END_EXPORTS_PATTERN,
KEY_PRIVATE_PATTERN,
KEY_KEEP_PATTERN,
};
private PixelConverter pixelConverter;
@ -83,9 +85,12 @@ public class IncludePragmasBlock extends OptionsConfigurationBlock {
control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern,
KEY_END_EXPORTS_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40));
LayoutUtil.setHorizontalGrabbing(control, true);
control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_end_exports_pattern,
control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_private_pattern,
KEY_PRIVATE_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40));
LayoutUtil.setHorizontalGrabbing(control, true);
control = addTextField(composite, PreferencesMessages.IncludePragmasBlock_keep_pattern,
KEY_KEEP_PATTERN, 0, pixelConverter.convertWidthInCharsToPixels(40));
LayoutUtil.setHorizontalGrabbing(control, true);
updateControls();
return composite;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -469,6 +469,7 @@ public final class PreferencesMessages extends NLS {
public static String IncludePragmasBlock_begin_exports_pattern;
public static String IncludePragmasBlock_end_exports_pattern;
public static String IncludePragmasBlock_private_pattern;
public static String IncludePragmasBlock_keep_pattern;
public static String NameStylePreferencePage_title;
public static String NameStyleBlock_code_node;

View file

@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2000, 2014 IBM Corporation and others.
# Copyright (c) 2000, 2015 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@ -522,10 +522,11 @@ IncludeOrderBlock_order_of_includes= O&rder of Include Statements:
IncludePragmasPreferencePage_title= Include Pragmas
IncludePragmasBlock_description=Include pragmas are special comments that affect behavior of Organize Includes command. A description of include pragmas can be found in <a href="https://code.google.com/p/include-what-you-use/wiki/IWYUPragmas">IWYU Pragmas</a>. Include patterns can be customized by defining regular expressions matching each of the pragmas.
IncludePragmasBlock_link_tooltip=Wiki page describing include-what-you-use pragmas
IncludePragmasBlock_export_pattern= &Export:
IncludePragmasBlock_export_pattern= E&xport:
IncludePragmasBlock_begin_exports_pattern= &Begin Exports:
IncludePragmasBlock_end_exports_pattern= En&d Exports:
IncludePragmasBlock_end_exports_pattern= &End Exports:
IncludePragmasBlock_private_pattern= &Private:
IncludePragmasBlock_keep_pattern= &Keep:
NameStylePreferencePage_title=Name Style
NameStyleBlock_code_node=Code

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2013 Google, Inc and others.
* Copyright (c) 2012, 2015 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -107,9 +107,9 @@ public class IncludeOrganizer {
* {@code null} if the include was not resolved.
*/
IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style,
IASTPreprocessorIncludeStatement existingInclude) {
IASTPreprocessorIncludeStatement existingInclude, boolean required) {
super(header, includeInfo, style, existingInclude);
this.required = false;
this.required = required;
}
public boolean isRequired() {
@ -195,6 +195,8 @@ public class IncludeOrganizer {
List<InclusionRequest> requests = createInclusionRequests(ast, bindingsToInclude, false, reachableHeaders);
processInclusionRequests(requests, headerSubstitutor);
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
// Use a map instead of a set to be able to retrieve existing elements using equal elements.
// Maps each element to itself.
Map<IncludePrototype, IncludePrototype> includePrototypes = new HashMap<>();
@ -215,12 +217,12 @@ public class IncludeOrganizer {
IPath header = path.isEmpty() ? null : Path.fromOSString(path);
IncludeGroupStyle style =
header != null ? fContext.getIncludeStyle(header) : fContext.getIncludeStyle(includeInfo);
IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include);
boolean required = hasPragmaKeep(include, commentedNodeMap);
IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include, required);
updateIncludePrototypes(includePrototypes, prototype);
}
}
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
IRegion includeReplacementRegion =
IncludeUtil.getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap);
@ -934,4 +936,31 @@ public class IncludeOrganizer {
buf.append(lineComment);
return buf.toString();
}
private boolean hasPragmaKeep(IASTPreprocessorIncludeStatement include, NodeCommentMap commentedNodeMap) {
List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(include);
for (IASTComment comment : comments) {
String text = getTrimmedCommentText(comment);
if (fContext.getKeepPragmaPattern().matcher(text).matches())
return true;
}
return false;
}
private String getTrimmedCommentText(IASTComment comment) {
char[] text = comment.getComment();
int end = text.length - (comment.isBlockComment() ? 2 : 0);
int begin;
for (begin = 2; begin < end; begin++) {
if (!Character.isWhitespace(text[begin]))
break;
}
if (end <= begin)
return ""; //$NON-NLS-1$
while (--end >= begin) {
if (!Character.isWhitespace(text[end]))
break;
}
return new String(text, begin, end + 1 - begin);
}
}